OpenGL ES 2.0和动态VBO的含义是什么?

3
我已经浏览了现有的主题并进行了一些测试,但我不确定自己是否正确地做了这件事,因此我宁愿提出一个问题,而不是被卡在多个OpenGL ES 2.0教程中花费更多时间弄清楚自己误解了什么。
我按照“OpenGL ES 2 for Android - A Quick Start Guide”中所述实现了VBO,现在需要一个动态VBO,每帧都要用一些矩阵计算(这可能是我错了的地方)对模型顶点进行更新。在这些计算之后,我想使用以下Android/Java代码将其传输(更新)到已分配的VBO(使用glBufferData)。
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.id);

    //GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, this.getLength() * Utils.BYTES_PER_FLOAT, null, GLES20.GL_DYNAMIC_DRAW);

    /* CREATE BUFFER AND SET POSITION */
    FloatBuffer fBuffer = ByteBuffer.allocateDirect((this.length = vertices.length) * Utils.BYTES_PER_FLOAT)
            .order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertices);
    fBuffer.position(0);

    GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, fBuffer.capacity(), fBuffer);

    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
  • this.id - 先前生成缓冲区的ID
  • this.getLength - 最初分配的 FloatBuffer 的长度

您可能已经注意到,我已经注释掉了其他问题中提到的那行代码,因为保留它会导致屏幕上出现闪烁的对象,我无法正确理解。以这种方式进行一切工作得很好,但我注意到在运行应用程序时会出现一些 GC_FOR_ALLOC,这使我确信它可能不是有效的代码。

问题 - 或多或少 - 是:

  • 更新此类缓冲区是否是有效方法,或者我可以获得如何在我提供的情况下(OpenGL ES 2.0、Android 4+)正确执行它的建议?

子问题包括:

  • 在动态模型中使用 VBO 方法是否比在这些情况下使用 VAO 更好,或者我应该实现静态的 VBO 方法和动态的 VAO 方法?
  • 仅查看 GC_FOR_ALLOC 调试信息,何时应开始担心内存问题?
  • (一个有点超出话题的子问题)使用正交等距视图矩阵(正交 + 2x 旋转 + Z-平移),对于应用程序创建 GUI 的适当方法是什么?

编辑(27.12):

为了实现等距投影矩阵,我只需使用以下行:

    Matrix.orthoM(this.orthoProjectionMatrix, 0, -screenAspect, screenAspect, -1.0f, 1.0f, -1.0f, 10.0f);
    Matrix.translateM(this.viewProjectionMatrix, 0, this.orthoProjectionMatrix, 0, 0.0f, 0.0f, -5.0f);
    Matrix.rotateM(this.viewProjectionMatrix, 0, 45, 1.0f, 0.0f, 0.0f);
    Matrix.rotateM(this.viewProjectionMatrix, 0, 45, 0.0f, 1.0f, 0.0f);

简单来说:

  • 根据屏幕的纵横比创建正交的肖像投影
  • 仅在 Z 轴上移动(以便从远处看到场景)
  • 将 X 轴旋转 45 度
  • 将 Y 轴旋转 45 度

有些元素已经组合在一起了,所以请不要考虑一些翻译是就地进行的,而另一些是针对另一个矩阵的结果。


2
你在问题中描述的让我有些担忧。你正在使用OpenGL ES 2.0,但是你谈论的是对存储在VBO中的顶点应用矩阵变换。如果你可以访问着色器,通常不会这样做。你会将要转换顶点的矩阵传递给顶点着色器,并在每次绘制时应用该变换。你的顶点缓冲区数据实际上是静态的,而是顶点着色器完成所有工作(重复)。从听起来的情况来看,你可以在矩阵中累积变换,而不是在顶点缓冲区中。 - Andon M. Coleman
我已经解决了这个问题。请查看下面我的评论之一。这确实是一种无效的方法。现在,我在渲染场景或HUD时,在统一矩阵之间切换。关于你刚才说的-每帧应用一些变换时-我是否应该始终操作传递给着色器的变换矩阵,还是对模型顶点进行操作并具有动态VBO更有效?我找不到任何涉及OpenGL ES 2.0(Android)并以可理解的方式描述它的书籍/材料。我感到困惑。 - Łukasz Modliński
1
除非必须更改顶点缓冲区中的顶点,否则您永远不希望这样做。如果您将顶点缓冲区视为存储在GPU内存中,则希望每帧从CPU传输尽可能少的数据到GPU。更新矩阵(16个浮点数)几乎总是比更新每个顶点的数据量要小。因此,如果您想在每个帧中以不同的角度旋转相同的模型,请更新矩阵并让顶点着色器执行实际的变换。动态VBO更适合像“有状态”的粒子系统之类的东西,这些系统依赖于先前的顶点着色器输出。 - Andon M. Coleman
经过又两天的开发,我得出了一个结论:在这些评论中,我找到了我一直在寻找的答案。然而,对我来说,这似乎有点不合理。让我解释一下为什么我有这些疑虑。考虑以下情况:1)一个对象位于(0, 0, 0) 2)一个对象正在被向量(1, 0, 0)移动我应该选择: A)将viewProjectionMatrix进行平移,使其按照(-1, 0, 0)进行转换,然后传递给着色器 B)更新包含该对象顶点的VBO。对于这个例子的回答应该能够澄清我的看法。 - Łukasz Modliński
1个回答

4

前言:我对OpenGL ES不熟悉,只了解常规的OpenGL。然而,我无法想象API有太大的区别。

更新这样的缓冲区是否是有效的方法?

除非fBuffer始终小于或等于OpenGL缓冲区的大小,否则不是有效的方法。glBufferSubData不会调整缓冲区的大小,并且不允许写入超出分配范围之外的内容。您可以使用glBufferData将缓冲区重新分配为新的大小。还要查看缓冲对象流

在动态模型中使用VBO的方法比在这些情况下使用VAO的方法更好,还是应该实现VBO方法用于静态和VAO方法用于动态?

VBO和VAO是正交的概念。VBO存储顶点数据;VAO只是指定从哪里获取顶点以及如何呈现。

使用正交等距视图矩阵(正交+ 2x旋转+ Z-平移),为应用程序创建GUI的适当方法是什么?

放弃Z轴平移,而是最后使用禁用深度缓冲区的方式呈现GUI。此外,旋转是用来做什么的?

  1. (根据缓冲区大小)我想要摆脱所有以前的数据并发送一个相同大小的新批次。简单来说 - 我想用一些计算更新旧的缓冲区数据。我觉得创建新的缓冲区是不必要的,但我没有找到提供另一种解决方案的答案。
  2. (根据VBO / VAO)好的。我会阅读一些关于这个主题的其他材料,因为似乎我没有正确理解它。
- Łukasz Modliński
  1. (指等轴投影)我已经掌握了一个更好的方法:使用等轴投影,绘制场景对象,切换到简单正交投影,绘制HUD。它似乎工作得非常好(HUD保持在相同位置,在我拖动时场景移动如预期)。
- Łukasz Modliński
1
如果上传的数据大小相同,则可以使用glBufferSubData进行写入。只有在需要调整缓冲区大小(或者放弃旧缓冲区;请参见缓冲区流链接)时才需要使用glBufferData。将glBufferSubData视为memcpy,将glBufferData视为realloc - Colonel Thirty Two
当然可以,但我更倾向于省略创建新的FloatBuffer - 我应该保留已初始化glBufferData的那个吗?我相信这种更新方式会导致最近我注意到的GC_FOR_ALLOC's(禁用更新进行了测试)。 - Łukasz Modliński
1
@ŁukaszModliński 你可以保留缓冲区。如果在ES中存在,你也可以使用glMapBuffer将缓冲区映射到内存中。 - Colonel Thirty Two
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接