PDA

View Full Version : Normal Generation Trouble.



MrShoe
06-15-2001, 03:20 AM
Today I came up with an algorith for generating normals. It works fine for like a single surface and all. I then tried to apply it to my terrain generator to get lighting. But the results are not good and the terrain does not look like its lit at all. Here is the important code:

struct normals{
float x, y, z;
};
typedef struct normals normals;
normals normal[TERRAINWIDTH*TERRAINLENGTH];

...

void calculate_normal(int normal_number, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3){
float vec1_x, vec1_y, vec1_z, vec2_x, vec2_y, vec2_z;
float bigx, bigy, bigz;

vec1_x = x2 - x1;
vec1_y = y2 - y1;
vec1_z = z2 - z1;
vec2_x = x3 - x1;
vec2_y = y3 - y1;
vec2_z = z3 - z1;

bigx = (vec1_y*vec2_z) - (vec1_z*vec2_y);
bigy = (vec1_z*vec2_x) - (vec1_x*vec2_z);
bigz = (vec1_x*vec2_y) - (vec1_y*vec2_x);

normal[normal_number].x = bigx/sqrt(bigx*bigx + bigy*bigy + bigz*bigz);
normal[normal_number].y = bigy/sqrt(bigx*bigx + bigy*bigy + bigz*bigz);
normal[normal_number].z = bigz/sqrt(bigx*bigx + bigy*bigy + bigz*bigz);

normal[normal_number].x = abs(normal[normal_number].x);
normal[normal_number].y = abs(normal[normal_number].y);
normal[normal_number].z = abs(normal[normal_number].z);
}

void renderScene(void){
float height;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glBindTexture(GL_TEXTURE_2D, texture[0]);
glRotatef(angle, 0.0, 1.0, 0.0);
glRotatef(25, 1.0, 0.0, 0.0);
glTranslatef(-4.0, -2.0, zoom);
glColor3f(1.0, 1.0, 1.0);
for(mapx = 0; mapx < TERRAIN_LENGTH-1; mapx++){
for(mapz = 0; mapz < TERRAIN_WIDTH-1; mapz++){
glBegin(GL_TRIANGLE_STRIP);
calculate_normal((mapx*99)+mapz, mapx/DETAIL, height, mapz/DETAIL, mapx/DETAIL, height, (mapz+1)/DETAIL, (mapx+1)/DETAIL, height, mapz/DETAIL);
glNormal3f(normal[(mapx*99)+mapz].x, normal[(mapx*99)+mapz].y, normal[(mapx*99)+mapz].z);
height = map[mapx][mapz];
glTexCoord2f(0.0, 0.0);
glVertex3f(mapx/DETAIL, height, mapz/DETAIL);
height = map[mapx][mapz+1];
glTexCoord2f(0.0, 1.0);
glVertex3f(mapx/DETAIL, height, (mapz + 1)/DETAIL);
height = map[mapx+1][mapz];
glTexCoord2f(1.0, 0.0);
glVertex3f((mapx+1)/DETAIL, height, mapz/DETAIL);
height = map[mapx+1][mapz+1];
glTexCoord2f(1.0, 1.0);
glVertex3f((mapx+1)/DETAIL, height, (mapz+1)/DETAIL);
glEnd();
}
}
glPopMatrix();
glutSwapBuffers();
}


I just made the normal algorithm today so it might not be the "proper' or standard way to calculate normals but it works for a single polygon... Please help, thanks.

Michael Steinberg
06-15-2001, 06:15 AM
The abs( ... ) look strange. Try to leave them out. Normals may have positive and negative components as any other vector may have. The only thing is that they have to be unit (length 1).
You can do this like this:

vector* 1/ |vector| = unit vector
since 1/|vector| is constant over all three components, you can calculate it just once.

MrShoe
06-15-2001, 03:32 PM
Well, I removed the abs but the results are still exactly the same. Also, the following lines normalise the vectors:
normal[normal_number].x = bigx/sqrt(bigx*bigx + bigy*bigy + bigz*bigz);
normal[normal_number].y = bigy/sqrt(bigx*bigx + bigy*bigy + bigz*bigz);
normal[normal_number].z = bigz/sqrt(bigx*bigx + bigy*bigy + bigz*bigz);

Any other ideas why it doesnt work??

harsman
06-16-2001, 05:17 AM
Maybe your normals are reversed, try reversing the order of the crossproduct of the edge vectors, use (vec2 x vec1) instead of (vec1 x vec2) like you do now. Also consider the fact that it might not be your normals that are incorrect, it might be something stupid like forgetting to set a material or enabling color material. If everything shows up white thats probably your problem since you set the current colour to white above the rendering loop.

lgrosshennig
06-16-2001, 08:49 AM
Hmmmm...

I see you have an 2D array of normals and iterate though it step by step.

The problem is your code is calculating only a normal for the given triangle and not the normal for the vertex (you need to average all triangle normals that share a given point).

I can find a that you assign the variable "Normal_number" a meaningfull value (it will be allways zero right?)

Hope that help.

LG http://www.opengl.org/discussion_boards/ubb/biggrin.gif

MrShoe
06-16-2001, 05:05 PM
At this moment I dont want per-vertex lighting so that I only assign a normal to each surface not to each vertex. But the results still do not look like the scene is properly lighted. If anyone is willing to look at the whole source code I can send the whole source code to look at.

lgrosshennig
06-17-2001, 02:42 AM
I dont mind having a look at it.
You'll find my email address in the profile.

Regards,

LG http://www.opengl.org/discussion_boards/ubb/biggrin.gif

Michael Steinberg
06-17-2001, 04:29 AM
Say that I'm an idiot, but can you do reasonable flat shading in a triangle strip?

Michael Steinberg
06-17-2001, 04:30 AM
Try replacing GL_TRIANGLE_STRIP with GL_TRIANGLES. Well probably I don't get your algo correctly...

Michael Steinberg
06-17-2001, 04:50 AM
As I see from your code snippet, you try to combine a grid of quads to a surface. I every loop iteration but the first, you are creating 4 triangles. Try using GL_QUADS in the glBegin statement.
The line where you calculate the normal, you are using the same height for all three vertices, so that should produce a vector that is 0,1,0 in your representation. This may produce the lighting error you see (every surface will be lit the same way!)

Michael Steinberg
06-17-2001, 12:04 PM
Okay... forget that with the triangle strip... I thought you'd have called glBegin outside the loop...

MrShoe
06-17-2001, 09:40 PM
Cant use GL_QUADS since the quad will not necessarily lie on the same plane, which could cause some screw ups.

06-17-2001, 10:21 PM
Hi,
The algorithm i use is:

V1.x = B.x - A.x
V1.y = B.y - A.y
V1.z = B.z - A.z
V2.x = C.x - B.x
V2.y = C.y - B.y
V2.z = C.z - B.z

N.x = (V1.y * V2.z) - (V1.z * V2.y)
N.y = (V1.z * V2.x) - (V1.x * V2.z)
N.z = (V1.x * V2.y) - (V1.y * V2.x)

scale = sqrt ((N.x * N.x) + (N.y * N.y) + (N.z * N.z))

N.x = N.x / scale
N.y = N.y / scale
N.z = N.z / scale

//B****A
//* *
//* *
//* *
//* *
//C****D

don't know if this is any help but thought i may try.

Paul

evil
06-17-2001, 10:57 PM
vec1_x = x2 - x1;
vec1_y = y2 - y1;
vec1_z = z2 - z1;
vec2_x = x3 - x1;
vec2_y = y3 - y1;
vec2_z = z3 - z1;

I do this slightly different.
Don't know if it makes a difference, but try :
vec1_x = x1 - x2;
vec1_y = y1 - y2;
vec1_z = z1 - z2;
vec2_x = x3 - x2;
vec2_y = y3 - y2
vec2_z = z3 - z2;


bigx = (vec1_y*vec2_z) - (vec1_z*vec2_y);
bigy = (vec1_z*vec2_x) - (vec1_x*vec2_z);
bigz = (vec1_x*vec2_y) - (vec1_y*vec2_x);

normal[normal_number].x = bigx/sqrt(bigx*bigx + bigy*bigy + bigz*bigz);
normal[normal_number].y = bigy/sqrt(bigx*bigx + bigy*bigy + bigz*bigz);
normal[normal_number].z = bigz/sqrt(bigx*bigx + bigy*bigy + bigz*bigz);

You don't want to calculate that sqrt every time http://www.opengl.org/discussion_boards/ubb/smile.gif. Put it in a tmp variable.


normal[normal_number].x = abs(normal[normal_number].x);
normal[normal_number].y = abs(normal[normal_number].y);
normal[normal_number].z = abs(normal[normal_number].z);

abs() returns an int. Meaning that this will never work.

ffish
06-17-2001, 11:41 PM
Same algorithm Zeus, evil and MrShoe. The only difference in the calculation of your two vectors can be the direction that the normal points in. The hard part of the algorithm is determining whether the normals point in the right direction or not.

Firstly, as Michael Steinberg says, you should _definitely not_ get the absolute value of the normals. What I would suggest for debugging purposes is to pass into your calculation function a normal that is perpendicular to the terrain. For example, if your terrain is basically in the xz plane, pass in (0, 1, 0) and so on.

Next, when you calculate the normal, calculate the dot product of the normal with the perpendicular vector. The +ve or -ve sign of the dot product will tell you whether the angle between the two vectors is < 90 degrees or > 90 degrees respectively. If the sign is -ve, the normal is in the wrong direction so change its direction by multiplying by -1. Voila!

This will only work if your terrain has no intentional overhangs (caves, etc) and if it's a terrain based on a flat plane. The main problem with calculating normals is determining whether your normals point in the right direction. We once had an assignment to draw the Utah teapot (GLUT teapot) lit and shaded and I remember my first effort only drew some of the triangles because of normals in the wrong direction. Of course, with curved objects like the teapot it's much more difficult to determine if the normals are facing in the right direction http://www.opengl.org/discussion_boards/ubb/smile.gif

Hope that helps.

lgrosshennig
06-18-2001, 03:05 AM
MrShoe! Thanks for sending me the sourcecode.

Its very easy, you passed the same height for all three vertices into the normal calculation.

Change the call to

calculate_normal((mapx*99)+mapz, mapx, map[mapx][mapz], mapz, mapx, map[mapx][mapz+1], (mapz+1), mapx+1, map[mapx+1][mapz], mapz);

and you are done!

BTW: There is no need to calculate the normals over and over again! Do it once right after the terrain generation, speeds things up by a magnitude http://www.opengl.org/discussion_boards/ubb/smile.gif

That devide by DETAIL is a bit strange to.

You are also using the same normal for both parts of a triangle strip...

Hope that helps.

LG http://www.opengl.org/discussion_boards/ubb/biggrin.gif

Michael Steinberg
06-18-2001, 03:08 AM
I don't find that to be the greatest problem. The crossproduct creates a right-handed system, so you can predict in which direction the resulting vector points. You already have to mind that when specifying the triangles, so if you have them defined correctly (counterclockwise for default), the crossproduct will always point in the correct direction.
MrShoe said that his terrain almost looked as if it wasn't lit. And this here is the reason:




calculate_normal((mapx*99)+mapz, mapx/DETAIL, height, mapz/DETAIL, mapx/DETAIL, height, (mapz+1)/DETAIL, (mapx+1)/DETAIL, height, mapz/DETAIL);


He uses the same height in every vertex as I pointed out before. So, you simply define a normal that is perpendicular to the xz-plane in your case. That is for _every_ quad you draw exactly the same. Thus the same lighting. Also the value of height is not defined until after the first loop iteration (but still, it's the same for all so it doesn't matter).
So what MrShoe needs to do is, to use the three different height values he uses later in the loop. Also, the normals must be different for the two triangles formed by the quad, since they do _not_ lie on the same plane (or at least are guaranteed to).
That is, because it is very unlikely that any four points (forming a quad for you) share a plane.

Hope that helps.

Michael Steinberg
06-18-2001, 03:10 AM
Couldn't you have told me that you were writing exactly the same at exactly the same time, LG? http://www.opengl.org/discussion_boards/ubb/smile.gif

MrShoe
06-18-2001, 04:37 PM
Ok, I think I have it! Thanks for looking at the source code! I realise that calculating the normals over and over again is very inefficient, but my code is a first stage, I just did it in like 30 minutes, not optimisations of any kind! As I said, Thanks all!