glDrawElements和立即模式之间的OpenGL质量差异

时间:2013-09-07 18:52:34

标签: c++ opengl graphics 3d

我第一次在3D项目上工作(实际上,我正在使用Quartz Composer插件编写Bullet Physics集成),当我尝试优化渲染方法时,我开始使用glDrawElements而不是glVertex3d直接访问顶点......

我对结果感到非常惊讶。我没有检查它是否真的更快,但我尝试了下面这个非常简单的场景。而且,从我的角度来看,渲染在立即模式下真的更好。

“绘制元素”方法会持续显示三角形的边缘以及立方体上非常难看的阴影。

我非常感谢有关这种差异的一些信息,并且可能是一种通过glDrawElements保持质量的方法。我知道这真的是地雷的错误......

立即模式 enter image description here

DrawElements enter image description here

顶点,索引和法线在两种方法中的计算方法相同。这是2个代码。

立即模式

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);

谢谢。

修改

这是顶点/索引/法线的代码

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 个答案:

答案 0 :(得分:2)

您可以(并且将会)使用立即模式和基于顶点数组的渲染实现完全相同的结果。你的照片表明你的法线错了。由于您没有包含用于创建阵列的代码,因此我只能猜出可能出现的问题。我可以想象的一件事:你在每个三角形使用一个法线,所以在普通数组中,你必须为每个顶点重复该法线。

你应该知道GL中的一个顶点不仅仅是你在立即模式下通过glVertex指定的位置),而是一组所有属性,如位置,法线,texcoords等。因此,如果你有一个网格,其中一个终点是不同三角形的一部分,如果共享所有属性,这只是一个顶点,而不仅仅是位置。在您的情况下,法线是每个三角形,因此每个三角形需要不同的顶点(与其他顶点共享位置,但使用不同的法线)。

答案 1 :(得分:2)

  

我开始使用glDrawElements

好!

  

而不是glVertex3d直接访问顶点...

关于立即模式没有任何“直接”。实际上,它与GPU相差甚远(在现代GPU架构上)。

  

我对结果感到非常惊讶。我没有检查它是否真的更快,但我尝试了下面这个非常简单的场景。而且,从我的观点来看,使用直接访问方法渲染真的更好。

实际上它的几个数量级更慢。每个glVertex调用都会导致上下文切换的开销。 GPU也需要更大批量的数据才能高效工作,因此glVertex调用首先填充临时创建的缓冲区。

您的直接代码段必须实际理解如下

    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());

原因是,顶点不仅仅是一个位置,而是它的属性的整个组合。在处理顶点数组时,必须提供完整的属性向量以形成有效的顶点。

相关问题