OpenGL 性能问题

Posted

技术标签:

【中文标题】OpenGL 性能问题【英文标题】:OpenGL performance issue 【发布时间】:2012-08-10 13:03:36 【问题描述】:

我正在使用 LWJGL 和 Java 1.6 编写 2D RPG。现在,我有一个“World”类,它包含一个 Tile 的 ArrayList(与每个 Tile 的基本代码的接口)和一个 GrassTile 类,它使用 Spritesheet。

当使用即时模式绘制 64x64 GrassTiles 网格时,我得到大约 100 FPS,并通过从 ArrayList 内的每个图块调用 .draw() 方法来完成此操作,该方法绑定了 spritesheet 并绘制了它的某个区域(使用glTexCoord2f())。所以我听说最好使用 VBO,得到一个基本教程并尝试在 .draw() 方法上实现它们。

现在有两个问题:我不知道如何仅将纹理的某个区域绑定到 VBO(整个纹理将只是 glBindTexture())所以我尝试仅将它们与颜色一起使用。 这把我带到了第二个问题:我只有 +20 FPS(总共 120),这并不是我所期望的,所以我想我做错了什么。此外,我在 ArrayList 中迭代时为每个 GrassTile 制作了一个 VBO。我认为这是错误的,因为我可以简单地将所有图块放入一个 FloatBuffer 中。

那么,如何以更好的方式绘制相似的几何图形,以及如何仅将纹理的某个区域绑定到 VBO?

【问题讨论】:

“仅将纹理的特定区域绑定到 VBO”是什么意思。您不将纹理绑定到缓冲区,而是将它们绑定到纹理单元... 好吧,我不知道,对不起。我的意思是:glTexCoord2f() 的 vbo 等价物是什么? opengl.org/sdk/docs/man/xhtml/glVertexAttribPointer.xml 哦,所以我可以像对顶点或颜色那样做? 是的。顶点属性是通用的......呃,我不想骗你。无论如何,现代 OpenGL (>= 3.3) 将允许您这样做。 【参考方案1】:

那么,我怎样才能以更好的方式绘制相似的几何图形...

就像@Ian Mallett 描述的那样;将所有顶点数据放入一个 single 顶点缓冲区对象。这使得一次调用即可呈现您的地图。 如果您的地图变大 1000 倍,您可能希望实施一个仅绘制屏幕上显示的顶点的相机解决方案,但如果您计划显着更大的地图。

...我怎样才能只将纹理的某个区域绑定到 VBO?

您只能绑定整个纹理。您必须指向到要映射的纹理的某个区域。

每个纹理坐标都与特定的顶点相关。每个图块都与四个顶点有关。您游戏中的常见图块共享相同的纹理,因此称为“图块地图”。好好利用它。将所有瓷砖纹理放在纹理表中并绑定该纹理表。 对于您创建的每个新“图块”,检查该区域是指空气、草地还是地面,然后指向与您想要的纹理对应的部分

假设您的纹理区域(以像素为单位)为 100x100。地面区域是从左下角算起的 15x15。按照上面的逻辑解释下面显示的示例代码:

// The vertexData array simply contains information
// about a tile's four vertices (or six 
// vertices if you draw using GL_TRIANGLES).

mVertexBuffer.put(0, vertexData[0]);
mVertexBuffer.put(1, vertex[1]);
mVertexBuffer.put(2, vertex[2]);
mVertexBuffer.put(3, vertex[3]);
mVertexBuffer.put(4, vertex[4]);
mVertexBuffer.put(5, vertex[5]);
mVertexBuffer.put(6, vertex[6]);
mVertexBuffer.put(7, vertex[7]);
mVertexBuffer.put(8, vertex[8]);
mVertexBuffer.put(9, vertex[9]);
mVertexBuffer.put(10, vertex[10]);
mVertexBuffer.put(11, vertex[11]);

if (tileIsGround) 
   mTextureCoordBuffer.put(0, 0.0f);
   mTextureCoordBuffer.put(1, 0.0f);

   mTextureCoordBuffer.put(2, 0.15f);
   mTextureCoordBuffer.put(3, 0.0f);

   mTextureCoordBuffer.put(4, 0.15f);
   mTextureCoordBuffer.put(5, 0.15f);

   mTextureCoordBuffer.put(6, 0.15f);
   mTextureCoordBuffer.put(7, 0.0f);
 else  /* Other texture coordinates. */ 

您实际上编写了解决方案。唯一的区别是您应该将纹理坐标数据上传到 GPU。

【讨论】:

【参考方案2】:

这是关键:

我正在为每个 GrassTile 制作一个 VBO,同时在 ArrayList 中进行迭代。

不要这样做。您制作一次 VBO,然后在必要时对其进行更新。制作纹理、VBO、着色器是 OpenGL 最慢的使用方法——难怪你会遇到帧率问题——你要这样做 O(n) 次,每一帧

我认为这有点不对,因为我不能['t?] 简单地将所有图块扔到一个 FloatBuffer 中。

只有在批量绘制调用时才能获得性能。这意味着当您绘制图块时,您应该使用一个 VBO一次绘制所有个。

//Initialize
Make a single VBO (or two: one for vertex, one for texture
coordinates, whatever--the key point is O(1) VBOs).
Fill your VBO with ALL of your tiles' data.

//Main loop
while (true) 
    Draw the VBO with a single draw call,
    thus drawing all your tiles all at once.

【讨论】:

以上是关于OpenGL 性能问题的主要内容,如果未能解决你的问题,请参考以下文章

绘制多边形网格时的 Opengl 性能问题

糟糕的 OpenCL ImageSampling 性能与 OpenGL TextureSampling

OpenGL 调试上下文性能警告

为啥没有实现 OpenGL 灰度纹理性能增益?

OpenGL 的低性能水平

OpenGL glReadPixels 性能