高效的精灵渲染opengl es 2.0

时间:2010-09-27 15:45:43

标签: iphone opengl-es

我正在使用ipad应用程序,每帧渲染数百个精灵(2d图像)。我使用的是从sdk的opengl模板中获取的修改绘图方法,但问题是,我只得到了3fps,我甚至没有渲染我需要的一切。

我尝试了简单的优化方法,比如使用纹理图册,最小化状态变化次数,高级剪裁,甚至渲染精灵按常用属性排序,如使用的颜色或纹理,但似乎没什么帮助。我不能使用pvr压缩,因为我的图像有精细的边缘和alpha通道,压缩后看起来很糟糕(我只在少数背景图像上使用它)。

我现在正在尝试使用VBO,但我不确定它们是否适用于简单的精灵(2个三角形)。我一直认为它们适用于具有大量顶点的模型。我甚至不确定如何正确实现它们。我可能需要将VBO索引保存到我的sprite类中。问题是我并不总是使用类来渲染精灵,有时我只是动态计算精灵的位置,大小和紫外线(例如文本渲染)。如果使用带有精灵渲染的VBO,那么任何想法都会带来一些性能提升吗?

这是我的渲染功能:

- (void)RenderTexture:(GLTexture*)tex InRect:(CGRect)dest WithUV:(CGRect)uv Color:(LSColor*)color Effect:(SpriteEffect)effect Rotation:(float)rot AroundPoint:(CGPoint)rotCenter {
 if(tex.ID != mLastBoundTexture) {
   [tex bind];
   mLastBoundTexture = tex.ID;
 }

 mSquareVertices[2] = mSquareVertices[6] = dest.size.width;
 mSquareVertices[5] = mSquareVertices[7] = dest.size.height;

 mSquareUVs[0] = mSquareUVs[4] = uv.origin.x;
 mSquareUVs[1] = mSquareUVs[3] = uv.origin.y;
 mSquareUVs[2] = mSquareUVs[6] = uv.origin.x + uv.size.width;
 mSquareUVs[5] = mSquareUVs[7] = uv.origin.y + uv.size.height;

 mSquareColors[0] = mSquareColors[4] = mSquareColors[8] = mSquareColors[12] = color.red;
 mSquareColors[1] = mSquareColors[5] = mSquareColors[9] = mSquareColors[13] = color.green;
 mSquareColors[2] = mSquareColors[6] = mSquareColors[10] = mSquareColors[14] = color.blue;
 mSquareColors[3] = mSquareColors[7] = mSquareColors[11] = mSquareColors[15] = color.alpha;

 mat4f_LoadTranslation2f(rotCenter.x, rotCenter.y, mModelViewMatrix);

 mat4f_LoadTranslation2f(dest.origin.x, dest.origin.y, mModelViewMatrix);
 mat4f_MultiplyMat4f(mProjectionMatrix, mModelViewMatrix, mModelViewProjMatrix);

 if(mLastUsedShader != effect) {
   int program;
   if(effect == SENormal) {
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     program = mShaderNormal;
   }
   else if(effect == SEMultiply) {
     glBlendFunc(GL_DST_COLOR, GL_ZERO);
     program = mShaderMultiply;
   }  
 else {
   NSLog(@"Implement SpriteEffect %i", effect);
 }

 glUseProgram(program);
 mLastUsedShader = effect;
 } 
 glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEW_PROJECTION_MATRIX], 1, GL_FALSE, mModelViewProjMatrix);

 // Update attribute values
 glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, mSquareVertices);
 glVertexAttribPointer(ATTRIB_UV, 2, GL_FLOAT, 0, 0, mSquareUVs);
 glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 1, 0, mSquareColors);

 glUniform4fv(uniforms[UNIFORM_POSTPROCES_PARAMS], 4, mPostprocessParams);

 // Draw
 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 }

我认为颜色可能是另一个改进的领域,因为它不会经常变化(每帧几次),但我不知道如何将其设置的时间长于当前的渲染调用。

你是否看到我可以提高帧率的其他方面?我真的需要至少以30fps的速度获得这个

编辑:事实证明我有太复杂的片段着色器。因为没有禁用它来测试它我觉得很愚蠢。看起来我将不得不告别我的去饱和功能。使用默认的片段着色器,我可以轻松获得超过60 fps。

3 个答案:

答案 0 :(得分:3)

你不是填充率(你可能会,但有一个更大的问题)。你说你有2000个精灵。对于每个你单独设置顶点/片段着色器,你计算EACH精灵的投影和其他矩阵,你只用每个渲染调用渲染一个精灵。这样,无论您是使用纹理/复杂着色器还是仅使用纯平面着色,您都将永远无法渲染出相当数量的精灵。

你需要做什么:批处理。批处理意味着您必须尝试将尽可能多的精灵累积到单个顶点缓冲区对象中,然后通过一次调用glDrawElements / glDrawArrays来尽可能多地绘制它们。有几件事可能会让你不能使用不同的纹理(它们不应该使用纹理图集),它们使用不同的着色器(除非你做了真正需要的东西,否则不太可能使用2000个精灵)等等。这些可以在一定程度上解决。按z顺序排序,然后按材质排序,其中材质为纹理/着色器。然后,您可以在一次调用中将更大的精灵组发送到GPU。

我要提到的最后一件事:你必须自己在CPU上进行转换,而不是为每个sprite设置一个新的Matrix,让CPU完成工作。

关于这样一个sprite批处理可能看起来的例子,你可以签出我为Android游戏开发人员编写的SpriteBatch类。它不是100%最佳但非常接近,适用于GL ES 1.x和2.0(尽管在后一种情况下使用静态着色器)。在那里你可以找到如何在没有矩阵的情况下轻松地自己变换精灵的顶点。您可以在http://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/graphics/SpriteBatch.java

找到代码

HTH, 马里奥

答案 1 :(得分:2)

我的直觉是你只是填充率。

你的100个精灵覆盖了多少像素? GPU具有有限的计算像素容量(特别是在混合的情况下 - 你说有alpha,因为它需要读取和写入帧缓冲区)。如果你生成太多,你的帧速率将受到严重影响。最糟糕的情况是每个精灵都覆盖整个屏幕,大约是屏幕总像素数的100倍。 (那100倍是我们所谓的透支因子)。

另一种选择是你是着色器。你的片段着色器做什么?如果用简单的恒定颜色输出替换它会发生什么?

我不认为几何提交与你的性能问题有关(不是100个精灵)。

最重要的是,要了解性能,您需要使用性能分析工具。我不反对ipad编码。 SDK是否提供了分析性能的任何工具?

答案 2 :(得分:0)

你可能想要研究点精灵来加速渲染。