Android OpenGL 2.0 精灵优化



Hej,

我正在OpenGL ES 2.0中为Android创建简单的游戏。游戏将包含几种不同类型的不同精灵,但这些精灵将不止一次出现。

现在假设我有 1 个对象(精灵)。到目前为止,我已经实现了 VBO 和索引缓冲,因此据我所知,整个对象都存储在 GPU 上。

我现在想做的是多次绘制这个对象,只是它的位置不同。目前,这是按如下方式实现的:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.getBufferId());
for(int i=0; i<1000; i++){
    multiplyMM(MVP, 0, viewMatrix, 0, tempGetRandomMVPMatrix(), 0);
    glUniformMatrix4fv(uMatrixLocation, 1, false, MVP, 0);//TODO

    if(androidVersion > Build.VERSION_CODES.FROYO)
        glDrawElements(GL_TRIANGLES, indexArray.length, GL_UNSIGNED_SHORT, 0);
    else{
        if(repairedGL20 == null){
            repairedGL20 = new AndroidGL20();
        }
        repairedGL20.glDrawElements(GL_TRIANGLES, indexArray.length, GL_UNSIGNED_SHORT, 0);
    }
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

如果我理解正确,主要问题是调用 glDrawElements,每次我更改 MVP 矩阵时都会调用它。有没有办法将所有 MVP 矩阵发送到 GPU 并在那里多次绘制一个元素,只需调用 1 次 glDrawElements?

有关对象的更多信息。它有大约 24 个顶点和纹理 64x64。目前对于 1k 对象,我有 35FPS,我想获得更高的 fps,因为我将绘制更多的精灵。

这是我的着色器:顶点:

    uniform mat4 u_Matrix;
attribute vec4 a_Position;
attribute vec2 a_TextureCoordinates;
varying vec2 v_TextureCoordinates;
void main(){
    v_TextureCoordinates = a_TextureCoordinates;
    gl_Position = u_Matrix * a_Position;
}

片段:

precision mediump float;
uniform sampler2D u_TextureUnit;//actual texture data
varying vec2 v_TextureCoordinates;
void main(){
    gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);
}

关于纹理,我还有一件事不太了解。如果我创建这样的纹理:

glBindTexture(GL_TEXTURE_2D, textureObjectIds[0]);//binds texture to texture object
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//minimization filter
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);//send texture data to OpenGL to the CURRENTLY BOUND object

当我绘制具有此纹理的对象时,纹理保存在 CPU 内存或 GPU 上的什么位置?此外,在上面的示例中,我正在绘制相同的精灵,纹理是否在每次绘制调用时发送到 GPU?如果是这样,有没有办法优化它(类似于 VBO 的东西)?

例如,假设我有 1 个具有 2 个顶点的对象,我想使用 MVP 将其转换为 2 个位置。如何使"gl_Position"同时在两个不同的地方绘制?

这是永远做不到的。我在最初的评论中指的是一个顶点缓冲区,每个精灵充满了 N-多个(可能是 4 个)顶点......如果除了顶点缓冲区中的位置之外,还包括一个额外的字段,则可以使用着色器中的不同矩阵来转换顶点。您始终必须为每个对象定义 N 个顶点,但您可以使用静态顶点缓冲区来执行此操作,我将在下面解释。

假设你有一个由点定义的精灵(在转换之前):

   Position
   <0.0,0.0>
   <1.0,0.0>
   <1.0,1.0>
   <0.0,1.0>

顺便说一下,如果您的模型视图矩阵包含缩放信息,则可以一遍又一遍地使用相同的点集。

现在,如果您想将其扩展为在一次调用中绘制 3 个精灵,您可以这样做:

  Position  Idx
  <0.0,0.0> [0]
  <1.0,0.0> [0]
  <1.0,1.0> [0]
  <0.0,1.0> [0]
  <0.0,0.0> [1]
  <1.0,0.0> [1]
  <1.0,1.0> [1]
  <0.0,1.0> [1]
  <0.0,0.0> [2]
  <1.0,0.0> [2]
  <1.0,1.0> [2]
  <0.0,1.0> [2]

基本上,我在顶点缓冲区中添加了一个新字段,该字段标识每组 4 个点所属的精灵;使用GLubyte以获得最佳性能。您会注意到同一组点一遍又一遍地重复。以这种方式实现实例化有效地增加了存储需求O (N*V + 4N),其中 V 是 4 个点的原始顶点数据结构的大小,N 是精灵的数量。

您甚至可以定义一个顶点缓冲区,其中包含足够 16 个精灵的点,然后当您想在一次调用中绘制多个精灵时,您将始终使用相同的顶点缓冲区并使用点总数的子集。要使用此顶点缓冲区绘制 4 个精灵,只需绘制它包含的 64 个点中的前 16 个

现在,这只是过程的一半。您还需要设置顶点着色器,以采用定义每个精灵的转换的模型视图矩阵制服数组。

下面是可用于执行此操作的示例顶点着色器:

#version 100
uniform mat4    proj_mat;
uniform mat4    instanced_mv [16];
attribute vec4  vtx_pos;
attribute vec2  vtx_st;
attribute float vtx_sprite_idx; // This would be a uint in desktop GLSL
varying   vec2  tex_st;
void main (void) {
  gl_Position = proj_mat * instanced_mv [(int)vtx_sprite_idx] * vtx_pos;
  tex_st      = vtx_st;
}

这里有几点需要注意:

  1. GLES 2.0 不支持整数顶点属性,因此索引必须是浮点型

      索引
    • 统一数组必须在顶点着色器中使用整数表达式完成,因此必须进行强制转换。

  2. 模型视图制服的数量实际上是一次可以实例化多少精灵的限制因素

      顶点着色器只需要支持 128 个 4 分量制服
    • 变量(一个mat4计为 4 个 4 分量制服),因此这意味着如果您的顶点着色器只有 ModelView 制服,则最多可以支持 32 个数组 (128/4)。

  3. 精灵索引作为GLubyte存储在顶点缓冲区中,但请确保在设置顶点 Attrib 指针时不要启用浮点规范化。

最后,此着色器尚未经过测试。如果您在理解此处的任何内容时遇到问题或遇到实现此问题的问题,请随时发表评论。

最新更新