我正试图围绕 GLSL 中的着色器,我找到了一些有用的资源和教程,但我一直在为一些应该是基本和微不足道的东西而碰壁:我的片段着色器如何检索颜色当前片段?
您通过说 设置最终颜色gl_FragColor = whatever
,但显然这是一个仅输出值。如何获得输入的原始颜色,以便对其执行计算?那肯定是在某个地方的变量中,但如果有人知道它的名字,他们似乎没有在我到目前为止遇到的任何教程或文档中记录它,这让我很难过。
我正试图围绕 GLSL 中的着色器,我找到了一些有用的资源和教程,但我一直在为一些应该是基本和微不足道的东西而碰壁:我的片段着色器如何检索颜色当前片段?
您通过说 设置最终颜色gl_FragColor = whatever
,但显然这是一个仅输出值。如何获得输入的原始颜色,以便对其执行计算?那肯定是在某个地方的变量中,但如果有人知道它的名字,他们似乎没有在我到目前为止遇到的任何教程或文档中记录它,这让我很难过。
片段着色器接收gl_Color
和gl_SecondaryColor
作为顶点属性。它还获得四个不同的变量:gl_FrontColor
, gl_FrontSecondaryColor
, gl_BackColor
, 并且gl_BackSecondaryColor
可以写入值。如果您想直接通过原始颜色,您可以执行以下操作:
gl_FrontColor = gl_Color;
gl_FrontSecondaryColor = gl_SecondaryColor;
gl_BackColor = gl_Color;
gl_BackSecondaryColor = gl_SecondaryColor;
顶点着色器之后的管道中的固定功能会将这些固定在 [0..1] 范围内,并确定顶点是正面还是背面。然后它会像往常一样插入选择的(正面或背面)颜色。然后片段着色器将接收选择的、钳位的、插值的颜色为gl_Color
和gl_SecondaryColor
。
例如,如果您绘制了标准的“死亡三角形”,例如:
glBegin(GL_TRIANGLES);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, 0.0f, -1.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 0.0f, -1.0f);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3d(0.0, -1.0, -1.0);
glEnd();
然后是这样的顶点着色器:
void main(void) {
gl_Position = ftransform();
gl_FrontColor = gl_Color;
}
使用这样的片段着色器:
void main() {
gl_FragColor = gl_Color;
}
将传输颜色,就像您使用固定功能管道一样。
如果您想进行多通道渲染,即如果您已经渲染到帧缓冲区并想要使用前一个渲染的第二个渲染通道,那么答案是:
3.2 的着色器代码:
uniform sampler2D mytex; // texture with the previous render pass
layout(pixel_center_integer) in vec4 gl_FragCoord;
// will give the screen position of the current fragment
void main()
{
// convert fragment position to integers
ivec2 screenpos = ivec2(gl_FragCoord.xy);
// look up result from previous render pass in the texture
vec4 color = texelFetch(mytex, screenpos, 0);
// now use the value from the previous render pass ...
}
另一种处理渲染图像的方法是OpenCL with OpenGL -> OpenCL interop。这允许更多类似 CPU 的计算。
如果您所说的“片段的当前值”是片段着色器运行之前渲染目标中的像素颜色值,那么不,它不可用。
这样做的主要原因是,在您的片段着色器运行时,它可能是未知的。片段着色器并行运行,可能(取决于哪个硬件)影响相同的像素,并且从某种 FIFO 读取的单独块通常负责稍后将它们合并在一起。这种合并称为“混合”,还不是可编程管道的一部分。它是固定功能,但它确实有许多不同的方法可以将片段着色器生成的内容与像素的先前颜色值结合起来。
您需要在当前像素坐标处对纹理进行采样,如下所示
vec4 pixel_color = texture2D(tex, gl_TexCoord[0].xy);
注意, - 正如我所见,纹理 2D 在 GLSL 4.00 规范中已被弃用 - 只需寻找类似的纹理......获取功能。
有时最好提供自己的像素坐标而不是 gl_TexCoord[0].xy - 在这种情况下,编写顶点着色器,例如:
变化 vec2 texCoord; 无效主要(无效) { gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0); texCoord = 0.5 * gl_Position.xy + vec2(0.5); }
在片段着色器中使用该 texCoord 变量而不是 gl_TexCoord[0].xy。
祝你好运。
片段着色器的重点是决定输出颜色是什么。你如何做到这一点取决于你想要做什么。
您可能会选择进行设置,以便根据顶点着色器的输出获得插值颜色,但更常见的方法是使用从顶点着色器插值传入的纹理坐标在片段着色器中执行纹理查找. 然后,您将根据您选择的光照计算以及您的着色器要执行的任何其他操作来修改纹理查找的结果,然后将其写入 gl_FragColor。
在着色器运行后,GPU 管道可以立即访问底层像素信息。如果您的材质是透明的,则管道的混合阶段将组合所有片段。
通常,对象按照它们添加到场景中的顺序进行混合,除非它们已由 z 缓冲算法排序。您应该先添加不透明对象,然后按照要混合的顺序小心添加透明对象。
例如,如果您想在场景中添加 HUD 叠加层,您应该只创建一个具有适当透明纹理的屏幕四边形对象,然后将其最后添加到您的场景中。
为透明对象设置 SRC 和 DST 混合功能可让您以多种不同方式访问先前的混合。
您可以在此处使用输出颜色的 alpha 属性进行真正精美的混合。这是访问帧缓冲区输出(像素)的最有效方式,因为它在 GPU 管道的单通道(图 1)中工作。
如果您确实需要多通道(图 2),那么您必须将帧缓冲区输出定位到一个额外的纹理单元而不是屏幕,并将此目标纹理复制到下一个通道,依此类推,在最后一个通道中定位屏幕. 每次传递至少需要两次上下文切换。
额外的复制和上下文切换将严重降低渲染性能。请注意,多线程 GPU 管道在这里没有多大帮助,因为多通道本质上是序列化的。
由于着色器语言(俚语/GLSL)可能会发生变化,因此我使用了流水线图的口头描述以避免弃用。
我如何获取片段的当前颜色
有人说这做不到,但我说这对我有用:
//Toggle blending in one sense, while always disabling it in the other.
void enableColorPassing(BOOL enable) {
//This will toggle blending - and what gl_FragColor is set to upon shader execution
enable ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
//Tells gl - "When blending, change nothing"
glBlendFunc(GL_ONE, GL_ZERO);
}
在那次调用之后,gl_FragColor 将等于着色器第一次在每个像素上运行时颜色缓冲区的清除颜色,并且每次运行的输出将是每次连续运行时的新输入。
好吧,至少它对我有用。