Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

ZMaster

Calculating normals

This topic is 5733 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I have a little problem with calculating the vector normals for lightning. I want to draw a torso to an OpenGL window, this works pretty good for now. So, now i want to add some specular lighning to the scene to give the "doughnut" a some shining effect. Ok, this works pretty good too, but to have a smooth shaded model, I need to specify a normal for each vertex. This is my problem. With my code, I still get a ugly flat shaded "doughnut". Here''s my code:
  
void CalculateNormal(float point1[3], float point2[3], float point3[3], float normal[3])
{
 float cross1[3], cross2[3];
 float length;
 
 //Calculating the two vectors from point1 -> point2 and point1 -> point3 

 cross1[0] = point1[0] - point2[0];
 cross1[1] = point1[1] - point2[1];
 cross1[2] = point1[2] - point2[2];
 
 cross2[0] = point2[0] - point3[0];
 cross2[1] = point2[1] - point3[1];
 cross2[2] = point2[2] - point3[2];
 
 //Calculating the crossproduct. Produces the normal

 normal[0] = cross1[1]*cross2[2] - cross1[2]*cross2[2];
 normal[1] = cross1[2]*cross2[0] - cross1[0]*cross2[2];
 normal[2] = cross1[0]*cross2[1] - cross1[1]*cross2[0];
 
 //Calculate length of the normal and make it unit length

 length = sqrt((normal[0]*normal[0]) + (normal[1]*normal[1]) + (normal[2]*normal[2]));
 
 normal[0] /= length;
 normal[1] /= length;
 normal[2] /= length;
}

void DrawTorus(float depth, float iR, float oR, int u, int v)
{
 //Variables for radius and temp vertices to draw

 float innerRadius = iR;
 float outerRadius = oR;
 float thickness = depth;
 int maxV = v;
 int maxU = u;
 float x1, y1, z1;
 float x2, y2, z2;
 float x3, y3, z3;
 float x4, y4, z4;
 
 //defines pi :)

 float pi = 3.141; 
 
 //Draw the torso with GL_TRIANGLE_STRIPS

 glBegin(GL_TRIANGLE_STRIP);
 //I draw the points using a loop that draws the four vertices by increasing the counter with a calculated

 //angle and this 360/GIVEN_ANGLE times

 for(float phi = 0.0; phi <= 360.0; phi += maxV)
 {
  for(float theta = 0.0; theta < 360.0; theta += maxU)
  {
    //Sine and cosine calculations for the four vertices (freaky stuff :) )

    float CosPhi=cos(phi/180.0*3.142);
    float SinPhi=sin(phi/180.0*3.142);
    float CosTheta=cos(theta/180.0*3.142);
    float SinTheta=sin(theta/180.0*3.142);

    x1= CosTheta * (CosPhi * innerRadius+outerRadius);
    y1= SinTheta * (CosPhi * innerRadius+outerRadius);
    z1= (SinPhi) * innerRadius;
    
     CosPhi=cos(phi/180.0*3.142);
     SinPhi=sin(phi/180.0*3.142);
     CosTheta=cos((theta + maxU)/180.0*3.142);
     SinTheta=sin((theta + maxU)/180.0*3.142);

    x2= CosTheta * (CosPhi * innerRadius+outerRadius);
    y2= SinTheta * (CosPhi * innerRadius+outerRadius);
    z2= (SinPhi) * innerRadius;
    
     CosPhi=cos((phi + maxV)/180.0*3.142);
     SinPhi=sin((phi + maxV)/180.0*3.142);
     CosTheta=cos(theta/180.0*3.142);
     SinTheta=sin(theta/180.0*3.142);

    x3= CosTheta * (CosPhi * innerRadius+outerRadius);
    y3= SinTheta * (CosPhi * innerRadius+outerRadius);
    z3= (SinPhi) * innerRadius;
    
     CosPhi=cos((phi + maxV)/180.0*3.142);
     SinPhi=sin((phi + maxV)/180.0*3.142);
     CosTheta=cos((theta + maxU)/180.0*3.142);
     SinTheta=sin((theta + maxU)/180.0*3.142);

    x4= CosTheta * (CosPhi * innerRadius+outerRadius);
    y4= SinTheta * (CosPhi * innerRadius+outerRadius);
    z4= (SinPhi) * innerRadius;
    
    //putting the calculated points in 4 arrays

    float v1[3] = {x1, y1, z1};
    float v2[3] = {x2, y2, z2};
    float v3[3] = {x3, y3, z3};
    float v4[3] = {x4, y4, z4};
    //the normals I''ll calculate

    float normal1[3], normal2[3], normal3[3], normal4[3];
    
    //This is the buggy part. I calculate the normals for every single vertex (to get a smooth shaded figure) here.

    //I don''t know if wether this is right or this is the correct order :)

    CalculateNormal(v1, v2, v3, normal1);
    CalculateNormal(v2, v4, v3, normal2);
    CalculateNormal(v3, v2, v4, normal3);
    CalculateNormal(v4, v3, v2, normal4);
    
    //Setting up every normal and then draw the corresponding vertex

    glNormal3fv(normal1);
    glVertex3fv(v1);
    
    glNormal3fv(normal2);
    glVertex3fv(v2);
    
    glNormal3fv(normal3);
    glVertex3fv(v3);
    
    glNormal3fv(normal4);
    glVertex3fv(v4);
  }
 }
 glEnd();
}
  
Maybe there is a bug in my CalculateNormal() function or somewhere else. No idea. I hope someone else has thx Flo

Share this post


Link to post
Share on other sites
Advertisement
A quick look, and I caught:

normal[0] = cross1[1]*cross2[2] - cross1[2]*cross2[2];

You really should make a function cross_product. Premature optimization...

Cédric

Share this post


Link to post
Share on other sites
Ohh... what a silly mistake
Ok thanks, normals work now.
But... my doughnut is now some kind of mixture between a flat and smooth shaded model
Any Ideas to optimise the code to get a nice real smooth shaded doughnut.

Share this post


Link to post
Share on other sites
what you are effctively doing IS flat shading. One normal per triangle. You need one normal per vertex.

Usually, for each vertex V, have a normal N.

Set the normal to V=(0, 0, 0) to start with.

Everytime you compute the triangle TriN, add TriN to the vertex normals. N += TriN.

When you finished computing your triangle normals, normalise all the vertex normals. This is the standard method of computing vertex normals when only given vertex positions and triangle indices.

But based on your code, the algo won''t work. It would be much better to store the v[], into a large buffer, and use vertex indices for vertices. Then, you can get the real vertex normals.

If I were you, I would compute the normals analitically, using trigonometry, the same way you compute the vertices (freaky stuff).

It could be quite easy for a torus. you base your normal calcualtion on the vertex position, and the position at the ''core'' of the doughnut.


  
// N1

// .

// .

// v1 *-------

// / . \

// | *C |

// | |

// \_______/

//


N1[0] = (v1[0] - C[0]) / innerradius;
N1[1] = (v1[1] - C[1]) / innerradius;
N1[2] = (v1[2] - C[2]) / innerradius;

Share this post


Link to post
Share on other sites
Thanks a lot oliii. Works perfectly and looks really smooth shaded now
But there's one thing left, I'd like to know : You were right, in the case of the doughnut I could simply take the vertices relative to the core point and generate a unit vector out of the result. But what to do if I'd have a much more complex structure eg. a Quake3 model or something. Isn't there a way to calculate the normals of each vertex of a model by using the cross product or something? Or is there any core point in such a model?

[edited by - ZMaster on April 3, 2003 11:01:08 AM]

Share this post


Link to post
Share on other sites
As I said, you need to base your triangles with indices. Else you can''t find the normals. Or you''ll have to convert the vertex-based triangles in index-triangles first.

Right, here is the code using vector maths (very easy, you''re half-way there in your code).


  


struct cVertex
{
Vector P;
Vector N;

cVertex(void):
N(0, 0, 0),
P(0, 0, 0)
{}

cVertex(const Vector& Pos):
P(Pos),
N(0, 0, 0)
{}
};


//----------------------------------------------------------------

// triangle structure. 3 indices, 1 normal.

//----------------------------------------------------------------

struct cTri
{
int V[3];

Vector N;

cTri(void):
N(0, 0, 0)
{
V[0] = V[1] = V[2] = 0;
}

//----------------------------------------------------------------

// Setup triangle. If you want, compute vertex normals based on edges

// instead of triangle normals.

//----------------------------------------------------------------

cTri(cVertex* Pool, int V0, int V1, int V2, bool edges=false)
{
V[0] = V0;
V[1] = V1;
V[2] = V2;

Vector E(Pool[V1].P - Pool[V0].P);
Vector F(Pool[V2].P - Pool[V1].P);

N = E ^ F;
N.Normalise();

if (edges)
{
Vector G(Pool[V0].P - Pool[V2].P);
E.Normalise();
F.Normalise();
G.Normalise();

Pool[V0].N += (G - E);
Pool[V1].N += (E - F);
Pool[V2].N += (F - G);
}
else
{
Pool[V0].N += N;
Pool[V1].N += N;
Pool[V2].N += N;
}
}
};


//----------------------------------------------------------------

// Smooth shaded mesh, with 1 normal per vertex

//----------------------------------------------------------------

struct cSmoothMesh
{
//----------------------------------------------------------------

// list of smooth vertices and tris

//----------------------------------------------------------------

cVertex* pVertices;
cTri* pTris;
int Vnum;
int Tnum;

//----------------------------------------------------------------

// Make a smooth mesh out of a list of vertex triplets

//----------------------------------------------------------------

cSmoothMesh(const Vector *TriVerts, int NumTriVerts)
{
pVertices = new cVertex[NumTriVerts];
pTris = new cTri[NumTriVerts];
Vnum = 0;
Tnum = 0;

for (int i = 0; i < NumTriVerts / 3; i ++)
{
int i0 = AddVertex(TriVerts[i*3+0]);
int i1 = AddVertex(TriVerts[i*3+1]);
int i2 = AddVertex(TriVerts[i*3+2]);

pTris[Tnum++] = cTri(pVertices, i0, i1, i2);
}

for (i = 0; i < Vnum; i ++)
{
pVertices[i].N.Normalise();
}
}

~cSmoothMesh(void)
{
delete[] pVertices;
delete[] pTris;
}

//----------------------------------------------------------------

// Add a new vertex in the list. If found a vertex at the same position,

// don''t add a new one, but return the index of the old one instead.

//----------------------------------------------------------------

int AddVertex(const Vector& V)
{
for(int i = 0; i < Vnum; i ++)
{
if (pVertices[i].P == V)
return i;
}

pVertices[Vnum] = cVertex(V);

return Vnum-1;
}
};


Share this post


Link to post
Share on other sites
note. this might actually look too smooth. You can''t get sharp edges with this technique, as you blend vertices together.

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!