Skip to main content
gamedev.net
🔒 Locked

opengl normal averageing

Started by detox Oct 18, 2000 at 5:47 AM 6 replies 350+ views
Original Post
detox
detox
Im looking for some normal averaging algorithims so i can do smooth shadeing correctly. The basic one is for each vertex to add all the normal vectors of the surrounding faces and then normalise it. First off im a little unsure on how to weight it. Whats a good weighting formula? Also it seems to be very slow and my athlon 750 dies if i try to do normal averageing on a large model. Any fast algorithims for this? Cheers for any help/web links adam hamfdeath@yahoo.com
havoc
havoc
But you can calculate normals only once. Also you can save your model to file with averaged normals.
Ridcully
Ridcully
i assume you want a one-time normal calculation of your model.
well, just find all adjacent faces around a vertex, calculate their normals, add them up, and normalize the vector you get.

i have been doing this for a 20000 triangle landscape for testing purposes, and it takes about 5-10secs on my athlon 700.
and my algorithm is REALLY lame, i am sure you can do a lot better if you think about it some time

rid
Ferrit
Ferrit
Well, i'll answer you question: Heres the code.

    

//usage: float *Normals = CalcVertexNormals(Array containing the models vertices, Number of vertices, Array of indexes to all triangles)



float* CalcVertexNormals(float *fVertices,unsigned int num_verts,unsigned int *uiTriList,
unsigned int num_tri_list)
{
int total=0;

float *fVertNorms = (float*)malloc((sizeof(float))*num_verts*3);

for(unsigned int VertexNum=0;VertexNum<num_verts;VertexNum++)
{

unsigned int tri = 0; //tri represent no. of shared triangles in a vertex


unsigned int *triangles = (unsigned int*)malloc(sizeof(unsigned int)*3*6);
//Allocate memory for 6 tri's, inc. x,y,z. Triangles is
//an array [6][3] to hold 6 (max. number of triangles
//sharing a vertex) triangles
//


for(unsigned int ctr=0;ctr<num_tri_list;ctr++)
{



if((uiTriList[(ctr*3)] == VertexNum) ||
(uiTriList[(ctr*3)+1] == VertexNum) ||
(uiTriList[(ctr*3)+2] == VertexNum))
//"If Triangle ctr's x,y or z index is equal to the current vertex (VertexNum) then store it
//in triangles array, and increase tri by one"


{
triangles[(tri*3)] = uiTriList[(ctr*3)];
triangles[(tri*3)+1] = uiTriList[(ctr*3)+1];
triangles[(tri*3)+2] = uiTriList[(ctr*3)+2];

tri++;
}



}
//We now have all the triangles sharing the current vertex in the triangles array

float NormalBuffer[3];
NormalBuffer[0] = 0;
NormalBuffer[1] = 0;
NormalBuffer[2] = 0;

for(unsigned int ctr=0;ctr<tri;ctr++)
{
float VectorIn[3][3];
float NormalOut[3];
int temp;

temp = triangles[(ctr*3)]; //Triangle vertex A

VectorIn[0][0] = fVertices[(temp*3)]; //A's x component

VectorIn[0][1] = fVertices[(temp*3)+1]; //A's y component

VectorIn[0][2] = fVertices[(temp*3)+2]; //A's z component


temp = triangles[(ctr*3)+1]; //Triangle Vertex B

VectorIn[1][0] = fVertices[(temp*3)]; //etc.

VectorIn[1][1] = fVertices[(temp*3)+1];
VectorIn[1][2] = fVertices[(temp*3)+2];

temp = triangles[(ctr*3)+2]; //Triangle Vertex C

VectorIn[2][0] = fVertices[(temp*3)];
VectorIn[2][1] = fVertices[(temp*3)+1];
VectorIn[2][2] = fVertices[(temp*3)+2];

calcNormal(VectorIn,NormalOut,FALSE);

NormalBuffer[0] += NormalOut[0]; //Add to normal buffer

NormalBuffer[1] += NormalOut[1];
NormalBuffer[2] += NormalOut[2];
//To find a vertex normal, we average the face normals of the triangles which share that vertex.
//Hence we must put all the normal info into a buffer, and divide by no. of triangles

}

NormalBuffer[0] = (NormalBuffer[0]/tri);
NormalBuffer[1] = (NormalBuffer[1]/tri);
NormalBuffer[2] = (NormalBuffer[2]/tri);

//Above code has averaged the normal. We do this BEFORE reducing it to unit


ReduceToUnit(NormalBuffer);

fVertNorms[(VertexNum*3)] = NormalBuffer[0];
fVertNorms[(VertexNum*3)+1] = NormalBuffer[1];
fVertNorms[(VertexNum*3)+2] = NormalBuffer[2];

//Vertex no. (VertexNUm) has now got the averaged, unit-length normal. Wahey!


}



return fVertNorms;
}



void ReduceToUnit(float vector[3])
{
float length;

length = (float)sqrt((vector[0]*vector[0]) +
(vector[1]*vector[1]) +
(vector[2]*vector[2]));

if(length == 0.0f)
length = 1.0f;


vector[0] /= length;
vector[1] /= length;
vector[2] /= length;
}

void calcNormal(float v[3][3], float out[3], unsigned int Reduce)
{
float v1[3],v2[3];
static const int x = 0;
static const int y = 1;
static const int z = 2;

v1[x] = v[0][x] - v[1][x];
v1[y] = v[0][y] - v[1][y];
v1[z] = v[0][z] - v[1][z];

v2[x] = v[1][x] - v[2][x];
v2[y] = v[1][y] - v[2][y];
v2[z] = v[1][z] - v[2][z];

out[x] = v1[y]*v2[z] - v1[z]*v2[y];
out[y] = v1[z]*v2[x] - v1[x]*v2[z];
out[z] = v1[x]*v2[y] - v1[y]*v2[x];

if(Reduce == TRUE)
{
ReduceToUnit(out);
}
}


I hope this helps.

P.S. It probably is slow

Edited by - Ferrit on October 18, 2000 2:39:56 PM
zedzeek
zedzeek
a more correct smooth would be to do like the red book saiz and average the normals depending on the faces angles

http://members.xoom.com/myBollux
detox
detox
Ferrit, thats the exact algo im useing (well its in c++ not c!) but if the model has 50000 triangles and 20000 vertexs it does take a while to do it.

Yeah the red book says weight it my angles but im a little unsure what that means. Im asuming you only do a average if the dot product is greater than the cosine of a pre-defined angle (say 90) or something like that.

So im looking for that weight function and a better way of doing it. Finding the list of triangles which each vertex is connected to is costly. Any better ways guys?

adam
Ferrit
Ferrit
To be honest, seeing as the calculation sin''t at runtime, speed
shouldn''t really be such a problem

The tin-foil lining in my baseball cap stops the government reading my thoughts. ok? OK??
zedzeek
zedzeek
i dont know of any other ways except for checking against all the other tris in the model. do what i do precalculate the normals and include them in the model data

http://members.xoom.com/myBollux

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.