【问题标题】:What is the correct sequence for uploading a uniform block?上传统一块的正确顺序是什么?
【发布时间】:2019-06-27 21:27:39
【问题描述】:

https://www.lighthouse3d.com/tutorials/glsl-tutorial/uniform-blocks/ 的示例页面中有这样的内容:

  1. uniformBlockBinding()
  2. bindBuffer()
  3. bufferData()
  4. bindBufferBase()

但从概念上讲,这不是更正确吗?

  1. bindBuffer()
  2. bufferData()
  3. uniformBlockBinding()
  4. bindBufferBase()

这个想法是上传到缓冲区 (bindBuffer+bufferData) 应该不知道缓冲区将用于什么 - 然后,uniformBlockBinding()+bindBufferBase() 将分别用于更新这些制服,每个着色器,当相关缓冲区发生变化时?

【问题讨论】:

  • 您应该选择标记 OpenGL 或 WebGL。答案会有所不同。 OpenGL 支持映射缓冲区,而 WebGL 不支持(因为它不安全),因此更新统一块缓冲区的方式可能会有很大不同。此外,OpenGL 是一个 C api,而 WebGL 是一个 JavaScript API,因此更新数据的方法大不相同。在 OpenGL 中,您可以声明一个与您的统一块数据匹配的结构。在 JavaScript 中你不能,你必须以非常不同的方式来做。正如您接受的答案所暗示的那样,WebGL 也不支持在 GLSL 中设置绑定索引。

标签: webgl webgl2


【解决方案1】:

添加答案,因为接受的答案有很多与 WebGL2 无关的信息

在初始化时,您调用uniformBlockBinding。对于给定的程序,它设置特定程序将从哪个统一缓冲区索引绑定点获取特定的统一缓冲区。

在渲染时调用bindBufferRangebindBufferBase 将特定缓冲区绑定到特定统一缓冲区索引绑定点

如果您还需要将新数据上传到该缓冲区,则可以调用 bufferData

在伪代码中

// at init time

for each uniform block
   gl.uniformBlockBinding(program, indexOfBlock, indexOfBindPoint)

// at render time

for each uniform block
   gl.bindBufferRange(gl.UNIFORM_BUFFER, indexOfBindPoint, buffer, offset, size)
   if (need to update data in buffer)
      gl.bufferData/gl.bufferSubData(gl.UNIFORM_BUFFER, data, ...)

请注意,没有“正确”的顺序。这里的问题是如何更新缓冲区完全取决于您。由于您可能将多个统一缓冲区数据存储在不同偏移量的单个缓冲区中,因此像上面那样调用 gl.bufferData/gl.bufferSubData 确实不“正确”,它只是 100 秒的一种方式。

WebGL2 (GLES 3.0 ES) 不支持接受的答案中提到的layout(binding = x)。 WebGL2中也没有glGenBuffers这样的东西

【讨论】:

  • 谢谢 - 将此更改为已接受的答案,尤其是鉴于已删除的“opengl”标签
  • 后续问题 - 但不确定它是否应该是它自己的 SO 帖子...是否可以/应该在初始化时绑定采样器位置?例如gl.uniform1i(u_sampler, bindPoint);
  • 是的,这应该是它自己的问题,但这是一个见仁见智的问题。 gl.uniform 设置程序状态,因此如果您想预先决定哪些采样器单元或纹理单元将用于特定程序,那么可以肯定的是,预先这样做是有意义的。我不使用采样器。我很少遇到需要使用具有不同采样器参数的纹理的情况。也许当我遇到这种情况时,我会开始使用它们。我并不是说它们不存在,只是至少在我参与的项目中很少见。
【解决方案2】:

两者都不比另一个“更正确”;他们都工作。但是如果你在谈论关注点分离,第一个更好地强调正确的分离。

glUniformBlockBinding修改程序;它不会影响缓冲区对象或上下文缓冲区状态的性质。事实上,无论如何,该调用甚至不应该在同一个函数中;它是程序对象设置的一部分。在现代 GL 教程中,他们会使用 layout(binding=X) to set the binding,因此该函数甚至不会出现。对于较旧的代码,应在创建程序后将其设置为 已知 的常量值,然后保持不变。

因此,在为缓冲区分配存储空间和将其绑定到索引绑定点以供使用之间调用函数会造成他们应该每帧调用glUniformBlockBinding 的印象,这是错误的印象。

说到错误的印象,glBindBufferBase 甚至不应该被称为那里。该代码的其余部分是缓冲区设置代码;它应该只在应用程序开始时完成一次。 glBindBufferBase 应该作为渲染过程的一部分调用,而不是设置过程。在一个好的应用程序中,该调用不应位于glGenBuffers 调用附近。

【讨论】:

  • 谢谢!而bindBufferBase() 仅在缓冲区更新时才需要调用,对吗? (并且更新缓冲区需要bindBuffer()+bufferData(),但不需要 genBuffer(),因为我们只是重新使用创建的缓冲区?)
  • 另外——为了方便起见,如果在着色器编译时只缓存一次索引+绑定点,而不是硬编码常量,那么查询和获取索引+绑定点是否很糟糕? (我知道每次都查找会很糟糕 - 但如果只是一次......)
  • WebGL2 / OpenGL ES 3.0 / GLSL ES 3.0 在 GLSL 中没有统一块的布局绑定设置,因此您必须调用 uniformBlockBinding
  • 至于在GLSL中设置还是使用glUniformBlockBinding设置,见仁见智。我是一个试图保持干燥(不要重复自己)的人,所以我会选择生成我的着色器,在这种情况下,可以在生成它们时使用决定绑定位置的代码,或者我会使用一些共享常量预处理器文件,或者我会选择使用glUniformBlockBinding 来执行此操作,从那时起,无论如何我都可以按名称集中管理位置,而不必在遇到冲突时手动重新编写一堆着色器。跨度>
猜你喜欢
  • 2015-05-05
  • 2012-04-13
  • 2017-06-28
  • 2019-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多