Thread: My attempt at lighting math

  1. #1
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,071

    Question My attempt at lighting math

    Anyone who has seen any of my other threads, knows that 3D math isn't exactly my strong point. However, i'm trying to do a static lighting system based on the OpenGL lighting model.
    At this point, my light is dynamic (was easier to do this for testing purposes). The faces light up as they should, but I think Iv'e done somthing wrong with the vertex normals, as a normal that faces the opposite direction of the light is still fully lit.

    Here is my StaticLight.cpp, then StaticLight.h, and then a snippit from the rendering code where the light is applied.

    Code:
    /*static lighting code. Still needs alot of work. Currently only supports diffuse lighting*/
    #include <windows.h>
    #include <gl/gl.h>
    #include <gl/glu.h>
    #include <math.h>
    
    #include "StaticLight.h"
    
    bool pe_lightenabled;
    pe_staticLight pe_sL;
    
    //may remove the setupLighting function
    GLvoid staticLight::setupLighting(GLfloat glbAmbient[3], GLfloat pos[3], GLfloat norm[3])
    {
    	pe_sL.glbAmb = glbAmbient[3];
    	pe_sL.vPos3f[3] = pos[3];
    	pe_sL.vNormal3f = norm[3];
    }
    
    GLvoid staticLight::CalculateLighting(int ID, UINT lArray[], 
    									 float x, float y, float z, 
    									 float r, float g, float b,
    									 float ca, float la, float qa)
    {
    	//while (pe_lightenabled)
    	//{
    	//Setup:
    		//assign ID to the light
    		int id = lArray[ID];
    
    		//light positioning:
    		pe_sL.x = x; 
    		pe_sL.y = y; 
    		pe_sL.z = z;
    
    		//light coloring (diffuse):
    		pe_sL.r = r;
    		pe_sL.g = g;
    		pe_sL.b = b;
    
    		//light attenuation:
    		/*pe_sL.constant_attenuation = ca;
    		pe_sL.linear_attenuation = la;
    		pe_sL.quadratic_attenuation = qa;*/
    
    	//Render:
    		float L = sqrt( (pe_sL.vPos3f[0]-pe_sL.x)*(pe_sL.vPos3f[0]-pe_sL.x)+ \
    						(pe_sL.vPos3f[1]-pe_sL.y)*(pe_sL.vPos3f[1]-pe_sL.y)+ \
    						(pe_sL.vPos3f[2]-pe_sL.z)*(pe_sL.vPos3f[2]-pe_sL.z) );
    		float N = pe_sL.vNormal3f;
    
    		//calculate diffuse color:
    		float diffuser = pe_sL.r * (L*N);
    		float diffuseg = pe_sL.g * (L*N);
    		float diffuseb = pe_sL.b * (L*N);
    
    		if (pe_sL.r > 1.0)
    			pe_sL.r == 1.0;
    		if (pe_sL.g > 1.0)
    			pe_sL.g == 1.0;
    		if (pe_sL.b > 1.0)
    			pe_sL.b == 1.0;
    
    		pe_sL.finalColor3f[1] = diffuser; pe_sL.finalColor3f[2] = diffuseg; pe_sL.finalColor3f[3] = diffuseb;
    	//}
    
    }
    
    //this function, once/if finished, will tell a vertex to illuminate only when within the lights radius.
    GLvoid staticLight::Vertex_In_Radius(float x, float y, float z)
    {
    	/*some pseudo(spelling?) code for calculating the x:
    	diameter = total_attenuation/2 - lightposx;
    	if ( x < diameter | x > -diameter) then
    		light_the_vertex();*/
    }
    
    GLvoid staticLight::enableLighting(int type)
    {
    	if (type == 1)
    		glDisable(GL_LIGHTING);
    		pe_lightenabled = TRUE;
    	if (type == 2)
    		glEnable(GL_LIGHTING);
    		pe_lightenabled = TRUE;
    	if (type == 3)//default
    		glEnable(GL_LIGHTING);
    		pe_lightenabled = FALSE;
    	if (type == 4)
    		glDisable(GL_LIGHTING);
    		pe_lightenabled = FALSE;
    
    }
    Code:
    //hold information about our light
    struct pe_staticLight
    {
    public:
    	GLfloat x, y, z;
    	GLfloat r, g, b, a;
    	GLfloat vPos3f[3];
    	GLfloat vNormal3f;
    	GLfloat finalColor3f[3], finalColor4f[4];
    	GLfloat glbAmb;
    	GLfloat constant_attenuation, linear_attenuation, quadratic_attenuation;
    };
    
    //static light class
    class staticLight
    {
    private:
    	staticLight();
    	virtual ~staticLight();
    
    public:
    	GLvoid setupLighting(GLfloat glbAmbient[3], GLfloat pos[3], GLfloat norm[3]);
    	//GLvoid drawLight();
    	GLvoid CalculateLighting(int ID, UINT lArray[], float x, float y, float z, float r, float g, float b, float ca, float la, float qa);
    	GLvoid Vertex_In_Radius(float x, float y, float z);
    	GLvoid enableLighting(int type);
    };
    Code:
    	glBegin(GL_QUADS);
    	//-------front-------//
    	glNormal3f(0.0, 0.0, 1.0);
    	for (i=0; i<4; i++) {	
    		c[0]=SolidCubeData[5*i+2];		
    		c[1]=SolidCubeData[5*i+3];
    		c[2]=SolidCubeData[5*i+4];
    		glMultiTexCoord2fARB(GL_TEXTURE0_ARB,SolidCubeData[5*i]     , SolidCubeData[5*i+1]); 
    		glMultiTexCoord2fARB(GL_TEXTURE1_ARB,SolidCubeData[5*i], SolidCubeData[5*i+1]);
    		
    		//_peStaticLight->enableLighting(4);
    		GLfloat global_ambient[3] = {0.75, 0.75, 0.75};
    		GLfloat vert_position[3] = {5*i+2, 5*i+3, 5*i+4};
    		GLfloat vert_normal[3] = {0.0, 0.0, 1.0};
    		_peStaticLight->setupLighting(global_ambient, vert_position, vert_normal);
    		_peStaticLight->CalculateLighting(0, lightID, 10, 10, 10, 1, 1, 1, 0, 1.5, 0);
    		glColor3f(pe_sL.finalColor3f[1], pe_sL.finalColor3f[2], pe_sL.finalColor3f[3]);
    		glVertex3f(SolidCubeData[5*i+2], SolidCubeData[5*i+3], SolidCubeData[5*i+4]);
    	}
    	glEnd();
    	
    	glBegin(GL_QUADS);
    	//-------back-------//
    	glColor3f(1.0, 1.0, 1.0);
    	glNormal3f(0.0, 0.0, -1.0);
    	for (i=4; i<8; i++) {	
    		c[0]=SolidCubeData[5*i+2];		
    		c[1]=SolidCubeData[5*i+3];
    		c[2]=SolidCubeData[5*i+4];
    		glMultiTexCoord2fARB(GL_TEXTURE0_ARB,SolidCubeData[5*i]     , SolidCubeData[5*i+1]); 
    		glMultiTexCoord2fARB(GL_TEXTURE1_ARB,SolidCubeData[5*i], SolidCubeData[5*i+1]);
    
    		GLfloat global_ambient[3] = {0.75, 0.75, 0.75};
    		GLfloat vert_position[3] = {5*i+2, 5*i+3, 5*i+4};
    		GLfloat vert_normal[3] = {0.0, 0.0, -1.0};
    		_peStaticLight->setupLighting(global_ambient, vert_position, vert_normal);
    		_peStaticLight->CalculateLighting(0, lightID, 10, 10, 10, 1, 1, 1, 0, 1.5, 0);
    		glColor3f(pe_sL.finalColor3f[1], pe_sL.finalColor3f[2], pe_sL.finalColor3f[3]);
    		glVertex3f(SolidCubeData[5*i+2], SolidCubeData[5*i+3], SolidCubeData[5*i+4]);
    	}
    	glEnd();
    I'm still pretty shakey on this, so please bear with me

    thanx,
    -psychopath
    M.Eng Computer Engineering Candidate
    B.Sc Computer Science

    Robotics and graphics enthusiast.

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You are probably not clamping the light values to always be between 0.0f and 1.0f.

    Code:
    //Get vector from light to object
    D3DXVECTOR3 NrmToLight=LightSource-ObjectPosition;
    
    //Normalize the vector
    D3DXVec3Normalize(&NrmToLight,&NrmToLight);
    
    //Calculate dot product of ToLightVector and SurfaceNormal for this tri
    float LightCoef=D3DXVec3Dot(&NrmToLight,&SurfaceNormal);
    
    //Prevent backlighting
    if (LightCoef<=0.0f) LightCoef=0.0f;
    if (LightCoef>=1.0f) LightCoef=1.0f;
    
    ...
    ...

  3. #3
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,071
    Oh crap! I forgot about this thread!! Arg!
    I havn't really been focused on much of anything lately so that could be the reason

    Anyway, from what you posted, I think I can see what I left out.
    I'll post again if I end up with further questions (if i remember )

    -psychopath
    M.Eng Computer Engineering Candidate
    B.Sc Computer Science

    Robotics and graphics enthusiast.

  4. #4
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,071
    Hmm, well, I didn't forget the thread this time, lol
    Anyway, i'm seeing some signs of lighting, except theres still a problem. I only see "lighting" Whenever a vertex leaves the screen. When it does, I can see it start to turn dark and the dark "creeps over the quad" (sorry, thats the best I can describe it). I'm pretty sure its because i'm not converting to a matrix (projections, modelview, tangent, etc). Problem is, I don't know what to convert it to or how...I don't even know if 'convert' is the right word for it, lol.

    -psychopath
    M.Eng Computer Engineering Candidate
    B.Sc Computer Science

    Robotics and graphics enthusiast.

  5. #5
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You need to do some things before your light your vertices.

    Transform them from model space to world space. Perform the lighting in world space

    Your lighting is really only applicable to model space - essentially the light and your object are the only thing in the world at that time. But you transform to world space and then do the lighting. Then transform to clip space, view space, and project onto screen. If you perform lighting prior to transforming to world space, you will get some very odd results.

    This is because every object originates at 0,0,0 in model space. So if you compute the vector to the light from your model, you will get strange numbers because both of them are at 0,0,0. When you transform to world space, or essentially position them in the world, now the math works out fine because the center of the object is not 0,0,0, but wherever the object is in the world. So the lighting will work.

    From another post with lots of lighting information.

    Diffuse lighting
    X - denotes component-wise multiplication
    N - surface normal
    L - vector points towards light

    Diffuse=(N dot L)*SourceDiffuse X MaterialDiffuse

    So in Direct3D you would do this:

    Code:
    struct RGBA
    {
      BYTE Blue;
      BYTE Green;
      BYTE Red;
      BYTE Alpha;
    };
    
    void ComputeDiffuse(RGBA *outColor,D3DXVECTOR3 toLight,D3DXVECTOR3 Normal,RGBA SourceDiffuse,RGBA MaterialDiffuse)
    {
      //Normalize light vector
      D3DXVECTOR3 nrmToLight=D3DXVec3Normalize(&nrmToLight,&toLight);
    
      //Compute dot product
      float dot=D3DXVec3Dot(&nrmToLight,&Normal);
    
      //Prevent backlighting
      if (dot<0.0f) dot=0.0f;
      if (dot>1.0f) dot=1.0f;
    
      
      *outColor.Red=SourceDiffuse.Red*MaterialDiffuse.Red;
      *outColor.Green=SourceDiffuse.Green*MaterialDiffuse.Green;
      *outColor.Blue=SourceDiffuse.Blue*MaterialDiffuse.Blue;
      *outColor.Alpha=SourceDiffuse.Alpha*MaterialDiffuse.Alpha;
    
      *outColor.Red*=dot;
      *outColor.Green*=dot;
      *outColor.Blue*=dot;
      *outColor.Alpha*=dot;
    }
    I would highly recommend doing this inside of a vertex shader. They support data swizzling which makes all of this much easier than using C.
    Last edited by VirtualAce; 03-22-2005 at 09:07 AM.

  6. #6
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,071
    hmm, ok, i'll do some more fixing, and see what I come up with.

    -psychopath
    M.Eng Computer Engineering Candidate
    B.Sc Computer Science

    Robotics and graphics enthusiast.

  7. #7
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,071
    Actually, I AM rendering the light in world space, via the ModelView matrix. OpenGL is essentially doing all of the matrix transformations in the background. What you described is only applicable to D3D (from what google told me anyway). So, i'm pretty much lost again, lol

    -psychopath
    M.Eng Computer Engineering Candidate
    B.Sc Computer Science

    Robotics and graphics enthusiast.

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    What I posted is applicable to all 3D graphics. The functions from the D3DX library are, of course, proprietary to DirectX.
    Code:
    ...
    D3DXVECTOR3 nrmToLight=D3DXVec3Normalize(&nrmToLight,&toLight);
    
      //Compute dot product
      float dot=D3DXVec3Dot(&nrmToLight,&Normal);
    Here is what the code for them might look like in a software renderer or a non-API renderer.
    They would probably be a part of a vector class with operators much like D3DXVECTOR3, but I left all that out for clarity's sake.
    Code:
    struct Vector3
    {
      float x,y,z;
     
      Vector3(void):x(0.0f),y(0.0f),z(0.0f) {}
      Vector3(float _x,float_y,float_z):x(_x),y(_y),z(_z) {}
    };
    
    void Vec3Normalize(Vector3 &out,const Vector3 &in)
    {
      float distx=in.x*in.x;
      float disty=in.y*in.y;
      float distz=in.z*in.z;
      float totaldist=sqrt(distx+disty+distz);
      out.x=in.x/totaldist;
      out.y=in.y/totaldist;
      out.z=in.z/totaldist;
    }
    
    float Vec3Dot(Vector3 &V1, Vector3 &V2)
    {
      return (V1.x*V2.x)+(V1.y*V2.y)+(V1.z*V2.z);
    }

  9. #9
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,071
    ok, the lighting is working for the most part, but the normals are still messed up. Faces facing in the opposite direction of the light are still being the lit as much as all the other faces. I was looking at some sample code though, and I think its because the normals arn't being transformed in to eye coords, but i'm not really sure. Is this correct?

    -psychopath
    M.Eng Computer Engineering Candidate
    B.Sc Computer Science

    Robotics and graphics enthusiast.

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Yes could be. You must transform all of the vertex into the correct space, including the normals. And after transformation, you may have to re-normalize the normals.

  11. #11
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,071
    Iv'e been looking through some sample code, but I still don't get how to transform normals into different spaces .
    Can someone explain please?

    -psychopath
    M.Eng Computer Engineering Candidate
    B.Sc Computer Science

    Robotics and graphics enthusiast.

  12. #12
    Registered User
    Join Date
    Apr 2002
    Posts
    1,571
    Normals must be transformed by the transpose of the inverse of the matrix used to transform the geometry. I'm not going to get into the theory here, I'll leave that as an exercise for you.
    "...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Eliminating lighting "over-bright"
    By psychopath in forum Game Programming
    Replies: 1
    Last Post: 05-31-2006, 06:52 PM
  2. Replies: 6
    Last Post: 11-12-2005, 11:57 AM
  3. how to use operator+() in this code?
    By barlas in forum C++ Programming
    Replies: 10
    Last Post: 07-09-2005, 07:22 PM
  4. Help with C++ Math
    By aonic in forum C++ Programming
    Replies: 4
    Last Post: 01-29-2005, 04:40 AM
  5. toughest math course
    By axon in forum A Brief History of Cprogramming.com
    Replies: 12
    Last Post: 10-28-2003, 10:06 PM