怎么能阻止草落后于我的游戏?

时间:2015-01-14 14:29:07

标签: java opengl lwjgl

我正试图在我的游戏中添加草。

enter image description here

然而不幸的是,它将FPS从60+降至36.如果我将我的船撞到地形,它甚至可以降到32。

我已经尝试过不让草更远了。 enter image description here

然而它仍然是游戏FPS的巨大打击。请记住,没有它我的FPS超过双倍!

草使用与树木相同的模型 - 转向总是面向相机的广告牌。 enter image description here 显然我用片段着色器删除透明位。

我听说过" 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);

如何减少草所造成的滞后?

(快速注意:我删除了地形纹理以用于演示目的,因此您可以更容易地看到草地)

更新:我只是试图剔除草的背面,但似乎没有任何区别。即使我剔除了两个面孔也没有区别。

2 个答案:

答案 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);

方法1:实例化

实现这一目标的一种方法是使用实​​例化。通过实例化,您可以调用glDrawElementsInstanced()并告诉它您希望它绘制多少个模型副本。您需要找到获取每个对象信息的方法,有几种不同的方法。

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

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

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

  4. 方法2:几何着色器

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

    推荐

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

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

答案 1 :(得分:2)

由于@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);
    

    属性指针启用/禁用状态是VAO状态的一部分。因此,当您设置VAO时,您只需进行一次glEnableVertexAttribArray()次呼叫。