面向简单的基于着色器的 GL 程序的 OO 架构

Posted

技术标签:

【中文标题】面向简单的基于着色器的 GL 程序的 OO 架构【英文标题】:OO architecture for simple shader-based GL program 【发布时间】:2017-01-17 17:55:03 【问题描述】:

我正在设计一个 OO 程序(在 C++ 中),它处理用 OpenGL 实现的中等简单图形。我为我的Drawable 对象编写了几个顶点着色器程序。此外,我将着色器管理(编译、链接和使用)封装在 Shader 类中。

我的问题是,因为我所有的类都将使用这一小组着色器来渲染视觉对象,并且它们都有一个指向 Shader 对象的指针,所以提供一个通用引用是否有意义> 供所有人使用,这样就可以避免多次编译“相同”的着色器代码?

如果是这样,为什么?防止着色器代码重复真的很重要吗? (我的程序可能会有数千个需要一起渲染的独立视觉元素)。我是 OpenGL 的新手,性能/效率问题对我来说仍然很模糊......

编辑:此外,我想知道我的着色器制服会发生什么;它们也会被共享吗?这应该怎么让我,例如以不同的速度旋转我的元素?每次我想绘制每个元素时都编写元素统一(即模型矩阵)比复制着色器代码更好吗?

【问题讨论】:

你绝对不应该复制着色器。唯一需要复制的是 VAO 和 VBO。但是既然你已经说过 Drawable 有一个指向 Shader 的指针,我看不出为什么多个 Drawable 不应该指向同一个着色器对象。 每个元素都需要其独立(即不共享)的模型矩阵这一事实吗?还是制服的价值观是 VAO 的一部分? 制服是着色器的一部分。但是在着色器中更新统一变量比切换着色器要快得多。 【参考方案1】:

我敢打赌,在大多数(如果不是全部)OpenGL 实现中,多次编译和链接同一个着色器会导致着色器二进制文件的多个副本和制服空间等。调用 glUseProgram 在重复副本之间切换仍然会导致尽管在调用之前和之后在您的 GPU 内核上运行相同的代码,但状态发生了变化。对于足够复杂的场景,您可能还会切换纹理,因此无论如何都会发生状态变化。

它可能不是你的瓶颈,但它肯定是浪费。对于着色器和纹理等静态内容,一个好的模式是拥有一个或多个 Manager 类(AssetManagerTextureManager 等),它们将延迟加载(或预加载)您的所有资产并为您提供当您请求共享指针(或其他一些内存管理策略)时,通常通过某个字符串 ID。

关于编辑:

是的,您的制服将被共享,并且还将remain loaded after you unbind。这是首选的方法,因为updating a uniform is more than an order of magnitude faster than binding a new shader。您只需为每个新对象设置模型矩阵制服,但保持相同的着色器。

制服与着色器一起存储,因此切换着色器意味着无论如何都要加载所有制服。

【讨论】:

正如我刚刚在(最近编辑的)问题中提到的,如果我使用一个共享的Shader 参考,制服会被共享吗? 更新了我的答案

以上是关于面向简单的基于着色器的 GL 程序的 OO 架构的主要内容,如果未能解决你的问题,请参考以下文章

为啥 gl_Color 不是片段着色器的内置变量?

使用相同的着色器处理 GL_RGBA 和 GL_ALPHA 纹理?

OpenGl,glUseProgram() 返回 GL_INVALID_VALUE 即使着色器的 ID 是正确的 [重复]

绘制简单物体总结

OpenGL 程序着色器链接错误

openGL:带着色器的线条