glNormalPointer and flat shading

Started by
9 comments, last by Manaluusua 15 years, 9 months ago
Hi, I'm doing a simple modelviewer for .3ds files and I'm trying to make flat and smooth shading for the objects. The problem is that while smooth shading looks pretty much right, flat shading is terrible. So, Im using rendering arrays (or what was their names...) to do the actual drawing. I don't have any materials or colors yet, just vertexpointers and normalpointers are enabled. So: a) is it even possible to use flat shading when using these rendering lists since (as far as I know) glNormalPointer takes vertexnormals as an argument? b) take a look at my program. Is the smooth shading working as it should be (it doesn't look very smooth to me)? Modelviewer: http://bugi.oulu.fi/~mana/ModelViewer.exe example models: http://bugi.oulu.fi/~mana/3ds/apple.3ds and http://bugi.oulu.fi/~mana/3ds/cube.3ds -Mana
Advertisement
I cannot run your exe because I've no win at hand. So I cannot conclude from a visual impression of what happens. Could you put some screen shots aside? Some relevant code snippets may also be helpful. In the meanwhile some thoughts:

The usage of glNormalPointer hints at working with either vertex arrays (VAs) or vertex buffer objects (VBOs). In the latter case you must have some glGenBuffer and glBindBuffer routine calls in your code.

In my experience with this topic, you have to specify normals for both flat and smooth shading (but perhaps I'm wrong here). A vertex is a composite of its position, normal, tex-coords, ..., and hence if just a single bit differs, you have to make a second vertex. I.e. a vertex with 2 adjacent triangles in flat mode must be split into 2 vertices: although their positions are the same, their normals are not.

Perhaps the question arises what happens if flat mode is chosen but not all normals of a particular triangle are the same. The OpenGL implementation I'm using here simply ignores all normals with the exception of the normal of the 3rd (i.e. the last) vertex. Other drivers may handle this situation another way, I don't know.

Computing the normal for flat faces lacks the mean value calculation of the smooth normal variant. Presumbly you know this.

Specifying wrong start addresses and/or strides in the various glXyzPointer routines may cause, err, abstract artifacts.
Flat shading should work with normal pointers.
AFAIK the normal of the polygon's first vertex is used.

Could you post an image for answering b)?
If I was helpful, feel free to rate me up ;)If I wasn't and you feel to rate me down, please let me know why!
Ok, here goes:

Flat:


Smooth:


Flat:


Smooth:




Take a look at the flat Cube. Shouldn't the two faces shown be of the same brightness (normals should be the same, aye?).

-Mana
Looks like your normals aren't correct, e.g. for the cube you should have the same normal for each vertex of a face.
If I was helpful, feel free to rate me up ;)If I wasn't and you feel to rate me down, please let me know why!
Yep, for me it looks like you're using the same vertices for flat and smooth shading. Do you see that the lower right triangle of the flat cube's face is as dark as the lower right corner of the smooth cube? And also that the upper left triangle is shaded like the upper right (or lower left) corner of the smooth cube? That indicates that you're using normals that are computed for smoothing although you're rendering in flat mode.

If I'm right, then one thing happening is that you provide 3 different normals for a float triangle, and OpenGL picks one of them for rendering (IMHO the 3rd one; e.g. I assume the lower right corner is the last vertex for the lower right triangle). You ignore the difference in computing normals for flat and smooth faces, and you ignore to split vertices due to their different normals. (See also the 3 top points of explanations in my previous answer above.)
Thanks for your help guys, I appreciate it.

I do have another normalsArray that contains a normal for each face, but when I used it, the results were even more bizarre, propably because the glNormalPointer wants a normal for each vertex and the normals array for faces that I used only had a normal per face.

So if I got this right, I can get the flat shading to work if I make each of the vertex normals equal to the facenormals? For instance, if I have vertexes a,b,c and they belong to face d and the face d has a normal vector D. Now I just define normals for vertexes a,b,c equal to D?


What about that smoothshading, does it look right to you? ( I have computed the smoothing normals so that each vertex normal is an average of all the facenormals of those faces that are intersecting it).

[Edited by - Manaluusua on July 23, 2008 4:57:33 PM]
yes for a cube even though theres only 8 vertices
u will need to create 24 vertices, ie since each corner can have 3 possible normals
I see.

I'll propably use glbegin-glend to render the flat shading then, might be easier that way.

-Mana
Quote:Original post by Manaluusua
What about that smoothshading, does it look right to you? ( I have computed the smoothing normals so that each vertex normal is an average of all the facenormals of those faces that are intersecting it).

No it doesn't look entirely correct. Look at your cube, two diagonal vertices should not be lighter than the others if you only use 1 light.

The problem probably lies with your normal calculation. Keep in mind that you should not just add all the face normals and then normalize the result but take some weight into account.

Your cube's normals (assuming smooth shading) should all have the same absolute coordinates (differing only in the sign).

A short explanation why weighting is needed:

Consider a vertex that is shared by 4 triangles: one front of the front side, one of the left side and two of the top (since it lies on the split diagonal here). If you just add the face normals you'd get (-1,2,1) instead of (-1,1,1) before normalization. This is due to the fact that for the top triangles you add (0,1,0) twice.

There are some methods for calculating the weights, two of which use the angles at the vertex or the triangle sizes. I personally use the angles, i.e. I first count the angles of all triangles (for the aforementioned example 90° for front and left and 2x 45° for the top triangles = 270° in total.). Then I multiply each normal with angle/totalAngle before adding if (so for the top triangles I'd do normal += faceNormal * 45/270 etc. The resulting normal would then be: (-1/3, 1/3, 1/3) which would result in the same normalized vector as normalize(1,1,1).
If I was helpful, feel free to rate me up ;)If I wasn't and you feel to rate me down, please let me know why!

This topic is closed to new replies.

Advertisement