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;
}
这里有几点需要注意:
- GLES 2.0 不支持整数顶点属性,因此索引必须是浮点型
- 索引
- 统一数组必须在顶点着色器中使用整数表达式完成,因此必须进行强制转换。
- 统一数组必须在顶点着色器中使用整数表达式完成,因此必须进行强制转换。
- 模型视图制服的数量实际上是一次可以实例化多少精灵的限制因素
- 顶点着色器只需要支持 128 个 4 分量制服
- 变量(一个
mat4
计为 4 个 4 分量制服),因此这意味着如果您的顶点着色器只有 ModelView 制服,则最多可以支持 32 个数组 (128/4)。
将 - 变量(一个
- 精灵索引作为
GLubyte
存储在顶点缓冲区中,但请确保在设置顶点 Attrib 指针时不要启用浮点规范化。
最后,此着色器尚未经过测试。如果您在理解此处的任何内容时遇到问题或遇到实现此问题的问题,请随时发表评论。