我怎样才能阻止草地落后于我的比赛?
How can I stop grass from lagging my game?
我正尝试在我的游戏中添加草。
然而不幸的是,它使 FPS 从 60+ 下降到只有 36。如果我的船直接撞到地形上,它甚至可以下降到 32。
我试过不把草渲染得更远。
然而,它仍然是游戏 FPS 的巨大打击。请记住,如果没有它,我的 FPS more-than 会翻倍!
草使用与树木相同的模型- 广告牌始终面向相机。
显然我用片段着色器删除了透明位。
我听说过像 "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);
如何减少草造成的延迟?
(小提示:出于演示目的,我删除了地形纹理,以便您更容易看到草地)
更新:我刚刚尝试剔除草的背面,但似乎没有任何区别。即使我剔除两张脸也没有区别。
您有很多对象要绘制,并且您要为每个对象执行 glDrawElements()
(加上约 10 次状态更改)。如果您有 1,000 块草地,则相当于对 OpenGL 进行了 10,000 API 次调用。众所周知,这是做事的缓慢方式,当您以这种方式做事时,通常会 运行 进入 CPU 限制,然后 运行 进入 GPU 限制。
缓慢的方式
这是将内容绘制到屏幕的缓慢方法。
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
和朋友,至少在你画草的时候。
由于@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);
属性指针enable/disable状态是VAO状态的一部分。因此,当您设置 VAO 时,您只需调用一次 glEnableVertexAttribArray()
。
我正尝试在我的游戏中添加草。
然而不幸的是,它使 FPS 从 60+ 下降到只有 36。如果我的船直接撞到地形上,它甚至可以下降到 32。
我试过不把草渲染得更远。
然而,它仍然是游戏 FPS 的巨大打击。请记住,如果没有它,我的 FPS more-than 会翻倍!
草使用与树木相同的模型- 广告牌始终面向相机。
我听说过像 "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);
如何减少草造成的延迟?
(小提示:出于演示目的,我删除了地形纹理,以便您更容易看到草地)
更新:我刚刚尝试剔除草的背面,但似乎没有任何区别。即使我剔除两张脸也没有区别。
您有很多对象要绘制,并且您要为每个对象执行 glDrawElements()
(加上约 10 次状态更改)。如果您有 1,000 块草地,则相当于对 OpenGL 进行了 10,000 API 次调用。众所周知,这是做事的缓慢方式,当您以这种方式做事时,通常会 运行 进入 CPU 限制,然后 运行 进入 GPU 限制。
缓慢的方式
这是将内容绘制到屏幕的缓慢方法。
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
和朋友,至少在你画草的时候。
由于@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);
属性指针enable/disable状态是VAO状态的一部分。因此,当您设置 VAO 时,您只需调用一次
glEnableVertexAttribArray()
。