我一直在阅读this OpenGL4.1新功能评论。我真的不明白GL_ARB_separate_program_objects使用背后的想法,至少基于帖子作者的说法:
它允许独立使用着色器阶段而无需更改其他阶段 着色器阶段。我看到两个主要原因:Direct3D,Cg甚至是 旧的OpenGL ARB程序可以做到,但更重要的是它带来了一些 软件设计灵活性允许在图形管道中查看 较低的粒度。例如,我最好的敌人VAO,是一个 容器对象链接缓冲区数据,顶点布局数据和GLSL 程序输入数据。没有专门的软件设计,这意味着 当我更改对象的材质(一个新的片段着色器)时, 我需要不同的VAO ......幸运的是,保持相同的VAO是可能的 并且只通过定义如何的约定来改变程序 在C ++程序和GLSL程序之间进行通信。有用 即使有一些缺点,也是如此。
现在,这一行:
例如,我最好的敌人VAO是一个容器对象,它链接缓冲区数据,顶点布局数据和GLSL程序输入数据。没有专门的软件设计,这意味着当我更改材料时一个对象(一个新的片段着色器),我需要不同的VAO ......
让我感到惊讶。在我的OpenGL程序中,我使用VAO对象,我可以在不同的着色器程序之间切换而不对VAO本身进行任何更改。所以,我是否误解了整个想法?也许他意味着我们可以在不重新链接的情况下为同一个程序切换着色器?
答案 0 :(得分:10)
我将这个答案分成多个部分。
此功能的目的是能够轻松地在顶点/片段/几何/曲面细分着色器之间进行混合和匹配。
目前,您必须将所有着色器阶段链接到一个整体程序中。所以我可以使用相同的顶点着色器代码和两个不同的片段着色器。但这导致两个不同的程序。
每个程序都有自己的一套制服和其他状态。这意味着如果我想在顶点着色器中更改一些统一数据,我必须在两个程序中更改它。我必须在每个上使用glGetUniformLocation
(因为它们可能有不同的位置)。然后我必须单独设置每个值。
这是一个巨大的痛苦,而且非常不必要。使用单独的着色器,您不必这样做。你有一个只包含顶点着色器的程序,以及两个包含两个片段着色器的程序。更改顶点着色器制服并不需要两次glGetUniformLocation
次调用。实际上,缓存数据更容易,因为只有一个顶点着色器。
此外,它还涉及着色器组合的组合爆炸。
假设您有一个顶点着色器可以进行简单的刚性变换:它采用模型到相机矩阵和相机到剪辑矩阵。也许是法线的矩阵。你有一个片段着色器,它将从一些纹理中采样,根据法线进行一些光照计算,然后返回一种颜色。
现在让我们假设你添加另一个片段着色器,它需要额外的光照和材质参数。它没有来自顶点着色器的任何新输入(没有新的纹理坐标或任何东西),只有新的制服。也许它适用于投影光照,顶点着色器并不参与。不管。
现在让我们说我们添加一个新的顶点着色器,它进行顶点加权蒙皮。它提供与旧顶点着色器相同的输出,但它有一堆制服和用于蒙皮的输入权重。
这为我们提供了2个顶点着色器和2个片段着色器。共有4个程序组合。
当我们添加2个更兼容的片段着色器时会发生什么?我们得到8种组合。如果我们有3个顶点和10个片段着色器,我们有 30 总程序组合。
使用单独的着色器,3个顶点和10个片段着色器需要30个程序管道对象,但只需要 13 程序对象。程序对象比非单独案例少50%。
现在,这条线让我感到惊讶。
它会让你惊叹;它在几个方面是错误的。例如:
VAO是一个容器对象,它链接缓冲区数据,顶点布局数据和GLSL程序输入数据。
不,它没有。它将提供顶点数据的缓冲区对象与该数据的顶点格式联系起来。它指定了哪些顶点属性索引。但是这与GLSL程序输入数据的紧密耦合是什么?是完全由你决定。
如果没有专门的软件设计,这意味着当我更改对象的材质(新的片段着色器)时,我需要不同的VAO ......
除非这条线等同于专门的软件设计"通过"合理的编程实践",这是纯粹的废话。
这就是我的意思。您将在设置顶点数据时看到在线示例代码:
glBindBuffer(GL_ARRAY_BUFFER, buffer_object);
glEnableVertexAttribArray(glGetAttribLocation(prog, "position"));
glVertexAttribPointer(glGetAttribLocation(prog, "position"), ...);
这有一个技术术语:可怕的代码。这样做的唯一原因是prog
指定的着色器是否在某种程度上不在您的直接控制之下。如果是这样的话......你怎么知道prog
有一个名为" position"的属性? 根本?
着色器的合理编程实践是使用约定。这就是你如何知道prog
有一个名为" position"的属性。但是如果你知道每个程序都有一个名为" position"的属性,为什么不进一步呢?当需要链接程序时,请执行以下操作:
GLuint prog = glCreateProgram();
glAttachShader(prog, ...); //Repeat as needed.
glBindAttribLocation(prog, 0, "position");
毕竟,你知道这个程序必须有一个名为" position&#34 ;;当你以后到达它的位置时,你会假设它。因此,切断中间人并告诉OpenGL 使用什么位置。
这样,您就不必使用glGetAttribLocation
;你的意思是"位置"。
即使prog
没有名为" position"的属性,这仍然会成功链接。如果绑定不存在的属性位置,OpenGL并不介意。因此,您可以对您创建的每个程序应用一系列glBindAttribLocation
调用,而不会出现任何问题。实际上,您可以为您的属性名称设置多个约定,只要您坚持使用一组或另一组,您就可以了。
更好的是,将其粘贴在着色器中并且根本不需要使用glBindAttribLocation
解决方案:
#version 330
layout(location = 0) in vec4 position;
简而言之:始终对属性位置使用约定。如果您在程序中看到glGetAttribLocation
,请考虑代码味道。这样,您可以将任何VAO用于任何程序,因为VAO只是根据惯例编写的。
我不知道约会如何等同于专门的软件设计",但是,嘿,我也没有写过那条线。
答案 1 :(得分:4)
我可以在不同的着色器程序之间切换
是的,但您必须完全替换整个程序。单独的着色器对象允许您仅替换一个阶段(例如,仅替换顶点着色器)。
如果您有N个顶点着色器和M个顶点着色器,使用传统链接,您将拥有N * M个程序对象(以涵盖所有可能的组合)。使用单独的着色器对象,它们彼此分离,因此您只需要保留N + M着色器对象。这是复杂场景的重大改进。