有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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) 个答案

  1. # 1 楼答案

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

    让我们从VBOs中退一步,把整个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_aquad_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 );
    

    现在,普通的旧顶点数组和VBOs之间的关键区别在于,VBOs将数据置于OpenGL的监护之下——这就是全部。如果您了解VAs,您将了解VBO。但是,与VAs不同,您无法轻松更改VBO的内容。与着色器的区别在于,不再预定义属性的类型。而是使用glEnableVertexAttribArray(而不是glEnableClientState)和glVertexAttribPointer设置通用顶点属性

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