更新着色器制服的最佳方法是什么?


10

使着色器矩阵保持最新状态的最常用方法是什么?为什么?

例如,目前我有一个Shader类,用于存储GLSL着色器程序和统一体的句柄。每次移动摄像机时,我都必须将新的视图矩阵传递给着色器,然后我必须将每个不同的世界对象的模型矩阵传递给着色器。

这严重限制了我,因为如果不访问该着色器对象就无法执行任何操作。

我想到创建一个ShaderManager负责容纳所有活动着色器的单例类。然后,我可以从任何地方访问该对象,世界对象不必知道什么着色器处于活动状态,只需要ShaderManager知道所需的矩阵即可,但是我不确定这是最好的方法,并且可能存在一些问题将采取这种方法。


为什么在渲染时不将数据传递给着色器?
Nick Caplinger

那就是我的打算,但是如何?如果一个对象创建了一个着色器,那么世界对象应该如何知道该着色器的存在并将其传递给必要的矩阵?
Lerp 2013年

Answers:


11

使用统一缓冲区(即D3D语言中的常量缓冲区)。

只要您所有的着色器都同意每个这样的缓冲区的布局和绑定点,那么更新就变得轻而易举。模型完全不需要了解着色器。他们只需要在其常量缓冲区中更新模型视图矩阵,渲染管道就会自动使用它。

至少我认为您应该有两个这样的缓冲区。第一个应该存储您的投影矩阵,摄影机矩阵,串联的摄影机-投影矩阵,视口信息,平截头体细节和矩阵逆。每个场景只需更新一次该缓冲区。

然后为每个模型提供另一个缓冲区,以存储其模型视图矩阵,法线矩阵,逆和材质属性。每个模型都会对此进行一次更新,并且可以通过不同的更新过程(如果适用)来完成。如果您能够在多个对象之间共享材料,则可以/应该将材料信息移至第三个特定于材料的缓冲区。

在前向阴影设置中,将所有光源放置在另一个缓冲区中是有意义的,而在延迟阴影中,同样使用每个光源的缓冲区进行光照是有意义的(代替模型中使用的模型/材质缓冲区)。几何传递)。

请注意,您需要一个适度最新的GL版本,才能完全使用统一的缓冲区(3.1或扩展名;除了一些较旧但仍在使用中的笔记本电脑外,今天已经足够通用),并且需要一个相当新的版本能够从着色器代码内部将统一的缓冲区绑定到特定的绑定位置(4.2;仍然不常见,但是会变得更好),否则您必须在CPU端代码中做更多的工作来进行设置(您的CPU代码需要知道正确的绑定)还是要指出点,所以它更多的是API气味,而不是严重的问题)。不幸的是,OpenGL | ES直到3.0才添加统一缓冲区,但大多数流行的移动平台仍不支持该缓冲区。

如果没有选择缓冲区,那么您将需要一些全局位置来存储活动着色器的索引位置。您可以glGetUniformLocation在加载着色器后使用它来查找知名名称(如ModelViewMatrix或类似名称)的索引,然后存储这些索引。您的渲染器可以映射枚举值,例如MODEL_VIEW传递给SetUniform包装函数的值,以查看绑定的着色器,找到索引并glUniform正确调用。客户端代码对缓冲区的使用并没有特别大的改变,除非您将每个统一包装得很好,否则需要分别设置每个统一。

请参见GLSL接口块统一缓冲区对象


我知道他们会是一种内置的方式。谢谢!
Lerp 2013年

4

最简单的方法是标准化着色器之间的统一名称,并在绘制之前直接发送所有数据。开销不是很疯狂,但是您可以稍后对其进行优化,以免总是发送更新较少的制服。

我在游戏中执行此操作的方法是拥有一个抽象Material类。材质充当游戏和着色器之间的桥梁。每个Material都具有Shader和可以设置的各种属性,包括纹理。当需要绘制一个对象时,该对象Material已绑定。该Bind方法的GraphicsState参数包含所有当前图形状态-矩阵,灯光等。该Bind方法绑定着色器和纹理,并使用所需的所有设置统一GraphicsState

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.