Confusing Normals Problem

Member
Posts: 148
Joined: 2003.03
Post: #1
I'm rendering a terrain model (height map) as a vertex array, and I'm having a little problem generating the normals properly. I know how to calculate the normal of a triangle, thats not my problem. My problem is getting the correct vertices for the normal calculation. No matter what I try, I can't get it quite right. Note: The terrain is rendered as quads.

Code:
int hmapSize = 128;
double *hmapVertices;
int *hmapQuads;
double *hmapNormals;

-(void)buildTerrain
{
    NSImage *heightmap = [NSImage imageNamed:@"heightmap.jpg"];
    double hmapWidth=[heightmap size].width,hmapHeight=[heightmap size].height;
    double stepx = hmapWidth/((double)hmapSize), stepz = hmapHeight/((double)hmapSize);
    lmVec u,v;
    lmVec normal;
    int x,z;

    [heightmap lockFocus];
    
    hmapVertices = (double *)malloc(hmapSize*hmapSize * sizeof(double) * 3);
    hmapQuads = (GLuint *)malloc(hmapSize*hmapSize * sizeof(GLuint) * 4);
    hmapNormals = (double *)malloc(hmapSize*hmapSize * sizeof(double) * 3);

    //build vertices array
    int index=0;
    NSColor *pixel;
    double hscale = 2.0,vscale = 3.0;
    for(x=0;x<hmapSize;x++)
    {
        for(z=0;z<hmapSize;z++)
        {
            pixel = NSReadPixel(NSMakePoint(x*stepx,z*stepz));
            
            hmapVertices[index*3+0] = ((double)x)/hscale;
            hmapVertices[index*3+1] = [pixel redComponent]*vscale;
            hmapVertices[index*3+2] = ((double)z)/hscale;
            
            index++;
        }
    }

    //build quad & normal array
    index=0;
    int q=0;
    for(x=0;x<hmapSize*hmapSize-5;x++)
    {
        if(q<hmapSize-1)
        {
            hmapQuads[index*4+0] = x;
            hmapQuads[index*4+1] = x+hmapSize;
            hmapQuads[index*4+2] = x+hmapSize+1;
            hmapQuads[index*4+3] = x+1;
            
            q++;
            index++;
        }
        else
        {
            q=0;
        }
    }

    // generate normals...
}

Now before I started using vertex arrays for this, I was using regular glVertex commands, and everything looked fine.

I was hoping someone could show me a loop (to be placed at //generate normals...) that makes reference to the proper vertices (3 of them) in hmapVertices for each quad in hmapQuads. I'd really appreciate some help on this, I've been going at it for almost a week and can't get it right. Thanks a million.
Quote this message in a reply
Jesse
Unregistered
 
Post: #2
I'll give this is a shot, but I apologize if I'm misunderstanding your code...

First of all, are you actually rendering using GL_QUADS, or as triangles? I assume you're keeping the quads separate for frustum culling, etc., but in general I think triangles in whatever form are preferred over quads. Also, there are probably more efficient ways to represent and render the data, but it would be good to get this version working first.

From your code it looks like the verts for each quad are oriented counterclockwise starting in the lower right, assuming you're looking down from positive Y with the Z axis pointing 'north'. This also assumes that you're working in OpenGL's right-handed coordinate system. So we'll call the verts v0, v1, v2, v3, counterclockwise from the lower right.

In OpenGL forward-facing tris generally have their verts ordered counterclockwise, so the triangles for a given quad should be:

v0, v1, v2
v0, v2, v3

You have to take the cross product in the right order to get the normals facing the right direction, and I'm not sure what that is off the top of my head, but it should look something like this for the first triangle:

e0 = v1 - v0;
e1 = v2 - v0;
n = e0.Cross(e1); // Or e1.Cross(e0);
n.Normalize();

And similarly for the second triangle with the appropriate vertices.

That's off the top of my head, so I may very well have missed something. Let us know if that helps, though.

P.S. I was just looking at your post again, and wasn't sure if maybe you just wanted a single normal for each quad? It seems unlikely that this would work, as your quads are unlikely to be planar (another argument against rendering as quads). To get a normal for each triangle, you'll have to double the size of your normal array. Then, instead of having a separate loop (which you certainly could do), I'd create the normals in the 'create the quads' loop, two each after each quad, since you have the data immediately available...
Quote this message in a reply
Mars_999
Unregistered
 
Post: #3
If you are using vertex arrays you're going to need to use GL_NORMAL_ARRAY as a flag.
Code:
glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
Quote this message in a reply
Member
Posts: 148
Joined: 2003.03
Post: #4
Mars_999: Yes I know this. I have everything setup properly, I can even see differences in the normals for all the quads. But everything is wierd and doesn't look anything like it did when I was rendering each individual quad separately.


Jesse:

>First of all, are you actually rendering using GL_QUADS, or as triangles?
As quads. I took a stab at rendering with triangles but again that is quite confusing as well.

>I assume you're keeping the quads separate for frustum culling, etc., but in general I think triangles in whatever form are preferred over quads.
Currently, no I'm not keeping everything separate for rendering optimizations. This is exactly why I'm doing this, as to provide a basis for some type of polygon management. Also, I know that triangles are preferred over quads. What I'm doing now is just to help me as a basis for a much larger project.

>Also, there are probably more efficient ways to represent and render the data, but it would be good to get this version working first.
Yes, I know my code is somewhat inefficient and sloppy. In order for me to debug my code I have to break everything down to much simpler forms (ie, the multiple loops for everything)

>From your code it looks like...And similarly for the second triangle with the appropriate vertices.
Yes I'm positive the way I generate the normals are correct . My problem is that as I go through the normal array loop, something is going wrong with the way I relate the loop 'index' to the normals and quads array indices (ie, hmapVertices[hmapQuads[x*4+0]+0] or something like that...this is where I'm confused). I've tried dozens of different possibilities for the normals array, and all I seem to get are outrageous normal values, or normal values that aren't quite right. Currently, this is what I'm trying:

[sourcecode]
index = 0;
int index2=0;
for(x=0;x<hmapSize*hmapSize-5;x++)
{

u.x = hmapVert[hmapQuads[index2+1]].x-hmapVert[hmapQuads[index2+0]].x;
u.y = hmapVert[hmapQuads[index2+1]].y-hmapVert[hmapQuads[index2+0]].y;
u.z = hmapVert[hmapQuads[index2+1]].z-hmapVert[hmapQuads[index2+0]].z;
v.x = hmapVert[hmapQuads[index2+2]].x-hmapVert[hmapQuads[index2+0]].x;
v.y = hmapVert[hmapQuads[index2+2]].y-hmapVert[hmapQuads[index2+0]].y;
v.z = hmapVert[hmapQuads[index2+2]].z-hmapVert[hmapQuads[index2+0]].z;
normal.x = u.y*v.z - u.z*v.y;
normal.y = u.z*v.x - u.x*v.z;
normal.z = u.x*v.y - u.y*v.x;

/* i was trying this ...
u.x = hmapVertices[hmapQuads[index*4]+0]-hmapVertices[hmapQuads[index*4]+9];
u.y = hmapVertices[hmapQuads[index*4]+1]-hmapVertices[hmapQuads[index*4]+10];
u.z = hmapVertices[hmapQuads[index*4]+2]-hmapVertices[hmapQuads[index*4]+11];
v.x = hmapVertices[hmapQuads[index*4]+0]-hmapVertices[hmapQuads[index*4]+3];
v.y = hmapVertices[hmapQuads[index*4]+1]-hmapVertices[hmapQuads[index*4]+4];
v.z = hmapVertices[hmapQuads[index*4]+2]-hmapVertices[hmapQuads[index*4]+5];
normal.x = u.y*v.z - u.z*v.y;
normal.y = u.z*v.x - u.x*v.z;
normal.z = u.x*v.y - u.y*v.x;
*/

hmapNormals[index+0] = normal.x;
hmapNormals[index+1] = normal.y;
hmapNormals[index+2] = normal.z;

index2+=4;
index+=3;
}
[/sourcecode]

This produces the following:
[Image: normal_problem.jpg]
It seems to be close, but it's definately not right. Even when rendering as quads, it should still look at least somewhat correct (correct being as if it were rendered as triangles), right?

>P.S. I was just looking at your post again, and wasn't sure if maybe you just wanted a single normal for each quad?
Yes, for each quad I need a normal.

>It seems unlikely that this would work, as your quads are unlikely to be planar (another argument against rendering as quads).
When I render quads (with normals and lighting) directly using glVertex3,glNormal,etc. everything looks to be 'on point'.

To get a normal for each triangle, you'll have to double the size of your normal array. Then, instead of having a separate loop (which you certainly could do), I'd create the normals in the 'create the quads' loop, two each after each quad, since you have the data immediately available...
I originally had it like this, but as I said above, I had to break this down in order to make it easier to interpret.

Anyway, maybe I should try using triangles instead.
Thanks a lot for your assistance.
Quote this message in a reply
Mars_999
Unregistered
 
Post: #5
Just a thought, but are you trying to calculate the normal for each vertex or each polygon?
Quote this message in a reply
Member
Posts: 148
Joined: 2003.03
Post: #6
I'm trying to generate a normal for each quad. I'm curious, how do you calculate the normal of a vertex? Rasp
Quote this message in a reply
Jesse
Unregistered
 
Post: #7
A couple of quick questions. First, where are you normalizing the normals? (Sorry if I overlooked it.) Also, in your original code I thought you had stored the vert data as an array of doubles of size number of verts * 3, but here it looks like you're using structure or class syntax (i.e. vert.x, vert.y, etc.). I'd be interested to know what I'm missing here...

Finally, I'm still confused by the one normal per quad thing. Non-planar quads will probably render correctly as they are converted to triangles at render time, but a single normal for a quad will only be correct for one of the triangles that make up the quad. I don't know if this has anything to do with your problem...
Quote this message in a reply
Member
Posts: 148
Joined: 2003.03
Post: #8
>Also, in your original code I thought you had stored the vert data as an array of doubles of size number of verts * 3...
Ahh yes my fault. The reason I'm doing this is because I decided to break this down further (once again, to make it easier to interpret), and make a 'test' array of vertex objects:

[sourcecode]
typedef struct {
GLdouble x,y,z;
} lmVec;

lmVec hmapVert[128*128];

for(x=0;x<hmapSize*hmapSize;x++)
{
hmapVert[x].x = hmapVertices[x*3+0];
hmapVert[x].y = hmapVertices[x*3+1];
hmapVert[x].z = hmapVertices[x*3+2];
}
[/sourcecode]

So...
hmapVert[hmapQuads[x*4+2]].x
hmapVert[hmapQuads[x*4+2]].y
hmapVert[hmapQuads[x*4+2]].z

would be the same as

hmapVertices[hmapQuads[x*4+2]+0]
hmapVertices[hmapQuads[x*4+2]+1]
hmapVertices[hmapQuads[x*4+2]+2]


It's essentially the same as the block of code in my normals loop that is commented out with /* i was trying this ...

>First, where are you normalizing the normals?
Ahh, this could quite possibly be the problem.

>Finally, I'm still confused by the one normal per quad thing. Non-planar quads will probably render correctly as they are converted to triangles at render time, but a single normal for a quad will only be correct for one of the triangles that make up the quad. I don't know if this has anything to do with your problem...
I know this. Correct me if I'm wrong, once I get this fixed, the model will look "almost" correct using quads instead of triangles, right?
Quote this message in a reply
Member
Posts: 304
Joined: 2002.04
Post: #9
Quote:Originally posted by MacFiend
I'm trying to generate a normal for each quad. I'm curious, how do you calculate the normal of a vertex? Rasp

you add all the normals of all the triangles/quads that share that vertex together and then normalize the result. Its the difference between faceted and smooth shading. You will probably want this for a terrain engine.

check out <http://www.lighthouse3d.com/opengl/terra...troduction>

especially <http://www.lighthouse3d.com/opengl/terra...p3?normals>

hth
Quote this message in a reply
Member
Posts: 148
Joined: 2003.03
Post: #10
Thanks codemattic.

Just to make sure I'm thinking straight, normalizing is done by:

normal = normal/fabsf(normal);

P.S. codemattic
I have no clue how I overlooked this page. Wacko
Quote this message in a reply
Member
Posts: 148
Joined: 2003.03
Post: #11
Just one last question: When using vertex arrays, and assigning my normals array through glNormalPointer(), does glDrawElements() try to do per-vertex normals or per-face normals? I think this could be my problem. Thanks.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #12
All normals in OpenGL are vertex normals.

If you don't specify a normal for a vertex (only possible in immediate mode), the last-specified normal gets used.
Quote this message in a reply
Member
Posts: 148
Joined: 2003.03
Post: #13
Ok thanks for clearing that up OneSadCookie.

I finally got it to work right with the help of the tutorial codemattic posted. Here's the payoff:

[Image: normal_problem_fixed.jpg]

Thanks for all your help.
Quote this message in a reply
Feanor
Unregistered
 
Post: #14
Nice work.
Quote this message in a reply
Mars_999
Unregistered
 
Post: #15
Quote:Originally posted by MacFiend
Just one last question: When using vertex arrays, and assigning my normals array through glNormalPointer(), does glDrawElements() try to do per-vertex normals or per-face normals? I think this could be my problem. Thanks.


Ah I brought that up earlier! Calculating normals per vertex or per polygon or in your case per face same difference.

Reason I say this is I have been and still am working on my terrain engine. I have done all of the things you're asking about. I have mine textured and now am going to add in shadows, bumpmapping. I just finised up multitexturing lastnight. I wish I could post a screenshot but I don't have a webpage. Sad
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Normals Nick 20 8,478 Mar 25, 2005 09:31 AM
Last Post: tigakub
  Normals? Quicksilver 3 2,944 Jan 13, 2003 05:31 PM
Last Post: henryj