我正在开发一个基于图块的自上而下的2D游戏,其中包含动态生成的地形,并开始(重新)在OpenGL中编写图形引擎。游戏是用Java编写的,使用LWJGL,我更喜欢它保持相对平台无关,也可以在旧电脑上播放。
目前我正在使用即时模式进行绘制,但显然这对于除最简单场景之外的任何事情来说都太慢了。
绘制的对象有两种基本类型:Tiles,即世界,Sprite,几乎所有其他东西(实体,项目,效果等)。
瓷砖为20 * 20像素,并以块(40 * 40个瓷砖)存储。地形生成是在完整的块中完成的,就像在Minecraft中一样 我现在使用的方法是迭代播放器附近的9个块,然后在内部的每个瓦片上迭代,为瓦片纹理绘制一个四边形,并根据材料绘制特征的可选额外四边形。 这最终会很慢,但是简单的视线外检查可以提供5-10倍的FPS提升。
为了优化这一点,我研究了使用VBO和四条带,但是当地形发生变化时我遇到了问题。这不是每一帧都会发生,但也不是非常罕见的事件。 一个简单的方法是在每次更改时丢弃并重建块的VBO。这似乎不是最好的方式。我读到VBO可以是“动态的”,允许更改其内容。如何做到这一点,以及哪些数据可以有效地在其中进行更改?有没有其他方法可以有效地吸引世界?
另一种类型的精灵当前是用四边形绘制的,纹理从精灵表映射。因此,通过更改纹理坐标,我甚至可以在以后设置它们的动画。这是做正义的正确方法吗? 目前即使是非常多的精灵也不会减慢游戏速度,通过了解VBO,我将能够加快它们的速度,但是我还没有看到任何有效的方法来获得有效的方法。这样做。有没有人知道一个?
感谢您的帮助!
答案 0 :(得分:7)
目前我正在使用即时模式进行绘制,但显然这对于除最简单场景之外的任何事情来说都太慢了。
我不同意。除非您正在绘制批次的图块(每帧数万个),否则立即模式对您来说应该没问题。
关键是你必须要做的才能获得良好的表现:纹理地图集。所有瓷砖都应存储在单个纹理中。在渲染时,您可以使用纹理坐标从该纹理中拉出不同的切片。所以如果这就是你现在的渲染循环:
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中尽可能多地工作。
如果您希望继续使用基于缓冲区的方法(如果纹理图集方法无法使您的性能达到标准,您应该只会烦恼),这非常简单。将所有tile“chunk”放入单个缓冲区对象中。不要为每一个做缓冲;没有真正的理由这样做,40x40瓦片的顶点数据只有12,800字节。你可以将81个这样的块放在一个1MB的缓冲区中。这样,您只需为您的地形调用glBindBuffer。这再次为您节省了成绩。
我需要了解更多有关这些“功能”的信息,您有时会使用这些功能来建议优化它们的方法。但至于动态缓冲区,我不担心。只需使用glBufferSubData来更新有问题的缓冲区部分。如果结果很慢,有几种选择可以让你加快速度。但除非你知道有必要,否则你不应该费心,因为它们很复杂。
精灵可能会从缓冲对象方法中获得绝对的最少。它在立即模式下真的没什么好处的。即使你渲染了数百个,每个人都有自己的变换矩阵。这意味着每个人都必须是一个单独的平局调用。所以它也可能是glBegin / glEnd。