java OpenGl>v3,2d游戏高效渲染
我对VBO和现代openGL感到困惑。在这篇文章的结尾有一个直接的问题,然后在去那里的路上有一堆问题。如果你对此有什么见解,我很乐意给你一个答复。如果你回答,请把我当作一个完全的白痴,一点知识都没有
我的历史是这样的:
我有一个游戏,是一个自上而下的2d游戏。我使用immideate模式渲染2d精灵。我的纹理地图集的实际纹理坐标是静态的,在一个单独的类中预定义。四边形坐标在每个实体中定义,并随着游戏的进行而更新。渲染时,我只需绑定一个称为glBegin(三角形)的特定纹理,然后调用每个可视对象渲染方法。这又将四坐标和纹理坐标发送到我的渲染器类,该类调用openGl。然后我刷新了纹理,它只调用glEnd()
我对所有不同的地图集都这样做,并按顺序进行,以便获得适当的深度
但时代确实在变化。我想移动到使用VBO和着色器。我过去试过好几次,但都失败了。有一些东西是我在谷歌上找不到的,可以让我对它有一个完整的了解,以及如何使用它来加速我的游戏
我知道基本情况。我可以简单地存储初始化阶段需要的所有信息,然后使用着色器计算最终结果,而不是在每次渲染调用时通过总线将所有信息发送到gpu。但是
我对纹理坐标有了一个想法。这些将是静态的,因为它们永远不会改变。将它们存储在GPU上是有意义的。但我如何知道每个四边形/三角形对应的坐标呢。我认为游戏中的每个可渲染对象都可以有某种索引,而不是四个浮动,它将其作为属性传递给顶点着色器。顶点着色器使用索引查找VBO中的四个纹理坐标。这是可行的解决方案吗?您将如何实现这样的功能
但至于四个顶点,我迷路了。它们会不断地四处移动。它们将是可见的,然后消失,等等。这意味着我的四个VBO将在每次渲染调用时改变,我看到的更新VBO的代码非常难看。我见过这样的情况:
- 将4个四元坐标存储在一个数组中李>
- 创建一个floatbuffer,把它们放在那里
- 操纵缓冲区李>
- 将缓冲区发送到VBO
我觉得很贵。我不知道如何删除某个条目(如果实体移出屏幕等),也不知道如何操作某个条目(实体移动)。如果每次渲染调用都必须以这种方式更新VBO,那么性能提高了多少?看起来更像是我的损失
此外,我如何跟踪生成图像的“深度”。我在做2d,但“深度”是指渲染的顺序,例如确保object2在object1的顶部渲染。每个深度可能有不同的VBO?或者我应该用z坐标来表示这个和enbale深度。后者不会给表演带来冲击吗
还有2d因素。我非常尊重3d,但我想使用2d,并利用它在理论上应该产生更好的性能这一事实。然而,据我所知,情况似乎并非如此。在OpenGL3+中,为了渲染2d内容,我需要首先将其转换为3d,因为这是硬件中的处理过程。我觉得很奇怪,因为屏幕上的最终结果是2d。有没有办法绕过这一点,让GPU不用再做2d->;3d->;2d
换言之,我如何才能改变这一点:
class main{
void main(){
while(true){
Renderer.bind();
//call render in all gameObjects
Renderer.flush();
}
}
}
class GameObject{
private float X1, X2, Y1, Y2;
private TexureCoordinate tex;
render(float dt){
//update X1, X2...
Renderer.render(tex.getX1(), tex.getX2()... X1, X2 ...);
}
}
class Renderer{
//called once
void bind(Texture texture){
texture.bind();
glBegin(GL_TRIANGLES)
}
//called "nr of visable objects" times
void render(texX1, texX2, texY1, texY2, quadX1, quadX2, quadY1, quadY2){
glTexCoo2d(texX1, texY1)
....
etc.
....
}
void flush(){
glEnd();
}
}
变成使用现代openGl的东西
# 1 楼答案
第一个也是最重要的关键洞察是,顶点不仅仅是位置。顶点是在调用glVertex之前,用于在即时模式下预设图形调用的整个属性元组。如果只更改其中一个属性,则最终会得到一个非常不同的顶点
让我们从VBOs中退一步,把整个glBuffer[Sub]数据放在一边,看看普通的旧客户端顶点数组(大约与即时模式一样长)
假设有两个位置数组,它们的布局完全相同,但值不同:
除了它们的值之外,它们的布局是相同的:四个连续的2元素属性。这样就可以使用公共纹理坐标数组,匹配这两个四边形的布局:
我认为,如何使用立即模式调用来绘制
quad_pos_a
和quad_pos_b
共享quad_texc
,这对您来说应该是显而易见的。如果不是很明显,现在是解决问题的时候了。这个答案是耐心的,会一直等到你做完间歇
…由于将几何数据放入数组是一件显而易见的事情,OpenGL很快引入了一个名为顶点数组的概念:您可以告诉OpenGL从何处获取顶点数据,然后告诉它要绘制多少个顶点,或者从给定索引列表的数组中选取哪些顶点
使用VAs如下所示:
或者,如果您只想绘制第0、第1和第3个顶点的三角形:
现在,普通的旧顶点数组和VBOs之间的关键区别在于,VBOs将数据置于OpenGL的监护之下——这就是全部。如果您了解VAs,您将了解VBO。但是,与VAs不同,您无法轻松更改VBO的内容。与着色器的区别在于,不再预定义属性的类型。而是使用
glEnableVertexAttribArray
(而不是glEnableClientState
)和glVertexAttribPointer
设置通用顶点属性那么,如何节省上传更新数据的开销呢?这取决于你认为昂贵的东西:数据最终必须进入GPU。因此,将其打包到合并的缓冲区数据上传传输中可能是有益的,因为这样可以节省每个glVertex调用的每次调用开销