java - OpenGL > v3,2d游戏高效渲染

标签 java opengl game-engine

我对 VBO 和现代 openGL 感到困惑。这篇文章的末尾有一个直接的问题,然后有一大堆问题正在那里。如果您对此有任何了解,我将不胜感激。如果你回复的话,请把我当成一个彻头彻尾的白痴,没有任何知识。

所以,我的历史是这样的:

我有一个游戏,它是一个自上而下的 2d 游戏。我使用 immideate 模式来渲染 2d Sprite 。我的纹理图集的实际纹理坐标是静态的,并在单独的类中预定义。四边形坐标在每个实体中定义并随着游戏的进展而更新。当我渲染时,我只是绑定(bind)一个特定的纹理,称为 glBegin(triangles),然后调用每个可见对象的渲染方法。这又将四边形坐标和纹理坐标发送到我的 Renderer 类,该类进行了 openGl 调用。然后我刷新了纹理,它只调用 glEnd()。

我为所有不同的 map 集并按顺序执行了此操作,以便获得适当的深度。

但时代确实在变化。我想转而使用 VBO 和着色器。过去我曾多次尝试过,但都惨遭失败。我在谷歌上找不到一些东西来让我完全理解它,以及如何使用它来加速我的游戏。

我了解基础知识。我可以简单地存储初始化阶段所需的所有信息,然后使用着色器计算最终结果,而不是在每次渲染调用时通过总线将所有信息发送到 GPU。但是...

我对纹理坐标有了一个想法。这些将是静态的,因为它们永远不会改变。将它们存储在 GPU 上是有意义的。但我如何知道每个四边形/三角形对应哪些坐标。我认为游戏中的每个可渲染对象可以具有某种索引,而不是四个 float ,并将其作为属性传递给顶点着色器。顶点着色器使用索引来查找 VBO 中的四个纹理坐标。这是一个可行的解决方案吗?你会如何实现这样的事情?

但是至于四边形顶点我迷失了。这些将不断移动。它们将是可见的,然后消失,等等。这意味着我的四 VBO 将在每次渲染调用时发生变化,并且我看到的更新 VBO 的代码非常难看。我见过类似的东西:

  • 将 4 个四边形坐标存储在数组中。
  • 创建一个 float 缓冲区,将它们放入其中
  • 操纵缓冲区。
  • 将缓冲区发送到 VBO

对我来说看起来相当昂贵。而且我不明白如何删除某个条目(如果实体移出屏幕等),也不明白如何操纵某个条目(实体移动)。如果我必须在每次渲染调用时都以这种方式更新 VBO,那么性能增益是多少?对我来说看起来更像是一种损失......

另外,我如何跟踪结果图像的“深度”。我正在做 2d,但“深度”是指渲染的顺序,例如确保 object2 渲染在 object1 之上。也许每个深度都有不同的 VBO?或者我应该使用 z 坐标来处理这个和 enbale 深度的东西。后者不会影响性能吗?

还有二次因子。我非常尊重 3d,但我想使用 2d 并利用理论上它应该产生更好性能的事实。然而,从我收集到的情况来看,情况似乎并非如此。在 opengl 3+ 中,似乎为了渲染 2d 的东西,我需要首先将其转换为 3d,因为这就是硬件中的进程。我觉得很奇怪,因为屏幕上的最终结果是二维的。有没有办法绕过这个问题,并节省 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 的东西?

最佳答案

第一个也是最重要的关键见解是,顶点不仅仅是位置。顶点是用于在调用glVertex之前在立即模式绘图调用中预设的属性的整个元组。如果您只更改其中一个属性,您最终会得到一个非常不同的顶点。

让我们暂时从 VBO 中退一步,将整个 glBuffer[Sub]Data 移开,看看普通的旧客户端顶点数组(大约与即时模式一样长)。

假设您有两个位置数组,它们具有完全相同的布局,但具有不同的值:

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 元素属性。这简单地允许使用公共(public)纹理坐标数组,匹配这两个四边形的布局:

GLfloat quad_texc[2][4] = {
  {0,0},{1,0},{1,1},{0,1}
};

我认为您应该很清楚如何使用立即模式调用来绘制 quad_pos_aquad_pos_b 共享 quad_texc。如果它还不明显,现在是时候解决它了。这个答案很耐心,会等到你完成......

<小时/>

中场休息

<小时/>

…由于将几何数​​据放入数组是显而易见的事情,OpenGL 很快就引入了一个名为顶点数组的概念:您可以告诉 OpenGL 从哪里获取顶点数据,以及然后告诉它有多少个顶点要绘制,或者从给定索引列表的数组中挑选哪些顶点。

使用 VA 如下所示:

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 的保管下 - 仅此而已。如果您了解 VA,那么您就了解了 VBO。然而,与 VA 不同,您无法轻松更改 VBO 的内容。与着色器的区别在于,不再预定义属性类型。相反,有通用顶点属性,使用 glEnableVertexAttribArray(而不是 glEnableClientState)和 glVertexAttribPointer 设置。

那么如何节省上传更新数据的开销呢?嗯,这取决于您认为昂贵的是什么:数据最终必须进入 GPU。因此,将其打包到合并的缓冲区数据上传传输中可能是有益的,因为它节省了每次调用的开销以对每个 glVertex 调用进行分块。

关于java - OpenGL > v3,2d游戏高效渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29726811/

相关文章:

swift - 使用 Swift 创建平铺的十六进制游戏

java - javafx 中使用了什么 javascript 引擎?

java - java中如何判断谁将文件上传到ftp?

java - 并发数组列表

c++ - 帮助 Try Catch

opengl - 对度数和 OpenGL/GLUT 相机移动/旋转感到困惑

java - 获取静脉中所有相似的 block

c++ - 优化 SphereInFrustrum 检查

ubuntu - 如何在 ubuntu 15.10 上安装 unity3d

c++ - 如何在 map 上找到所有的森林簇?