【问题标题】:OpenGL ES 2.0 and Dynamic VBO'sOpenGL ES 2.0 和动态 VBO
【发布时间】:2014-12-27 01:04:47
【问题描述】:

我已经了解了现有的主题并自己进行了一些测试,但我不太确定我是否以正确的方式做这件事,所以我宁愿提出一个问题,也不愿花更多时间来弄清楚自己我对使用多个 OpenGL ES 2.0 教程的误解。

我已经实现了 VBO,因为它们在“适用于 Android 的 OpenGL ES 2 - 快速入门指南”中有所介绍,我发现我需要一个动态 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 度

有些元素是放在一起的,所以请不要考虑到有些翻译是就地的,有些是与另一个矩阵的结果。

【问题讨论】:

  • 您在问题中描述的内容与我有关。您正在使用 OpenGL ES 2.0,但您正在谈论将矩阵变换应用于存储在 VBO 中的顶点。如果您可以访问着色器,通常不会这样做。您可以将要转换顶点的矩阵传递给顶点着色器,并在每次绘制时应用该转换。您的顶点缓冲区数据实际上是静态的,它将是完成所有工作的顶点着色器(重复)。 从事物的声音来看,您可以在矩阵而不是顶点缓冲区中累积变换。
  • 我已经弄清楚了。在下面检查我的一个 cmets。这确实是一种无效的做法。我现在在渲染场景或 HUD 时在附加到着色器的统一矩阵之间切换。参考您刚才所说的 - 每当我应用一些每帧变换时 - 我是否应该始终对传递给着色器的变换矩阵进行操作,或者对模型顶点进行操作并且拥有动态 VBO 更有效?我无法找到任何引用 OpenGL ES 2.0 (Android) 并以平易近人的方式描述它的书籍/材料。我感到困惑。
  • 除非必须,否则您永远不想更改顶点缓冲区中的顶点。如果您将顶点缓冲区视为存储在 GPU 内存中,这可能会有所帮助,您希望在每帧中尽可能少地将数据从 CPU 传输到 GPU。更新矩阵(16 个浮点数)几乎总是比更新每个顶点的数据少。因此,如果您想每帧将同一模型旋转不同的量,请更新矩阵并让顶点着色器进行实际转换。动态 VBO 更适合诸如“有状态”粒子系统之类的东西,它依赖于以前的顶点着色器输出。
  • 经过两天的开发,我得出结论,在这些 cmets 中,我一直在寻找答案。它——无论如何——对我来说似乎有点不合理。让我解释一下为什么我有这些疑问。考虑一种情况: 1) 一个对象位于 (0, 0, 0) 2) 一个对象正被 vextor (1, 0, 0) 从它的位置移动我应该: A) 转换 viewProjectionMatrix 所以它由 (-1, 0, 0) 平移,然后将其传输到着色器 B) 更新包含此类对象顶点的 VBO。这个例子的答案应该让我的愿景变得清晰。

标签: java android opengl-es vbo


【解决方案1】:

(前言:我不熟悉OpenGL ES,只熟悉普通的OpenGL。但是,我无法想象API差异太大)

这是更新此类缓冲区的有效方法

除非fBuffer 总是小于或等于 OpenGL 缓冲区的大小,否则不会。 glBufferSubData 不会调整缓冲区的大小,并且不允许写入分配范围之外的内容。您可以使用glBufferData 将缓冲区重新分配到新的大小。另请查看buffer object streaming

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

VBO 和 VAO 是正交概念。 VBO 存储顶点数据; VAO 只指定渲染顶点的位置和方式。

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

删除 Z 平移,而是在禁用深度缓冲区的情况下最后渲染 GUI。还有,旋转是为了什么?

【讨论】:

  • 1)(根据缓冲区大小)我想删除所有以前的数据并发送一个相同大小的新批次。简单地说——我想用一些计算来更新旧的缓冲区数据。我觉得没有必要创建新缓冲区,但我还没有找到为我提供另一种解决方案的答案。 2) (根据 VBO/VAO)OK。我将通过一些关于这个主题的其他材料,因为我似乎没有正确理解它。
  • 3)(指等距投影)我已经设法掌握了一种更好的方法,即:使用等距投影,绘制场景对象,切换到简单的正交投影,绘制 HUD。而且它似乎工作得很好(HUD 保持在同一个地方,在场景上方,而当我拖动时场景正在移动)
  • @ŁukaszModliński 如果上传的数据大小相同,则可以使用glBufferSubData 写入。如果您需要调整缓冲区大小(或孤立旧缓冲区;请参阅缓冲区流链接),您只需要使用glBufferData。将glBufferSubData 视为memcpy,将glBufferData 视为realloc
  • 当然可以,但我宁愿考虑省略创建新的FloatBuffer - 我应该保留我已经初始化glBufferData 的那个,还是这种创建方式是一种有效的方式?我相信这种更新会导致我最近注意到的GC_FOR_ALLOC's(测试它会禁用更新)
  • @ŁukaszModliński 您可以保留缓冲区。您还可以使用glMapBuffer 将缓冲区映射到内存(如果 ES 中存在)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-08
相关资源
最近更新 更多