Sign in to follow this  
cozzie

Vertex normals. Thread solved!

Recommended Posts

cozzie    5029
Hi. At the moment I use glEnableClientState for using normals in my vertex arrays, which means I'm using normals per vertex. I calculate them this way: vtxnormal.x = 0; vtxnormal.y = 0; vtxnormal.z = 0; then I calculate the facenormal and add it to the vtx normals: vtxnormal.x += facenormal.x; vtxnormal.y += facenormal.y; vtxnormal.z += facenormal.z; Now I'm wondering if this is the best way, so that my lighting turns out good. What would you advise, keep on going like this? Or should I use just facenormals, if so, how should I do that? Thanks. I studied the most vector theory today, I'm glad this is all clear to me know. It's sometimes good to stop adding feautures to your 3d engine and take a look at what you did till now, with this I mean a really good look (math etc.) [Edited by - cozzie on November 27, 2004 11:29:36 AM]

Share this post


Link to post
Share on other sites
Raduprv    997
To calculate the vetex normals, just find out all the faces that vertex belongs to, add all the X, Y and Z values of each face, then divide by he number of faces. In some cases vertex normals look better, but in other cases face normals look better.
For example, if you have a box, and the top plane is divided in many faces, then by using vertex normals the top plane will have different lighting, rather than uniform lighting. So the vertex normals work best for 'natural' objects such as stones, trees, etc. but suck for man made objects, such as houses, tables, etc.

Share this post


Link to post
Share on other sites
OpenGLAaron    122
Quote:
Original post by Raduprv
To calculate the vetex normals, just find out all the faces that vertex belongs to, add all the X, Y and Z values of each face, then divide by he number of faces.

If you just normalize at the end, you don't have to keep track of the number of faces. On the other hand, normalizing deals with a slow square root function that you might like to avoid.

Share this post


Link to post
Share on other sites
Raduprv    997
Quote:
Original post by OpenGLAaron
If you just normalize at the end, you don't have to keep track of the number of faces. On the other hand, normalizing deals with a slow square root function that you might like to avoid.


Yeah, that's the idea. 3 divisions (plus some incrementation) is much faster than normalizing.

Share this post


Link to post
Share on other sites
cozzie    5029
Thanks for all the reactions guys.

I was wondering:

"To calculate the vertex normals, just find out all the faces that vertex belongs to, add all the X, Y and Z values of each face, then divide by he number of faces."

Example:
vertex3 = is used in face 3,5 and 6
facenormal face3 = 4,2,1
facenormal face5 = 2,3,2
facenormal face6 = 1,4,2

Vertex3 normal = (4+2+1)/3, 2+3+4(/3), (1+2+2)/3
Vertex3 normal = 2.33, 3, 1,67

Is this the correct way your explaining? Doesn't this take long to calculate?

I'm also wondering if there's a way to use face normals instead of vertex normals, when using vertex arrays. I think glEnableClientState(normal stuff) can only be used for vertex normals, or can it also take face normals?

Thanks again.

Share this post


Link to post
Share on other sites
OpenGLAaron    122
cozzie - As you loop through all your vertices, for each face that it is a vertex of, add the current face normal to it and keep track of the number of faces with a counter. After you've looped through all the faces that it's a vertex of, then you divide by the counter. Unless you know beforehand how many faces there will be, as in your example, then that would be faster. Either way, it's less expensive than a square root in a normalization. (I just use the normalization because I like to think I'm clever)

To use face normals, you have to specify a given vertex the same number of times as the number of faces it is a part of. So, if a vertex is part of 3 faces, you need to explicitly set it's normal and vertex in openGL 3 times. That's because openGL only knows about normals as per-vertex attributes. At least, this is as far as I know.

Share this post


Link to post
Share on other sites
cozzie    5029
Okay, sounds clear.
You say that checking in which faces a vertex is used is quicker because then you don't need normalization. Is this true?

I would think that in your situation I have to do this:

- calculate facenormal
- per vertex: add facenormals together for all faces in which the vertex is used and divide by the number of faces
- normalize the resulting vertex normal

Or is it not necessary to normalize the resulting vertex normal?

It sounds like using facenormals during my rendering will be quite a bit slower per frame then using the vertex normals, which OpenGL supports without tricks.
Is there anyone who has experience with this?

Share this post


Link to post
Share on other sites
FReY    424
Quote:

Or is it not necessary to normalize the resulting vertex normal?


It's better to normalize, as there are less innaccuracies that way. Proof:
Take 2 perpendicular vectors (1,0,0) and (0,1,0), As you can see, they're both unit vectors. Now, add those 2 together and divide by 2 and the length of the result is nowhere even near 1.(it is 1.414) Granted, it's an extreme case, but this inaccuracy can seriously screw up lighting. But unless you have GL_NORMALIZE enabled (in which case you don't even need to divide), you're going to have to normalize yourself, unless your normals are static in which case you normalize BEFORE execution of your main loop.

Yah, go for vertex normals. They're a general solution and you can always force a vertex with the same position but different normals into 2 different vertices in your vertex arrays. I'm not sure if this is obvious, but again, do that in your tool chain and not at run-time. :)

Share this post


Link to post
Share on other sites
cozzie    5029
;-)

Thanks for the explanation.
I'm gonna stay with the vertex normals, with normalization for the vertex normals.
I'm not using glNormalize, just doing it myself.

The speed improvement isn't that necessary since calculating my normals is only done once, when initializing.

Thanks again.
greets from holland

Share this post


Link to post
Share on other sites
Coluna    314
A good way to calculate vertex normals, when u have some flat surfaces , is to set a threshold to check when add 2 normals...for example,

if DOT (normal_1, normal_2) > threshold
add normal_1, normal_2

so u can get some parts flat and others smooth...see ya

Share this post


Link to post
Share on other sites
cozzie    5029
I don't exactly now what you mean, can you maybe give an example?

As I read your explanation it looks like that way you calculate vertexnormals in 2 different ways, one special way for calculating them for larger surfaces with flat-to-each-other-connected-faces ;-) Is this what you mean?

Share this post


Link to post
Share on other sites
cozzie    5029
I've changed my CalcNormals function (per object),
it seems to be working fine.

Although without the last Normalization the effect looks better, is this explainable?

Here's the source:


void GLobject::CalcNormals()
{
int c, c2;

for(c=0;c<nr_vertices;c++)
{
normals[c].x = 0.0f;
normals[c].y = 0.0f;
normals[c].z = 0.0f;
}
for(c=0;c<nr_faces;c++)
{
facenormals[c] = GetFaceNormal(vertices[faces[c].v1],
vertices[faces[c].v2],
vertices[faces[c].v3]);
}

for(c=0;c<nr_vertices;c++)
{
for(c2=0;c2<nr_faces;c2++)
{
if(faces[c2].v1 == (unsigned int)c)
{
normals[faces[c2].v1].x += facenormals[c2].x;
normals[faces[c2].v1].y += facenormals[c2].y;
normals[faces[c2].v1].z += facenormals[c2].z;
}
if(faces[c2].v2 == (unsigned int)c)
{
normals[faces[c2].v2].x += facenormals[c2].x;
normals[faces[c2].v2].y += facenormals[c2].y;
normals[faces[c2].v2].z += facenormals[c2].z;
}
if(faces[c2].v3 == (unsigned int)c)
{
normals[faces[c2].v3].x += facenormals[c2].x;
normals[faces[c2].v3].y += facenormals[c2].y;
normals[faces[c2].v3].z += facenormals[c2].z;
}
}
}

for(c=0;c<nr_vertices;c++)
{
normals[c] = Normalize(normals[c]);
}
}

Share this post


Link to post
Share on other sites
FReY    424
No clue why it would look better without normalization... your algorithm looks spot on. Maybe there is something wierd in your Normalize function?

Share this post


Link to post
Share on other sites
zedzeek    529
this doesnt look right

for(c=0;c<nr_vertices;c++)
{
for(c2=0;c2<nr_faces;c2++)
{
if(faces[c2].v1 == (unsigned int)c)
{
normals[faces[c2].v1].x += acenormals[c2].x;
etc

for each vertice u want a anverage normal right?, thus u loop through all the verts seeing if this vertice is a part of one of the faces, if it is, then the faces normal is added to the vertices corresponding normal, thus it should be

if(faces[c2].v1 == (unsigned int)c)
{
normals[c].x +=

Share this post


Link to post
Share on other sites
FReY    424
I saw that too Zedzeek, but it will lead to exactly the same results as what his current code does anyway.


if(faces[c2].v1 == (unsigned int)c)



In this case, c is equal to faces[c2].v1, so his expression remains valid.

Share this post


Link to post
Share on other sites
_DarkWIng_    602
OK.. why nobody posted this? Your wersion runs in O(nr_vertices*nr_faces) while it can be done in O(nr_vertices+nr_faces) ...read: much faster.


void GLobject::CalcNormals() {
for( int c=0; c<nr_vertices; ++c ) {
normals[c].x = 0.0f;
normals[c].y = 0.0f;
normals[c].z = 0.0f;
}
for( int c=0; c<nr_faces; ++c ) {
facenormals[c] = GetFaceNormal( vertices[faces[c].v1],
vertices[faces[c].v2],
vertices[faces[c].v3] );

normals[faces[c].v1].x += facenormals[c].x;
normals[faces[c].v1].y += facenormals[c].y;
normals[faces[c].v2].z += facenormals[c].z;

normals[faces[c].v2].x += facenormals[c].x;
normals[faces[c].v2].y += facenormals[c].y;
normals[faces[c].v2].z += facenormals[c].z;

normals[faces[c].v3].x += facenormals[c].x;
normals[faces[c].v3].y += facenormals[c].y;
normals[faces[c].v3].z += facenormals[c].z;
}
for( int c=0; c<nr_vertices; ++c ) {
normals[c] = Normalize(normals[c]);
}
}


Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this