我正试图在我的游戏中添加草。
然而不幸的是,它将FPS从60+降至36.如果我将我的船撞到地形,它甚至可以降到32。
我已经尝试过不让草更远了。
然而它仍然是游戏FPS的巨大打击。请记住,没有它我的FPS超过双倍!
草使用与树木相同的模型 - 转向总是面向相机的广告牌。 显然我用片段着色器删除透明位。
我听说过" Clumping"等技术,但我不知道该怎么做,甚至不知道为什么它会有助于提高性能。
我使用此代码进行渲染:
TexturedModel texturedModel = TerrainDemo.textModel;
RawModel model = texturedModel.getRawModel();
glDisable(GL_CULL_FACE);
FloatBuffer buf = BufferUtils.createFloatBuffer(16 * 4);
// Get your current model view matrix from OpenGL.
glTranslatef(location.x * TerrainDemo.scale, location.y, location.z * TerrainDemo.scale);
glRotatef(90, 1f, 0f, 0f);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, buf);
buf.rewind();
buf.put(0, 1.0f);
buf.put(1, 0.0f);
buf.put(2, 0.0f);
buf.put(4, 0.0f);
buf.put(5, 1.0f);
buf.put(6, 0.0f);
buf.put(8, 0.0f);
buf.put(9, 0.0f);
buf.put(10, 1.0f);
GL11.glLoadMatrix(buf);
GL30.glBindVertexArray(model.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, TerrainDemo.texModel.getTexture().getID());
glScalef(15f, 15f, 15f);
glColor4f(0, 0, 0, 0.5f);
glRotatef(90, 0f, 1f, 0f);
glDrawElements(GL_TRIANGLES, model.getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
如何减少草所造成的滞后?
(快速注意:我删除了地形纹理以用于演示目的,因此您可以更容易地看到草地)
更新:我只是试图剔除草的背面,但似乎没有任何区别。即使我剔除了两个面孔也没有区别。
答案 0 :(得分:2)
您需要绘制很多对象,并且每个对象都要执行glDrawElements()
(加上~10个状态更改)。如果你有1000块草,那就是对OpenGL的10,000次API调用。这通常被认为是缓慢的做事方式,当你以这种方式做事时,在遇到GPU限制之前,你通常会遇到CPU限制。
这是将内容绘制到屏幕上的缓慢方式。
for (obj : all objects) {
glUseProgram(...);
glUniform(...);
glBindVertexArray(...);
glDrawElements(...);
}
这里的目标是,无论你想绘制多少草,都可以进行相同数量的OpenGL API调用。
int count = 0;
for (obj : all grass) {
add obj data to buffer;
count += 1;
}
glUseProgram(...);
glUniform(...);
glBindVertexArray(...);
glDraw?????(..., count);
实现这一目标的一种方法是使用实例化。通过实例化,您可以调用glDrawElementsInstanced()
并告诉它您希望它绘制多少个模型副本。您需要找到获取每个对象信息的方法,有几种不同的方法。
一种典型的简单方法是使用带有除数集的属性。这些属性将指定每个对象而不是每个顶点的数据。 glVertexAttribDivisor()
将设置除数。
另一种方法是将每个对象的属性放入一个统一的缓冲区,您可以使用gl_InstanceID
进行索引。
您还可以将每个对象的属性放在缓冲区纹理中,并使用gl_InstanceID
对其进行索引。
由于您的模型是一个简单的广告牌,您可以在几何着色器中生成整个事物。通过这种方法,您可以抛弃模型,并为每个草对象创建一个具有一个点的顶点数组。您调用glDrawArrays(GL_POINTS, ...)
,然后几何着色器将每个点转换为四边形(实际上是一个带有两个三角形的三角形条)。
我的建议是使用几何着色器方法,因为它非常擅长处理广告牌。如果你的草模型更复杂,我会推荐实例化。
我还注意到你将固定功能管道与现代VAO混合在一起。这有点奇怪。你可能不得不放弃使用GL_MODELVIEW_MATRIX
和朋友,至少在你画草的时候。
答案 1 :(得分:2)
由于@DietrichEpp已经涵盖了一些更高级别的方法,我将专注于对您当前代码的一些直接建议。如果你还没准备好跳跃,我认为你可以通过修复第一个项目获得实质性的改进。
您的渲染代码中有glGetFloat(GL11.GL_MODELVIEW_MATRIX, ...)
次调用。您应该从不在代码的任何速度关键部分进行glGet*()
调用,这些部分经常执行。它们可能对性能非常有害。
在这种情况下,您似乎尝试从当前转换矩阵中提取和转换部分。由于您自己指定了所有转换,因此只需计算必要的转换并应用它就相当容易。
您正在创建一个缓冲区。对象创建相当昂贵,我会尝试在渲染循环中避免它。我相信这可能会创建一个本机缓冲区,这可能是更多的开销。如果确实需要缓冲区,请创建一次,并在绘制每个对象时重复使用它。
每次都有几个看起来相同的电话。如果循环遍历此代码序列,请将它们拉出循环。例如:
glDisable(GL_CULL_FACE);
...
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, TerrainDemo.texModel.getTexture().getID());
摆脱固定的函数矩阵功能,自行构建矩阵,并进行一次glUniformMatrix4fv()
调用以指定它们。
您没有有效使用VAO:
GL30.glBindVertexArray(model.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
属性指针启用/禁用状态是VAO状态的一部分。因此,当您设置VAO时,您只需进行一次glEnableVertexAttribArray()
次呼叫。