目前使用OpenGL ES 3.1绘制大量四边形的最快方法?

时间:2016-09-19 15:27:55

标签: opengl-es-3.0

晚上好,以下情景:

我正在实施标签渲染算法。输入数据由许多字符串和纹理图集组成,纹理图集具有每个可能的字符串字形的纹理数据。每个字符串都应该生成一些四边形,每个字形的每个实例都有一个,其纹理坐标引用纹理图集。

框架之间唯一可以改变的是标签的屏幕位置及其字形。所以,我的第一个想法是创建一个庞大的四边形缓冲区,组成本地坐标中的字形,然后从每个帧更新的单独缓冲区中读取位置。

现在我的问题:在OpenGL ES 3.1中,目前最快的方法是什么?我的第一个想法是使用glDrawElements和GL_TRIANGLE_STRIP使用原始重启索引,然后将索引转换为顶点着色器内缓冲区中的某个条目并从中读取位置。

但是,我想知道在此期间是否出现了更快的方法。

提前谢谢!

2 个答案:

答案 0 :(得分:1)

我写了一个小的精灵着色器,这是一个精灵着色器的克隆,来自这里http://learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites

直观的OpenGL和OpenGL ES 3.1之间的唯一区别在于着色器中的几行。您需要在以下两个着色器中更改版本:

#version 330 core

#version 300 es

并且,对于片段着色器,添加行

precision mediump float;

靠近顶部的地方。

然后,假设您的着色器具有模型和投影矩阵制服,您只需使用2d向量位置来创建4d向量,将0.0和1.0替换为顶点着色器中的最后两个参数:

gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);

我觉得在Joey的LearnOpenGL教程中解释得比我在这里详细解释的要好。

你的绘图函数可能看起来像这样(如果你使用C ++ 11):

mShader( [ & ] ( ) // Same as glUseProgram( shaderID ) but automatically un-Uses it when it goes out of scope
{
    glm::mat4 model;
    model = glm::translate( model, glm::vec3( xPosition, 0.0f ) );
    model = glm::translate( model, glm::vec3( 0.5f * xSize.x, 0.5f * xSize.y, 0.0f ) );
    model = glm::rotate(    model, xRotate, glm::vec3( 0.0f, 0.0f, 1.0f ) );
    model = glm::translate( model, glm::vec3( -0.5f * xSize.x, -0.5f * xSize.y, 0.0f ) );
    model = glm::scale(     model, glm::vec3( xSize, 1.0f ) );

    mShader.getUniforms( ).setUniformMatrix4fv( "model", model );
    mShader.getUniforms( ).setUniformVector3f( "spriteColor", xColor );
    glActiveTexture( GL_TEXTURE0 );

     xTexture( [ & ]( ) // Does the same as glBindTexture but automatically unbinds when out of scope
     {
        glBindVertexArrayOES( mQuadVAO );
        glDrawArrays( GL_TRIANGLES, 0, 6 );
        glBindVertexArrayOES( 0 );
    } );
} );

我的实现与我使用SDL获取OpenGL ES 3.0上下文(用于可移植性和其他功能,如键盘,鼠标)的教程的唯一区别。我可以确认Joey的教程是在Android,嵌入式Linux Intel集成显卡和带有AMD GLES3 SDK的Windows上运行的,只是我上面提到的一些小改动。

您可能希望通过使用PBO来扩充实施;一个OpenGL版本在同一个repo中。

它使用公益组织。这可以用于比glTexImage2D更快地更新四边形纹理。我不知道OpenGL ES是否支持PBO,但它们可能是扩展的。如果你只需要提前加载一堆四边形并渲染它们,你就不需要那么远了。

要以简单的方式更新四元组,它只是:

glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE );
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, mW, mH, GL_RGBA, GL_UNSIGNED_BYTE, xData );

使用PBO,您可以使用两个PBO来启用“双缓冲功能”,清除PBO并在每个回合映射另一个PBO。有关详细信息,请参阅glBindBufferARBglBufferDataARBglMapBufferARBglUnmapBufferARB的OpenGL ES规范。显然,从3.0开始,OpenGL ES确实支持PBO,因此如果您需要快速更新纹理,这是可行的方法。

答案 1 :(得分:0)

基于OpenGL ES的设备大多受限于每帧的绘制调用次数。因此,您需要将每个角色的所有三角形批量分成一个网格并绘制一次。在大多数情况下,这将为您提供最佳性能。

  • 将所有顶点分成一个网格
  • 使用索引三角形构造网格并使用VAO
  • 在顶点属性中添加额外的索引以表示字符索引。
  • 将每个字符的Model矩阵作为统一数组传递。
  • 在“顶点着色器”中,将顶点位置与该索引的模型矩阵相乘。

此方法存在一些限制。

  • 您可以批量处理的字符数取决于硬件。 (该 一次允许一个着色器的缓冲区大小。)
  • 如果顶点的数量非常大,您可能很容易在顶点着色器中遇到瓶颈。