PDA

View Full Version : weird smooth shading, vertex normals?



Riskz
03-08-2009, 12:41 PM
Well, this is my first post in this forum and I don't speak English very well, so I'd like to apologize for any mistake I could make.

I'm doing my first program using OpenGL with C (it may end up as a videogame, but at the moment it's only a model loader and viewer).

The program loads a model from an .OBJ extension file, which i found the easiest for loading. The problem is that the OBJ file contains normal data, but there isn't always a normal for each vertex neighter a normal for each polygon/face. But each face contains data about which normal that face uses, so I add that normal to each of the face vertexs, and after loading all the faces, I divide the vertex normals by the quantity that have been added. So I end up with the vertex normals (or at least that is what I think).

The problem comes when I test my program. The models loads fine, but the smooth shading is not working properly (the best example is the cube), because when i rotate the scene, the shading in the cube changes a bit, on a weird way.

Here is my source code with the compiled .exe (I'm using AllegroGL too, because in the future i will use some Allegro functions with OpenGL)
I'm sorry if the source code is not documented yet, but I didn't have time for that.

I hope someone can tell me by the way the program looks if there is a problem with the vertex normals, or with the light.

Thank you very much, and sorry for the inconvenients.

http://www.megaupload.com/?d=AEDH4LR9

EDIT: to move around use the arrow keys and to rotate WASD keys.

ZbuffeR
03-08-2009, 01:01 PM
(did not look at the source)
1) there can be multiple normals for a given vertex, example, in the cube, each vertex has 3 different normals. This is not the case if you work with very smooth models, is this case your method is almost correct. It is not "average", but rather "normalize" : indeed it is very important that each normal is unit length, which is not guaranteed by a simple averaging.
sqrt(dx*dx +dy*dy +dz*dz) = 1.0

2) vertex shading is always a bit weird when tesseleation is not high enough.
Read pitfall 2 here (others can help too) :
http://www.opengl.org/resources/features/KilgardTechniques/oglpitfall/

Riskz
03-08-2009, 01:08 PM
Well, that is what i'd call a really fast answer. I'm now on my Vista session so I can't get that fixed, but I will test it on a few minutes and edit this reply. Thanks, probably that's the problem. Can you help me a bit with the "normalize" thing?
For example, if I have a vertex normal (2.3,0.3,0.6), how can i normalize it? (sorry for my ignorance, I'm only 16 years old and didn't learn normals at school yet)

Thanks again =)

ZbuffeR
03-08-2009, 02:13 PM
A normal sould be a 3D vector having its length equal to one.
So you first calculate the length of your current vector :

l = sqrt(x*x + y*y + z*z)
l = sqrt(2.3*2.3 + 0.3*0.3 + 0.6*0.6) = 2.396;

Then use it to divise each of its coordinates to build the correctly normalized normal :
nx = x/l;
ny = y/l;
nz = z/l;

There you go. You can check that sqrt(nx*nx + ny*ny + nz*nz) is indeed equal to 1.0

Riskz
03-08-2009, 02:47 PM
Thanks, I understood it correctly now. Now i normalize instead of average. I think one of the problems is solved, however, I think I still have a problem with the lighting.

http://img11.imageshack.us/img11/392/sphere.th.jpg (http://img11.imageshack.us/my.php?image=sphere.jpg)

As you can see, the lighting seems ok for the areas which are iluminated, but the dark areas look horrible, i mean, the shadows aren't smooth enough. But I also noticed that the floor is rendered all of the same colour, like if the light is at the same distance from each vertex of the floor (which is not the case). May this have something to do with the position of the light? I read something about the light having four position variables instead of three (x,y,z,w?) so this is how i inicialize that light:


...
luz0.pos[0]=15.0;
luz0.pos[1]=20.0;
luz0.pos[2]=30.0;
luz0.pos[4]=1.0;
...
glLightfv(GL_LIGHT0,GL_POSITION,luz0.pos);
...


Thanks very much, and sorry again xD

dletozeun
03-08-2009, 03:24 PM
It looks like your normals are still not correct. I advise you to display normals (draw them at each vertex as lines), the best for debugging.
About the 4th coordinate in the light position vector, it tells Opengl whether the light is directional (w=0) or omnidirectional (w=1).
The reason of this is that with homogeneous coordinates, when the 4th coordinate is 0 the 4d vector represents a 3D vector and when it is 1, a 3D position in euclidian space.

Riskz
03-08-2009, 04:19 PM
Thanks for the advice, I wrote the code to draw the normals.
This is how it looks with drawn normals:

http://img166.imageshack.us/img166/1686/spherevn.th.jpg (http://img166.imageshack.us/my.php?image=spherevn.jpg)

So now I have two questions:
1) Is thre any problem with the normals? I'm a beginner in this, but i see they are pointing to the right place, and their lenght is 1.0 i think.
EDIT: The top and bottom normals (of the sphere) are clearly wrong, the others "seem" to be fine, but now i'm doubting about that too... i will check with other models
EDIT2: I checked with a cube with 192 faces and almost all the normals are wrong, so there's obviously something wrong with my code when generating normals, I will try to get that fix, thanks anyway.

2) If my 4th cordinate for the light source is 1.0, why the shading doesn't change for all the vertex in the floor?

Thank you all guys, I'm learning a lot from you.

--------------------------------------------------------

Riskz
03-08-2009, 07:08 PM
I found the error while calcultaing the normals, now they are pointing better than before, but still not perfect, just look at this cube with 12 triangles (i used this example because maybe someone knows or can calculate the normals)
http://img211.imageshack.us/img211/2194/cubevn.th.jpg (http://img211.imageshack.us/my.php?image=cubevn.jpg)

Thanks again

dletozeun
03-09-2009, 03:06 AM
The result you get is logical looking at the normals. It is because you compute the average normal at a vertex between all triangles that use this one.
When you have this kind of shape, you need to create smoothing groups in which all triangles orientation do not differ to much with its neighborhood. I hope you see what I mean. If you are used to some 3D modelers like 3dsmax, I think you will. This kind of work (smoothing groups) is usually done by artists in modelers (and automatically, the model itself). Then you just have to export and import your model and all normals should be correct.

Y-tension
03-09-2009, 03:46 AM
For sphere: Just a guess but, this looks like your material has really low specular exponent. Try raising it a bit to see what happens. For cube: You can't use a single vertex for every face because with opengl you have a 1 vertex-1 normal relation. With smooth shading, this has the expected behavior but with models requiring flat shading, like the cube it doesn't work. Instead you must
1)either detect at load time if a vertex id corresponds to more than one normal id's and if it does create a different opengl vertex for each combination(remember, in opengl threre is a 1-1 vertex-normal relation)to use in your faces.
OR
2) use flat shading but in opengl this is a bit tricky as it requires you to supply your face normal as a vertex normal(see opengl sdk, glshademodel)or, update your normal state between each draw call which in turn increases your draw calls enormously and drops performance.

PS: your sphere and ground normals look fine to me, that's why I think it's a specular problem...

Y-tension
03-09-2009, 04:08 AM
Confirmed it by looking at the source files. Increase shininess a lot, somewhere in the 10s-20s. the range is 0 - 128 but ranges close to zero produce these artifacts.
I also see you're a blenderhead. Bear in mind that the values of materials within blender don't exactly correspond to opengl values, since opengl uses a single specular and diffuse model(shader in blender).

Riskz
03-09-2009, 06:47 PM
Just a guess but, this looks like your material has really low specular exponent. Try raising it a bit to see what happens

I'm so ashamed, that was the real problem (solved when I wrote the code for loading the materials from the files instead of using always the same, which was wrong after all). Anyway this thread made me learn lot of things.

Can you tell me a little more about that thing of materials in Blender? I'm a begginer at modelling.

Thank you all guys, now my program seems to work fine, so I can go on.

ZbuffeR
03-10-2009, 03:37 AM
It is just that for easier conversion of Blender materials to opengl, you should use the same color for diffuse and for specular inside your Blender material.

There are more advanced techniques to do separate specular color in GL, but you will see that later.

Y-tension
03-10-2009, 08:46 AM
It is just that for easier conversion of Blender materials to opengl, you should use the same color for diffuse and for specular inside your Blender material.

There are more advanced techniques to do separate specular color in GL, but you will see that later.

Well, there are some pitfalls if you use texturing(Specular must be added AFTER texturing and there is state to control this behaviour) but personally I don't believe it's that hard to specify both diffuse and specular color in OpenGL materials.

What I meant about materials is that OpenGL uses something like the lambertian diffuse and cook-Torr specular material shaders so if you use something different in blender you won't get the same result in OpenGL unless you use shaders. Also, as noted blender values are usually in range 0-1 while OpenGL exponent is in range 0-128.

ZbuffeR
03-10-2009, 09:28 AM
Blender specular exponent is called "Hard(ness)", with default value at 50, and it is very similar to GL exponent.

Riskz
03-10-2009, 12:08 PM
Well, maybe this comparison helps, it's a sphere made in blender and exported to opengl (with .*obj and .*mtl files). I know the Blender ones looks better, but the materials looks the same (but I'm not used to all this 3d stuff, maybe you can notice the differences):

http://img10.imageshack.us/img10/6069/openglblender.th.jpg (http://img10.imageshack.us/my.php?image=openglblender.jpg)

dletozeun
03-10-2009, 01:21 PM
The only difference in the two renderings above is that the sphere on right is shaded using per pixel lighting and on the left using gouraud shading, typical of OpenGL fixed pipeline (lighting is computed at each vertex and interpolated at each fragment.

Riskz
03-10-2009, 01:28 PM
Well, so the materials are loaded ok. Is there any form of using per pixel lighting in OpenGL??

dletozeun
03-10-2009, 01:30 PM
Yes you can, using shaders. See GLSL or Cg:

A nice tutorial on GLSL here:
http://www.lighthouse3d.com/opengl/glsl/

For Cg, see the nvidia developers site.

Riskz
03-10-2009, 01:33 PM
Ok, I'll try to implement that tomorrow in my loader. How much slower will my loader run if I use per pixel lighting?

dletozeun
03-10-2009, 01:35 PM
Depends on your hardware. But it is significantly slower. Use it in moderation.

Riskz
03-10-2009, 06:11 PM
I have a problem now when trying to load a model with texture and material.
I only get the material rendered but not the texture...
I will post the code which inicializes and uses the texture:



int num_texture=-1;
...
glEnable(GL_TEXTURE_2D);
...
objeto->tex_id=RS_Load_Bitmap(tex_name);
// then I load the u v coordinates for each vertex of each face
...
// And when I draw...
glBindTexture(GL_TEXTURE_2D, obj->tex_id);
...
if(obj->tex_id>-1)glTexCoord2f( obj->poligono[counter].ta.u, obj->poligono[counter].ta.v);
if(obj->tex_id>-1)glTexCoord2f( obj->poligono[counter].tb.u, obj->poligono[counter].tb.v);
if(obj->tex_id>-1)glTexCoord2f( obj->poligono[counter].tc.u, obj->poligono[counter].tc.v);
...


And this is the RS_Load_Bitmap function:

extern int RS_Load_Bitmap(char *filename)
{

unsigned char *l_texture;
int i, j=0;
FILE *l_file;
BITMAPFILEHEADER fileheader;
BITMAPINFOHEADER infoheader;
RGBTRIPLE rgb;
num_texture++;
if( (l_file = fopen(filename, "rb"))==NULL) return (-1);
fread(&fileheader, sizeof(fileheader), 1, l_file);
fseek(l_file, sizeof(fileheader), SEEK_SET);
fread(&infoheader, sizeof(infoheader), 1, l_file);
l_texture = malloc(infoheader.biWidth * infoheader.biHeight * 4);
memset(l_texture, 0, infoheader.biWidth * infoheader.biHeight * 4);
for (i=0; i < infoheader.biWidth*infoheader.biHeight; i++)
{
fread(&amp;rgb, sizeof(rgb), 1, l_file);

l_texture[j+0] = rgb.rgbtRed; // Red component
l_texture[j+1] = rgb.rgbtGreen; // Green component
l_texture[j+2] = rgb.rgbtBlue; // Blue component
l_texture[j+3] = 255; // Alpha value
j += 4; // Go to the next position
}
fclose(l_file); // Closes the file stream
glGenTextures( 1, &amp;num_texture );
glBindTexture(GL_TEXTURE_2D, num_texture);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

glTexImage2D(GL_TEXTURE_2D, 0, 4, infoheader.biWidth, infoheader.biHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, l_texture);
gluBuild2DMipmaps(GL_TEXTURE_2D, 4, infoheader.biWidth, infoheader.biHeight, GL_RGBA, GL_UNSIGNED_BYTE, l_texture);
free(l_texture);
return (num_texture);
}


Am I missing something?

Riskz
03-11-2009, 11:10 AM
Sorry for double posting, but I fixed the texture problem (I was enabling textures before setting them).

Now I have three questions which I am still wondering:
1) Why doesn't my models make shadows on other models when being in front or over them?
2) Why do all the faces with the same vertex normals looks the same? (like if they weren`t taking the position of light into account)
3) If I load a textured model, the texture is not afected by light or material, It looks like if using texture disables lighting. Why?

Thanks and sorry for this long thread about stupid questions hehe.

ZbuffeR
03-11-2009, 11:53 AM
1) casts shadows are quite advanced stuff. Two main ways to do them properly : shadow maps or stenciled shadow volumes.
2) GL_LIGHT_MODEL_LOCAL_VIEWER defaults to GL_FALSE in glLightModel(), this means it simulates infinitely far light like the sun. You want it to be GL_TRUE.
http://www.opengl.org/sdk/docs/man/
3) strange, do you glDisable lighting ?

Riskz
03-11-2009, 05:06 PM
1) casts shadows are quite advanced stuff. Two main ways to do them properly : shadow maps or stenciled shadow volumes.
2) GL_LIGHT_MODEL_LOCAL_VIEWER defaults to GL_FALSE in glLightModel(), this means it simulates infinitely far light like the sun. You want it to be GL_TRUE.
http://www.opengl.org/sdk/docs/man/
3) strange, do you glDisable lighting ?

1) Thanks, I'll search some information about it, maybe I can use that in the future
2) Thanks again, That was the problem =)
3) The lighting is enabled. For example, look at the following screenshot (both the sphere and the earth were rendered at the same time, but when I move the lighting it only affects the sphere)

http://img26.imageshack.us/img26/488/lightrlc.th.jpg (http://img26.imageshack.us/my.php?image=lightrlc.jpg)

ZbuffeR
03-11-2009, 05:25 PM
Have a read around 21.030 : http://www.opengl.org/resources/faq/technical/texture.htm

Riskz
03-11-2009, 05:33 PM
Have a read around 21.030 : http://www.opengl.org/resources/faq/technical/texture.htm


Do you wanna be my best friend?
hahaha :P
Now seriously, thanks very much, you replied so quickly, and it worked!

I'm posting in this thread like twice a day, I'm happy to see that some people work hard to help others, this way learning to use OpenGl is easier

Thanks again

ZbuffeR
03-11-2009, 05:44 PM
lol no problem

Y-tension
03-12-2009, 08:26 AM
it won't be any slower unless you embed the shader source in the file.As noted, display performance may be slightly decreased since hardware must calculate the lighting equation for every pixel instead of every vertex, but the result is worth it! Make sure you have OpenGL 2.0 capable hardware before trying. In any case you must write the shaders for the material yourself. I know that blender already supports real-time materials in the 3D view through shaders so if you dig into the blender source you may even find a shader that looks exactly like the blender rendering. But, after all why not write your own shader. It won't take long if you know, and even if you don't know you should consider learning.

Nice work by the way! I've worked on some custom made binary format and it's not easy to import every blender attribute in OpengGL. For shadows you will need some extra work and some reading. As stated, shadow maps and shadow volumes are the alternatives but, hey! Look into nvidia and ATI developer sites to find tons of info!

Riskz
03-12-2009, 09:44 AM
I don't mind if it's too easy or too hard, I would like to learn. Honestly, I love programming. In fact, all I know is what I learned from internet and people like you. I'm 16 now but next year I'm entering to a university in order to study computer engineering.

This loader may end up as a game, which I would like to show in a game developers exposition this year in my country (as a house made game, nothing too serius)

Now I will clean some of my code. Then I'll try to start with the shaders, and once I'm done with that, maybe start with the game (I think I'll need a physics library or something like that to do physics calculations, wont I?

Thanks for all, I think I'll be posting my problems soon :P

EDIT: Before starting with the shader stuff, how can i calculate and draw reflections in opengl? (for example, a metal-like ball reflecting light and objects)

ZbuffeR
03-12-2009, 10:44 AM
Reflections (and a bunch of other things) are much easier to do with shaders, so start GLSL as soon as you can.

This series of tutorials is a very good start :
http://www.lighthouse3d.com/opengl/glsl/

Then if can have access to "The Orange Book" second edition, you are set. The book website have code and tools, but no tutorials :
http://www.3dshaders.com/home/

Riskz
03-12-2009, 04:52 PM
I've been testing my program with lot of models today, and I found out that when I move an object, the lighting doesn't change. I mean, the object is lighted as if it were in the original position.
This is what I do to move the object:

1) call glPushMatrix();
2) translate
3) rotate
4) draw
5) call glPopMatrix();

ZbuffeR
03-13-2009, 01:08 AM
And where do you define your light ?
Read this page, especially 18.050 :
http://www.opengl.org/resources/faq/technical/lights.htm

dletozeun
03-13-2009, 07:23 AM
If you define a directional light this is normal. Directionnal light is set if you put 0 as the 4th coordinate in the light position. Put 1 for an omnidirectional light.

Riskz
03-13-2009, 09:38 AM
And where do you define your light ?
Read this page, especially 18.050 :
http://www.opengl.org/resources/faq/technical/lights.htm
I'm fixing the light position after calling glLoadIdentity(), so that the light sn`t moved or rotated with the objects. In fact, when I rotate an object, the light stay still (works well), but the problem appears when I move an object, so I think this is not the problem.


If you define a directional light this is normal. Directionnal light is set if you put 0 as the 4th coordinate in the light position. Put 1 for an omnidirectional light.

I'm using an array of 4 floats variables which I pass to glLightfv every frame. The 4th float of the array is 1.0, so I think it's an omnidirectional light :S

Well, if any of you have a little time here is my source code if you wanna check it
- in the "func.c" file is the light fixing function RS_Light_Fix, and also the drawing function RS_Render_Object, which is still slow because I'm not using VBO yet
- in the "init.c" file are the inicializations of light variables (function RS_GL_Init)

Thanks to both

http://www.megaupload.com/?d=8J10AHT0

dletozeun
03-14-2009, 06:27 AM
I have looked into your code very quicky and seen that you set a camera transformation. Since your light is an object like any any of your meshes drawn in the scene you have to transform it at least by the camera matrix.

So you have to set light position after computing camera transformation to transform the light properly.
If it is not the problem, I will check out your code more longer when I would have time.

Riskz
03-14-2009, 10:09 AM
That's not the problem :S. I've already checked that.

Thanks anyway

dletozeun
03-14-2009, 01:44 PM
Ah, that's a shame, I have no more ideas right now, it must be something in your code that might be not related to opengl. I can't find it your program is quite complicated, it is hard to find out. You should try to simplify it to isolate the source of your issue.
Anyway, I maintain what I said, you have to transform the light by the camera transformation matrix. This is normal since the camera movement is like a transformation that affects the whole scene including the lights.

Riskz
03-14-2009, 03:54 PM
I really understand you, my code IS a mess, I haven't documented anything yet. Maybe simplifying it is a good idea, I will try to do that. I don't know why but I feel like if my light is still directional and not omnidirectional (even if I'm doing the things the right way to make it be omnidirectional)

thanks for all

Riskz
03-17-2009, 06:32 PM
I started school so I've been busy, but I could made some time to check my code today, and I found the error. Like I suposed, it was the light. I was not inicializing it the right way (a really stupid error not related to OpenGL, I'm so ashamed that I can't even tell it).

Now I have one question: Which is the best way to control the speed of a game/program using OpenGL in order to make it run at the same speed no matter at how much FPS it runs? (I think most of you will get what I mean)
Thanks

Riskz
03-19-2009, 11:52 AM
Ok, I've already find out how to control the game speed.

I started with shaders, I've been able to use them in my game, but since I'm new to this 3d stuff, I'm not able to write my own shaders.
I'd like to have a reflection shader to use with my sphere and a shadow shader in order to have the sphere shadows on the floor.
Can anyone help me with this?

Thanks again