【问题标题】:Dynamic tile display optimalization in OpenGLOpenGL中的动态平铺显示优化
【发布时间】:2011-06-15 02:08:37
【问题描述】:

我正在开发一个基于图块的自上而下的 2D 游戏,该游戏具有动态生成的地形,并开始(重新)编写 OpenGL 中的图形引擎。该游戏是使用 LWJGL 用 Ja​​va 编写的,我希望它相对独立于平台,并且也可以在旧电脑上玩。

目前我正在使用即时模式进行绘图,但显然这对于​​除了最简单的场景之外的任何东西都太慢了。

有两种基本类型的对象被绘制:Tiles 是世界,Sprites 是几乎所有其他东西(实体、项目、效果等)。

图块大小为 20*20 像素,存储在块中(40*40 图块)。地形生成是分块完成的,就像在 Minecraft 中一样。
我现在使用的方法是遍历玩家附近的 9 个块,然后遍历内部的每个图块,为图块纹理绘制一个四边形,并根据材质为特征绘制可选的额外四边形。 这最终会很慢,但是一个简单的视野外检查可以提高 5-10 倍的 FPS。

为了优化这一点,我研究了使用 VBO 和四边带,但是当地形变化时我遇到了问题。这不会在每一帧都发生,但也不是非常罕见的事件。 一个简单的方法是在每次更改时删除和重建块的 VBO。不过,这似乎不是最好的方法。我读到 VBO 可以是“动态的”,允许更改其内容。如何做到这一点,以及可以有效地更改其中的哪些数据?还有其他有效绘制世界的方法吗?

另一种类型,精灵,目前使用四边形绘制,纹理从精灵表映射。因此,通过更改纹理坐标,我什至可以稍后为它们设置动画。这是做动画的正确方法吗?
目前即使是非常多的精灵也不会减慢游戏速度,通过了解 VBO,我将能够更快地加速它们,但我还没有看到任何可靠可靠的教程来提供有效的方法这样做。有人知道吗?

感谢您的帮助!

【问题讨论】:

    标签: opengl sprite tiles


    【解决方案1】:

    目前我正在使用即时模式进行绘图,但显然这对于​​除了最简单的场景之外的任何东西都太慢了。

    我不同意。除非您要绘制 很多 个图块(每帧数万个),否则立即模式应该适合您。

    关键是你必须做的事情才能获得良好的性能:纹理图集。您的所有图块都应存储在单个纹理中。渲染时,您使用纹理坐标从该纹理中拉出不同的图块。所以如果这是你的渲染循环现在的样子:

    for(tile in tileList) //Pseudocode. Not actual C++
    {
        glBindTexture(GL_TEXTURE_2D, tile.texture);
        glBegin(GL_QUADS);
            glTexCoord2f(0.0f, 0.0f);
            glVertex2fv(tile.lowerLeft);
            glTexCoord2f(0.0f, 1.0f);
            glVertex2fv(tile.upperLeft);
            glTexCoord2f(1.0f, 1.0f);
            glVertex2fv(tile.upperRight);
            glTexCoord2f(1.0f, 0.0f);
            glVertex2fv(tile.lowerRight);
        glEnd();
    }
    

    你可以把它变成这样:

    glBindTexture(GL_TEXTURE_2D, allTilesTexture);
    glBegin(GL_QUADS);
    for(tile in tileList) //Still pseudocode.
    {
        glTexCoord2f(tile.texCoord.lowerLeft);
        glVertex2fv(tile.lowerLeft);
        glTexCoord2f(tile.texCoord.upperLeft);
        glVertex2fv(tile.upperLeft);
        glTexCoord2f(tile.texCoord.upperRight);
        glVertex2fv(tile.upperRight);
        glTexCoord2f(tile.texCoord.lowerRight);
        glVertex2fv(tile.lowerRight);
    }
    glEnd();
    

    如果您已经在使用纹理图集但仍未获得可接受的性能,那么您可以继续使用缓冲对象等。但是,如果您不先执行此操作,您将不会从缓冲区对象中获得任何更好的性能。

    如果您的所有图块都无法放入单个纹理中,那么您将需要执行以下两项操作之一:使用多个纹理(在一个 glBegin/glEnd 对中为每个纹理渲染尽可能多的图块),或使用纹理数组。纹理数组仅在 OpenGL 3.0 级别的硬件中可用。这意味着任何 Radeon HDxxxx 或 GeForce 8xxxx 或更高版本。

    您提到您有时会在图块上渲染“特征”。这些功能可能使用混合和与常规图块不同的 glTexEnv 模式。在这种情况下,您需要想办法将相似的特征组合成一个 glBegin/glEnd 对。

    正如您可能从中了解到的,性能的关键是尽量减少调用 glBindTexture 和 glBegin/glEnd 的次数。在每个 glBegin/glEnd 中做尽可能多的工作。

    如果您希望继续使用基于缓冲区的方法(并且只有在纹理图集方法没有使您的性能达到标准时才应该打扰),这相当简单。将所有图块“块”放入单个缓冲区对象中。不要为每个人做一个缓冲区;没有真正的理由这样做,并且 40x40 瓦片的顶点数据只有 12,800 字节。您可以将 81 个这样的块放在一个 1MB 的缓冲区中。这样,您只需为您的地形调用 glBindBuffer。这又可以节省您的性能。

    我需要更多地了解这些“功能”,您有时会使用这些“功能”来建议优化它们的方法。但至于动态缓冲区,我不担心。只需使用 glBufferSubData 来更新有问题的缓冲区部分。如果事实证明这很慢,那么您可以使用多种选项来加快速度。但除非您知道这是必要的,否则您不应该打扰,因为它们很复杂。

    精灵可能是从缓冲区对象方法中受益的绝对最少的东西。与立即模式相比,它确实没有什么好处。即使您要渲染数百个,每个都将有自己的转换矩阵。这意味着每个都必须是一个单独的绘图调用。所以也可以是glBegin/glEnd。

    【讨论】:

    • 感谢您提供的信息,我将检查纹理图集的改进程度,并将考虑将绘图重组为更少的 glBegin/end 调用。这些特征的一个很好的例子是岩石中随机放置的一些小矿床,最简单的方法可能仍然可以正常工作,就是将它们绘制在石头的顶部,在一个单独的四边形中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多