OpenGL中的动态平铺显示优化

OpenGL中的动态平铺显示优化,opengl,sprite,tiles,Opengl,Sprite,Tiles,我正在开发一个基于平铺的、自上而下的2D游戏,其中包含了动态生成的地形,并开始(重新)在OpenGL中编写图形引擎。这个游戏是用LWJGL用Java编写的,我更喜欢它相对独立于平台,并且可以在旧电脑上玩 目前,我正在使用即时模式进行绘制,但显然这对于除最简单场景之外的任何场景来说都太慢了 绘制的对象有两种基本类型:平铺(即世界)和精灵(即几乎所有其他对象(实体、项目、效果等) 瓷砖为20*20像素,并以块(40*40瓷砖)的形式存储。地形生成是分块完成的,就像在Minecraft中一样。 我现在

我正在开发一个基于平铺的、自上而下的2D游戏,其中包含了动态生成的地形,并开始(重新)在OpenGL中编写图形引擎。这个游戏是用LWJGL用Java编写的,我更喜欢它相对独立于平台,并且可以在旧电脑上玩

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

绘制的对象有两种基本类型:平铺(即世界)和精灵(即几乎所有其他对象(实体、项目、效果等)

瓷砖为20*20像素,并以块(40*40瓷砖)的形式存储。地形生成是分块完成的,就像在Minecraft中一样。
我现在使用的方法是迭代播放器附近的9个区块,然后迭代内部的每个瓷砖,为瓷砖纹理绘制一个四边形,并根据材质为特征绘制可选的额外四边形。 这最终相当缓慢,但一个简单的看不见的检查提供了5-10倍每秒的提升

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

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

谢谢你的帮助

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

我不同意。除非您正在绘制大量的瓷砖(每帧上万块),否则立即模式应该适合您

关键是要获得良好的性能,您必须要做的事情:纹理地图集。所有的瓷砖都应该存储在一个纹理中。渲染时,可以使用“纹理坐标”从该纹理中拉出不同的瓷砖。因此,如果这就是渲染循环现在的样子:

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 8xxx或更高版本

您提到有时会在平铺上渲染“特征”。这些功能可能使用混合和不同于常规瓷砖的glTexEnv模式。在这种情况下,您需要找到将类似功能分组到单个glBegin/glEnd对中的方法

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

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

我需要更多地了解这些“特性”,您有时会使用这些特性来建议优化它们的方法。但对于动态缓冲区,我并不担心。只需使用glBufferSubData来更新有问题的缓冲区部分。如果结果证明这是缓慢的,有几个选项可以让它更快,你可以采用。但是你不应该麻烦,除非你知道这是必要的,因为它们很复杂

精灵可能从缓冲区对象方法中获益最少。在即时模式下,它真的没有什么好处。即使渲染了数百个,每个都有自己的变换矩阵。这意味着每一个都必须是单独的抽签。因此,它也可能是glBegin/glEnd

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

我不同意。除非您正在绘制大量的瓷砖(每帧上万块),否则立即模式应该适合您

关键是要获得良好的性能,您必须要做的事情:纹理地图集。所有的瓷砖都应该存储在一个纹理中。渲染时,可以使用“纹理坐标”从该纹理中拉出不同的瓷砖。因此,如果这就是渲染循环现在的样子:

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();
如果已经在使用纹理图集,但仍然无法获得可接受的性能,则可以继续使用缓冲对象等。但如果不先这样做,缓冲区对象的性能将不会得到任何改善

如果所有的瓷砖都无法放入单个纹理中,则需要执行以下两项操作之一:使用多个纹理(渲染尽可能多的瓷砖)