我怎样才能阻止草地落后于我的比赛?

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() 并告诉它您希望它绘制多少个模型副本。您需要找出一种方法来获取有关每个对象的信息,有几种不同的方法。

  1. 一个典型的简单方法是使用除数集的属性。这些属性将指定每个对象而不是每个顶点的数据。 glVertexAttribDivisor() 将设置除数。

  2. 另一种方法是将每个对象的属性放入统一缓冲区,您可以使用 gl_InstanceID.

  3. 对其进行索引
  4. 您也可以将每个对象的属性放在缓冲区纹理中,并使用 gl_InstanceID 对其进行索引。

方法二:几何着色器

由于您的模型是一个简单的广告牌,您可以在几何着色器中生成整个模型。使用这种方法,您可以放弃模型,并为每个草对象创建一个顶点数组。你调用glDrawArrays(GL_POINTS, ...),然后几何着色器将每个点转换成一个四边形(实际上,一个带两个三角形的三角形带)。

建议

我的建议是使用几何着色器方法,因为它非常适合处理广告牌。如果你的草模型更复杂,我会建议实例化。

我还注意到您将固定功能管道与现代 VAO 混合在一起。这……有点奇怪。你可能不得不放弃使用 GL_MODELVIEW_MATRIX 和朋友,至少在你画草的时候。

由于@DietrichEpp 已经涵盖了一些更高层次的方法,我将重点关注对您当前代码的一些直接建议。如果您还没有准备好进行跳跃,我认为您可以通过修复第一项来获得实质性的改进。

  1. 您的渲染代码中有一个 glGetFloat(GL11.GL_MODELVIEW_MATRIX, ...) 调用。您应该 永远不会 在执行非常频繁的代码的任何速度关键部分进行 glGet*() 调用。它们可能对性能非常有害。

    在这种情况下,您似乎试图从当前变换矩阵中提取和平移部分。由于您自己指定了所有转换,因此只需计算必要的转换并应用它应该相当容易。

  2. 您正在创建缓冲区。对象创建相当昂贵,我会尽量避免在渲染循环中使用它。而且我相信这可能会创建一个本机缓冲区,这可能会产生更多的开销。如果你真的需要一个缓冲区,创建一次,然后在绘制每个对象时重复使用它。

  3. 有几个电话每次看起来都一样。如果您遍历此代码序列,请将它们拉出循环。例如:

    glDisable(GL_CULL_FACE);
    ...
    GL13.glActiveTexture(GL13.GL_TEXTURE0);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, TerrainDemo.texModel.getTexture().getID());
    
  4. 摆脱固定函数矩阵功能,自己构建矩阵,并进行一次 glUniformMatrix4fv() 调用以指定它们。

  5. 您没有有效地使用 VAO:

    GL30.glBindVertexArray(model.getVaoID());
    GL20.glEnableVertexAttribArray(0);
    GL20.glEnableVertexAttribArray(1);
    

    属性指针enable/disable状态是VAO状态的一部分。因此,当您设置 VAO 时,您只需调用一次 glEnableVertexAttribArray()