1
votes

I'm working for the first time on a 3D project (actually, I'm programming a Bullet Physics integration in a Quartz Composer plug-in), and as I try to optimize my rendering method, I began to use glDrawElements instead of the direct access to vertices by glVertex3d...

I'm very surprised by the result. I didn't check if it is actually quicker, but I tried on this very simple scene below. And, from my point of view, the rendering is really better in immediate mode.

The "draw elements" method keep showing the edges of the triangles and a very ugly shadow on the cube.

I would really appreciate some information on this difference, and may be a way to keep quality with glDrawElements. I'm aware that it could really be a mistake of mines...

Immediate mode enter image description here

DrawElements enter image description here

The vertices, indices and normals are computed the same way in the two method. Here are the 2 codes.

Immediate mode

glBegin (GL_TRIANGLES);
    int si=36;
    for (int i=0;i<si;i+=3)
    {
        const btVector3& v1 = verticesArray[indicesArray[i]];;
        const btVector3& v2 = verticesArray[indicesArray[i+1]];
        const btVector3& v3 = verticesArray[indicesArray[i+2]];


        btVector3 normal = (v1-v3).cross(v1-v2);
        normal.normalize ();
        glNormal3f(-normal.getX(),-normal.getY(),-normal.getZ());
        glVertex3f (v1.x(), v1.y(), v1.z());
        glVertex3f (v2.x(), v2.y(), v2.z());
        glVertex3f (v3.x(), v3.y(), v3.z());

    }
    glEnd();

glDrawElements

glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer(GL_FLOAT, sizeof(btVector3), &(normalsArray[0].getX()));
    glVertexPointer(3, GL_FLOAT, sizeof(btVector3), &(verticesArray[0].getX()));
    glDrawElements(GL_TRIANGLES, indicesCount, GL_UNSIGNED_BYTE, indicesArray);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

Thank you.

EDIT

Here is the code for the vertices / indices / normals

GLubyte indicesArray[] = {
        0,1,2,
        3,2,1,
        4,0,6,
        6,0,2,
        5,1,4,
        4,1,0,
        7,3,1,
        7,1,5,
        5,4,7,
        7,4,6,
        7,2,3,
        7,6,2 };

    btVector3 verticesArray[] = {
        btVector3(halfExtent[0], halfExtent[1], halfExtent[2]),
        btVector3(-halfExtent[0], halfExtent[1], halfExtent[2]),
        btVector3(halfExtent[0], -halfExtent[1], halfExtent[2]),
        btVector3(-halfExtent[0], -halfExtent[1], halfExtent[2]),
        btVector3(halfExtent[0], halfExtent[1], -halfExtent[2]),
        btVector3(-halfExtent[0], halfExtent[1], -halfExtent[2]),
        btVector3(halfExtent[0], -halfExtent[1], -halfExtent[2]),
        btVector3(-halfExtent[0], -halfExtent[1], -halfExtent[2])
    };

    indicesCount = sizeof(indicesArray);
    verticesCount = sizeof(verticesArray);


    btVector3 normalsArray[verticesCount];

    int j = 0;
    for (int i = 0; i < verticesCount * 3; i += 3)
    {
        const btVector3& v1 = verticesArray[indicesArray[i]];;
        const btVector3& v2 = verticesArray[indicesArray[i+1]];
        const btVector3& v3 = verticesArray[indicesArray[i+2]];

        btVector3 normal = (v1-v3).cross(v1-v2);
        normal.normalize ();

        normalsArray[j] = btVector3(-normal.getX(), -normal.getY(), -normal.getZ());

        j++;
    }
2
What you call "Direct Access" is actually the slow track and detached from the GPU. To get the maximum performance you want to place the vertex data in a vertex array located in GPU's own memory. (Vertex Buffer Object). Also immediate mode has been removed from modern versions of OpenGL.datenwolf
Visual quality has nothing to do with immediate / programmable pipelines.Both end up on GPUMichael IV
@datenwolf I learned a lot with this forum thread, as an OpenGL newbie. Now, I cache the vertex data in C arrays and call them with glDrawElements. I'll try to integrate VBOs in my program but for the moment it seems quite complicated.Benoît Lahoz

2 Answers

2
votes

You can (and will) achieve the exact same results with immediate mode and vertex array based rendering. Your images suggest that you got your normals wrong. As you did not include the code with which you create your arrays, I can only guess what might be wrong. One thing I could imagine: you are using one normal per triangle, so in the normal array, you have to repeat that normal for each vertex.

You should be aware that a vertex in the GL is not just the position (which you specify via glVertex in immediate mode), but the set of all attributes like position, normals, texcoords and so on. So if you have a mesh where an end point is part of different triangles, this is only one vertex if all attributes are shared, not just the position. In your case, the normals are per triangle, so you will need different vertices (sharing position with some other vertices, but using a different normal) per triangle.

2
votes

I began to use glDrawElements

Good!

instead of the direct access to vertices by glVertex3d...

There's nothing "direct" about immediate mode. In fact it's as far away from the GPU as you can get (on modern GPU architectures).

I'm very surprised by the result. I didn't check if it is actually quicker, but I tried on this very simple scene below. And, from my point of view, the rendering is really better with the direct access method.

Actually its several orders of magnitudes slower. Each and every glVertex call causes the overhead of a context switch. Also a GPU needs larger batches of data to work efficiently, so glVertex calls first fill a buffer created ad-hoc.

Your immediate code segment must be actually understand as following

    glNormal3f(-normal.getX(),-normal.getY(),-normal.getZ());
    glVertex3f (v1.x(), v1.y(), v1.z());

    // implicit copy of the glNormal supplied above
    glVertex3f (v2.x(), v2.y(), v2.z());

    // implicit copy of the glNormal supplied above
    glVertex3f (v3.x(), v3.y(), v3.z());

The reason for that is, that a vertex is not just a position, but the whole combination of its attributes. And when working with vertex arrays you must supply the full attribute vector to form a valid vertex.