我对VBO和现代openGL感到困惑。在这篇文章的最后有一个直接的问题,然后在那里有一捆它们。如果您对此有任何疑问,我会非常感谢您的回复。如果你回复,请把我视为一个完全白痴,不知何处。
所以,我的历史就是这样:
我有一款游戏,这是一款自上而下的2D游戏。我使用了immideate模式来渲染2d精灵。我的纹理地图集的实际纹理坐标是静态的,并在单独的类中预定义。四元坐标在每个实体中定义,并随着游戏的进行而更新。当我渲染时,我只需绑定一个特定的纹理,称为glBegin(三角形),然后调用每个可见对象渲染方法。这又将quad坐标和texure坐标发送到我的Renderer类,后者进行openGl调用。然后我刷新纹理,只调用glEnd()。
我为所有不同的地图集和顺序做了这个,以便我得到适当的深度。
但时代确实发生了变化。我想转而使用VBO和着色器。我过去曾多次尝试,但失败了。我只能通过谷歌找到一些东西让我对它有一个完整的了解,以及如何使用它来加速我的游戏。
我知道基础知识。我可以简单地存储我在启动阶段需要的所有内容,然后使用着色器来计算最终结果,而不是通过总线将所有信息发送到每个渲染调用的gpu。但...
我对纹理坐标有所了解。这些将是静态的,因为它们永远不会改变。它会将它们存储在GPU上。但是我怎么知道哪个坐标对应于每个QUAD / TRIANGLE。我认为游戏中的每个可渲染对象不是四个浮点数,而是可以有某种索引,它作为属性传递给顶点着色器。顶点着色器使用索引来查找VBO中的四个纹理坐标。这是一个可行的解决方案吗?你会如何实现这样的东西?
但至于四肢椎体我输了。这些将不断移动。它们将是可见的,然后消失,等等。这意味着我的四维VBO将在每次渲染调用时改变,而且我已经看到更新VBO的代码非常难看。我见过类似的东西:
对我来说看起来很贵。而且我不明白如何删除某个条目(如果一个实体移出屏幕等),也不知道我如何操纵某个条目(一个实体移动)。如果我必须以这种方式更新VBO每个渲染调用,性能增益是什么?看起来更像是对我的损失...
另外,我如何跟踪"深度"得到的图像。我做2d,但是"深度"我指的是渲染的顺序,例如确保object2呈现在object1的ontop上。每个深度的VBO可能不同?或者我应该使用z坐标这个和enbale深度的东西。后者不会给出一个表现吗?
还有2d因素。我非常尊重3d,但我想使用2d并利用它在理论上应该产生更好性能的事实。但是,从我收集的内容来看,情况似乎并非如此。在opengl 3+中它表明,为了让我渲染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的东西?
答案 0 :(得分:3)
第一个也是最重要的关键见解是,顶点不仅仅是位置。顶点是在调用 glVertex 之前用于在立即模式绘制调用中预设的整个属性元组。如果只更改其中一个属性,则最终会得到一个非常不同的顶点。
让我们从VBO退一步,让整个glBuffer [Sub]数据不受影响,并查看普通的旧客户端顶点数组(大约与立即模式一样长)。
假设您有两个位置数组,它们的布局完全相同,但值不同:
GLfloat quad_pos_a[2][4] = {
{1,2}, {2,2}, {2,3}, {1,3}
};
GLfloat quad_pos_b[2][4] = {
{5,5}, {10,5}, {10,20}, {5,20}
};
除了它们的值之外,它们的布局是相同的:连续四个2元素属性。这通常允许使用常见的纹理坐标数组,匹配这两个四边形的布局:
GLfloat quad_texc[2][4] = {
{0,0},{1,0},{1,1},{0,1}
};
我认为对您来说很明显,如何使用即时模式调用来绘制quad_pos_a
和quad_pos_b
共享quad_texc
。如果不明显,现在是时候解决了。这个答案很耐心,会等到你完成......
在间歇
...因为将几何数据放入数组是一件很明显的事情,OpenGL很快就引入了一个名为顶点数组的概念:你可以告诉OpenGL从哪里获取顶点数据和然后告诉它要绘制多少顶点,或者给出一系列索引从阵列中挑选哪些顶点。
使用VAs看起来像这样:
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(
2 /* = number of elements per attribute */,
GL_FLOAT /* type of attribute elements */,
0 /* = the byte distance between attributes OR zero if tightly packed */,
quad_pos_a );
glTexCoordPointer(
2 /* = number of elements per attribute */,
GL_FLOAT /* type of attribute elements */,
0 /* = the byte distance between attributes OR zero if tightly packed */,
quad_texc );
glDrawArrays(
GL_QUADS /* what to draw */,
0 /* which index to start with */,
4 /* how many vertices to process*/ );
或者如果您只想绘制第0个,第1个和第3个顶点的三角形:
GLushort indices[] = {0,1,3};
glDrawElements(
GL_TRIANGLES /* what */,
3 /* how many */,
GL_UNSIGNED_SHORT /* type of index elements */,
indices );
现在普通旧顶点数组和VBO之间的关键区别在于,VBO将数据置于OpenGL的监管之下 - 这完全是关于它的。如果您已经了解VOA,那么您就无法体验VBO。然而,与VA不同,您无法改变VBO'内容如同毫不费力。着色器的不同之处在于,不再预定义属性类型。而是存在通用顶点属性,使用glEnableVertexAttribArray
(而不是glEnableClientState
)和glVertexAttribPointer
进行设置。
那么如何节省上传更新数据的开销呢?嗯,这取决于你认为昂贵的东西:数据最终必须转向GPU。因此,将其打包到合并缓冲区数据上传传输中可能是有益的,因为它可以节省每个调用开销,以便对每个glVertex调用进行分块。