View Full Version : Lighting a textured sphere (calculating normals)

12-02-2008, 07:39 AM
Hi, I'm studying openGL for university second year and I am having some problems with light and textures on a sphere.

The code to make the sphere was given to us by our tutor and I came up with the function to calculate normals:

Sphere funtion:

void drawSphere(float r, int n)
float len=r;
int inc=n;
int z,x;

float pi = M_PI;

float dist1=len*sin((pi*z)/180);
float dist2=len*sin((pi*(z+inc))/180);

float dist1a=sqrt(len*len-dist1*dist1);
float dist2a=sqrt(len*len-dist2*dist2);

float x1=dist1a*cos((2 *pi*x)/360);
float y1=dist1a*sin(2 *pi*x/360);

float x2=dist1a*cos(2 *pi*(x+inc)/360);
float y2=dist1a*sin(2 *pi*(x+inc)/360);

float x3=dist2a*cos(2 *pi*x/360);
float y3=dist2a*sin(2 *pi*x/360);

float x4=dist2a*cos(2 *pi*(x+inc)/360);
float y4=dist2a*sin(2 *pi*(x+inc)/360);

stVec p1,p2,p3;

p1.x= x1; p1.y=y1; p1.z= dist1;
p2.x= x2; p2.y=y2; p2.z= dist1;
p3.x= x4; p3.y=y4; p3.z=dist2;
CalcNormal(p2.x, p2.y, p2.z, p1.x, p1.y, p1.z, p3.x, p3.y, p3.z);
//printf("X: %f Y: %f Z: %f \n",normal.x,normal.y,normal.z);

glTexCoord2f(0.0, 0.0); glVertex3f(x1,y1,dist1);
glTexCoord2f(0.0, 1.0); glVertex3f(x2,y2,dist1);
glTexCoord2f(1.0, 1.0); glVertex3f(x4,y4,dist2);
glTexCoord2f(1.0, 0.0); glVertex3f(x3,y3,dist2);

p1.x= x1; p1.y=y1; p1.z= -dist1;
p2.x= x2; p2.y=y2; p2.z= -dist1;
p3.x= x4; p3.y=y4; p3.z= -dist2;
CalcNormal(p2.x, p2.y, p2.z, p1.x, p1.y, p1.z, p3.x, p3.y, p3.z);

glNormal3f(normal .x,normal.y,normal.z);
glTexCoord2f(0.0, 0.0); glVertex3f(x1,y1,-dist1);
glTexCoord2f(0.0, 1.0); glVertex3f(x2,y2,-dist1);
glTexCoord2f(1.0, 1.0); glVertex3f(x4,y4,-dist2);
glTexCoord2f(1.0, 0.0); glVertex3f(x3,y3,-dist2);


Normals function:

void CalcNormal(float p1_x,float p1_y,float p1_z,float p2_x,float p2_y,float p2_z,float p3_x,float p3_y,float p3_z)
// Calculate vectors
float var1_x = p2_x - p1_x;
float var1_y = p2_y - p1_y;
float var1_z = p2_z - p1_z;

float var2_x = p3_x - p1_x;
float var2_y = p3_y - p1_y;
float var2_z = p3_z - p1_z;

// Get cross product of vectors
normal.x = (var1_y * var2_z) - (var2_y * var1_z);
normal.y = (var1_z * var2_x) - (var2_z * var1_x);
normal.z = (var1_x * var2_y) - (var2_x * var1_y);

// Normalise final vector
float vLen = sqrt( (normal.x * normal.x) + (normal.y * normal.y) + (normal.z * normal.z) );

normal.x = normal.x/vLen;
normal.y = normal.y/vLen;
normal.z = normal.z/vLen;


These dont seem to be working very well... Using the sphere function to draw a textured sphere works ok, except all sides have a default normal of [0, 0, 1]; so light looks flat, when I implement the CalcNormal function to get the normal of each face on the sphere they seem to be completely wrong.

Each square is lit completely wrong, one may be very dark the next might be very bright, there is no coherence between faces.

Is there anything obvious I'm doing wrong? Ive only been using openGL for a few weeks so I'm a complete noob.

Thanks for any help :)

12-03-2008, 09:47 AM
At first sight, the CalcNormal function looks correct to me. Check that you pass the right points to it.
With your code you call set one normal for each quad, you should have flat shading.

Each square is lit completely wrong, one may be very dark the next might be very bright, there is no coherence between faces.

This is because your normals are flipped sometimes, pointing toward the sphere center. If you draw them, you would certainly see that.
To avoid this kind of problem, you need to create each quad vertex in the same order, for example clock wise. If it is not the case, some quads would disappear if you enable culling with glEnable(GL_CULL_FACE) which relies on the vertices order.