Vertex Arrays and normals

In trying to improve performance, I changed my glBegin(GL_TRIANGLES) which worked fine to an indexed vertex array. I created the indices for the vertices and put the normals in their own array; enabled client state for vertex arrays and normals, and used glDrawElements. Prob is normals aren’t working correctly? I thought it would be one normal for each three vertices (one normal per triangle) but that didn’t work. OpenGl superbible example has one normal per vertex so I did that but same result. By not working I mean my rectangular box which should only have four different normals (x,-x, z, -z) looks round - as if the normals are “blending”. Hopefully some of you have encountered, and solved, this problem.
Note that it worked fine originally so the problem is definitely linked to use of vertex array.
Thanks in advance

You must have a normal per vertex. However, this is where things get tricky, what normal you assign to a vertex has a very big influence on how the triangle it is a part of will be lit. To make object look mor rounded, one would assign to the vertex the average of the triangle normals that the vertex is a part of. You do not want to do this for a box that you want to remain looking like a box. In this case you simply want to assign to each vertex the normal of the triangle it is a part of. Meaning you need to duplicate vertices, each time with a different normal, the normal of the face, for each face the vertex is a part of. Uh, that sentence could probably be worded better.
If you have one normal per vertex and the normals are the same as the normals of the faces the corresponding vertexes are a part of, everything should look quite angular.

[This message has been edited by DFrey (edited 06-28-2000).]

Thanks you are right. Now I have a fairly complex model (an animated ferris wheel) with a lot of parts. I wasn’t using vertex arrays at all but had lots if vertices in arrays (using display lists). I realized that I had better reduce the number of vertices to hopefully improve performance and save memory. So I very labouriously (over 2000 lines of geometry code alone) reduced the number of duplicate vertices and basically cut the number in half. And this also simplified the geometry constuction. So if I have to put em all back,i.e., have tons of duplicate vertices, will I still get a performance increase by using glDrawElements? I’m thinking of how much tedium is involved here (a lot). But thanks I appreciate your response.

This depends. Are there any transparent surfaces? Or are they all opaque?
If they are all opaque then you can still use vertex arrays without much of a speed hit. The idea is to break the scene up into multiple ‘super-fragments’. Each ‘super-fragment’ containing one triangle per vertex and each super-fragment should have its own normal array. Here is a rough outline:

I. Prepare vertex array with all vertices (without duplicates)
I a. If compiled vertex arrays are supported then lock the vertex array.
II. Prepare the indicies array for glDrawElements by taking only one triangle per vertex (remember to mark which triangles were taken, as well as vertices since for a given super-fragment you don’t want to pick two triangles that share a vertex, I’d probably use a running stamp mark (int) to mark the vertices and a boolean to mark the triangles. It’d be cool if the triangles were in a linked list cause then you wouldn’t have to mark them, instead just remove them from the linked list, speeding up the calculation of the next super-fragment.
III. For each triangle chosen, copy its normal into the normal array in the position of its vertices position. For example, assume v[] is the vertex array and n[] is the normal array. Then if a given triangle use vertices v[a], v[b], and v[c], then copy the triangle’s normal into n[a], n[b], and n[c] (if you want an angular appearance).
IV. Also setup the texture and color arrays if you need them.
V. Then draw that super-fragment using glDrawElements and the arrays you’ve just created.
VI. If not all triangles have been drawn then get the next super-fragment by repeating, starting at II.
VI a. Unlock vertex array if locked.
VII. With no triangles left to draw, you can swap the buffers (if you are double buffering and rendering to the back buffer).

[This message has been edited by DFrey (edited 06-29-2000).]

Hi Glenn,
the reason why it worked in the non-vertex array mode, is that OpenGL works as a state machine. In your case the normal given once was applied to all following vetices. Vertex arrays instead are meant to be of the same number of elements in all arrays. Unfortunately the same is true for indexed arrays.
Depending on the lighting model you use the calculations wouldn’t be less with fewer normals (e.g. local light or local viewer). Agreed, data transfer would be less.

The thing which could really speed up your program would be the usage of other primitives like quads or strips, which greatly reduce the number of vertices (and normals) to be transformed.

Thank you gentlemen those are extremely useful insights and adds to my knowledge greatly. The thing that is different about my model is that it is in fact a 3 dimensional model that you can move around and inspect from any angle. A ferris wheel is actually two wheels with an axle at the hub, spokes for each and cross-members connecting the wheels at the apex. Plus bipod legs to hold it up. Plus gondolas that are open “cars”. So basically it is constructed by joining together a whole whack of seperate parts, mostly long thin rectangular ones (each which only has 8 discrete vertices). My approach has been to construct each part (or pair of parts) once, and then translate/rotate it into place the required number of times (12). So, 2 * 12 spokes, 2* 12 rims, 12 gondolas, 4* 1 leg, one axle (I used a quad), 2 axle end-pieces, plus the stand and miscellaneous other parts. All this was done in display lists using glVertexfv calls. The major problem was to get the 12 gondolas to counter-rotate against the rotation of the wheels so that they always remained verticle and the occupants didn’t fall out! The only way to do that was to call the display for the gondolas 12 times and rotate/translate in the actual rendering thread (program is multi-threaded). This is where I took the biggest performance hit. Hence my search for optimizing the display lists. What I intend to do now is pre-calculate the normals outside of the display lists before rendering even starts - I’m thinking that that might help. Currently when the entire model is in view I get 23fps - speeds up when I move in closer to it. All surfaces are opaque, using flat shading, no texture mapping yet. If you are interested in seeing the model, I can send you jpeg screen shots. Thanks again I really appreciate your feedback.

Why do you have to rotate the gondolas? I think you just have to translate them to the correct position. All you need to know is the position of the axle on which the specific gondola is hanging.
( ?:expressionless: )

Marc

[This message has been edited by Marc (edited 12-20-2000).]

How do you do compiled vertexes? And how does one lock them? This sounds very interesting. Also, if you have a model that’s just a long list of vertices, that you got from, say, a 3DS model, and you have an array of faces which are nothing more than indices to the vertices that comprise them, how would you go about converting that sort of data from a GL_TRIANGLES to GL_TRIANGLE_STRIP?

CVA’s are an OpenGL extension that you can use, http://oss.sgi.com/projects/ogl-sample/registry/EXT/compiled_vertex_array.txt. The best document that I have seen on how to use them is the one John Carmack wrote about optimizing OpenGL drivers for Quake3, http://www.quake3arena.com/news/glopt.html.

To convert you triangles into triangle strips check out NVTriStrip, it is on NVIDIA’s developer page in the Programming Resources section.