什么是在游戏中对3D对象进行排序以获得速度并避免冗余打开gl 2调用的最佳方法

时间:2013-04-26 00:01:27

标签: iphone ios 3d opengl-es-2.0

编辑 - 为了帮助澄清问题,我想我正在寻找哪种排序会更好:按程序排序还是按纹理排序?这有关系吗?我的所有对象都在相似的z空间中,并且都存储在同一个VBO中。如果我不通过glUseProgram切换着色器,我是否必须为每个对象重新设置属性?

原帖:

这是一个由两部分组成的问题。我试图弄清楚在绘制它们之前如何最好地对我的3d对象进行排序,以及每个glDrawElements必须完成哪些open gl调用以及每次刷新屏幕时可以完成哪些操作(甚至只需一次)。目的当然是速度。对于我的游戏,让我们假设z从前到后都不是一个大问题(大多数对象都在同一个z)。所以除了最后做透明度的所有对象之外,我不会为z排序。

当然,我不希望排序过程花费的时间比渲染未分类要长。

第2部分是每个glDrawElements必须使用哪些open gl调用,哪些只能在信息发生变化时才能使用?而且,presentRenderbuffer会清除某些内容,因此您必须重新调用它们。

大多数opengl 2演示都会对每个对象进行每次调用。实际上大多数演示只画了一个对象。所以在3d引擎中(比如我写作)我想避免不必要的冗余调用。

这是我正在做的命令(未分类,未经优化):

glUseProgram(glPrograms[useProgram]);
glDisable(GL_BLEND);
glEnable(GL_CULL_FACE);

Loop through objects {

   Do all matrix calcs

   Set Uniforms (matrix, camera pos, light pos, light colors, material properties)

   Activate Textures.. (x2)
   glActiveTexture(GL_TEXTURE0);
   glBindTexture(GL_TEXTURE_2D, texture0);
   glUniform1i(glUniforms[useProgram][U_textureSampler], 0);

   Bind VBOs
   glBindBuffer(GL_ARRAY_BUFFER, modelVertVBO);
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelIndVBO);

   Set Attributes (vertexpos, texcoord, norm, tan, bitan)

   glDrawElements(GL_TRIANGLES, models[modelToUse].indSize, GL_UNSIGNED_INT, (void *) (models[modelToUse].indOffset * sizeof(GLuint)));
}

当然,只有当所有对象使用相同的着色器/程序时才有效。在实践中,他们赢了。

3D对象是一个数组,其中包含每个对象的所有属性:模型ID,着色器ID,纹理ID,位置等。所以我的想法是进行快速简单的排序以堆叠类似的对象'其他数组中的索引号。然后在每个阵列中绘制项目。我可以按3D模型(对象类型),纹理或着色器排序。许多模型共享相同的纹理。许多模型共享相同的着色器。此时我有3个着色器。所有对象共享一个VBO。

我可以这样做吗?

Bind the VBO - since all objects use the same one
Loop through object types {
   If shader hasn't changed
      glUseProgram
      Set Attributes
   If texture hasn't changed
      glActiveTexture(s) - based on which program is active
   Loop through objects of that type {
      Do matrix calcs
      Set Uniforms - based on which program is active
      glDrawElements
   }
}

编辑 - 要清楚 - 我还在绘制所有对象,只是以不同的顺序组合着色器和/或纹理的使用,以避免绑定,然后在一帧内再次重新绑定&# 39;游戏。

我目前在第二次刷新时遇到glDrawElements崩溃,但我认为这很容易找到。我只包含这个事实,因为它让我认为绑定纹理可能不会延续到第二帧(或presentBuffers)。

避免更改着色器或更改纹理会更快吗?在多个glDrawElement调用中,属性,vbo和纹理是否保持活动状态?跨多个presentBuffers?

1 个答案:

答案 0 :(得分:0)

回答我自己的问题。

首先是一些背景。我目前有3个着色器,并期望我最终不会超过4或5.示例我有一个使用基本和普通纹理的凹凸贴图着色器。我还有一个不使用基础纹理的着色器,而是使用纯色作为对象,但仍然具有普通纹理。然后我有相反的,一个仅使用基本纹理的平面照明简单着色器。

我有许多不同的3D模型,但都使用相同的VBO。有些3D模型使用与其他模型相同的纹理。

所以在3d对象的定义中,我添加了一个renderSort属性,我可以预设它知道它使用什么着色器程序以及它需要什么样的纹理。

然后当我更新对象并确定它们是否需要在屏幕上绘制时,我还根据它们的3d对象类型的renderSort属性对它们进行一次简单排序...我只是抛出数组的索引'bucket'数组中的对象。我看不到有超过10个桶。

更新后快速排序我渲染。

渲染遍历存储桶,并通过每个存储桶中的对象进行迭代。在内部循环内部,我检查程序是否自上一个对象以来已经改变,并且如果它被改变则执行glUseProgram。与纹理相同..如果它们当前没有绑定,我只绑定它们。然后更新所有其他制服并执行glDrawElements。

以前的方式..未排序..如果有1000个对象,它会调用glUseProgram,绑定纹理,绑定vbo,设置所有属性.. 1000次。

现在..它只在需要时改变这些东西..如果它需要1000次,它仍然会做1000次。但是对于桶的排序,它应该只需要每桶一次。这样,即使它们没有正确分类,我也会优先正确绘图。

以下是代码:

排序...

if (drawThisOne) {
        // if an object needs to be drawn - toss it in a sort bucket.
        // each itemType has a predetermined bucket number so that objects will be grouped into rough program and texture groups
        int itemTypeID = allObjects[objectIndex].itemType;
        int bucket = itemTypes[itemTypeID].renderSort;

        sorted3dObjects[bucket][sorted3Counts[bucket]]=objectIndex;
        // increment the count for that bucket
        sorted3Counts[bucket]++;
}

渲染...

// only do these once per cycle as all objects are in the same VBO
glBindBuffer(GL_ARRAY_BUFFER, modelVertVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelIndVBO);

for (int bucket=0; bucket<10; bucket++) {
    // does this bucket have anything in it?
    if (sorted3Counts[bucket]>0) {
        // if so itterate though items in that bucket
        for (int thisObject=0; thisObject < sorted3Counts[bucket]; thisObject++) {
            // get the object index for this object in this bucket
            int objectIndex = sorted3dObjects[bucket][thisObject];
            int itemTypeID = candyPieces[objectIndex].pieceType;

            int modelToUse = itemTypes[itemTypeID].model;

            // switching to psudocode...

            GLuint useProgram = itemTypes[itemTypeID].shader;
            if (Program Changed or is not set) {
                glUseProgram(glPrograms[useProgram]);
                glDisable(GL_BLEND);
                glEnable(GL_CULL_FACE);
                currentProgram=useProgram;

                USE glVertexAttribPointer to set all attributes
            }

            // based on which program is active set textures and program specific uniforms
            switch (useProgram) { ....
                if (Texture Changed or is not set) {
                     glActiveTexture(s)
                }
            }

            Matrix Calculations
            glUniform - to set unforms

            glDrawElements(GL_TRIANGLES, models[modelToUse].indSize, GL_UNSIGNED_INT, (void *) (models[modelToUse].indOffset * sizeof(GLuint)));
          }}}