如何获取片段的当前颜色?

时间:2010-04-29 21:25:04

标签: opengl shader glsl fragment-shader

我正在尝试用GLSL中的着色器包裹我的头,并且我已经找到了一些有用的资源和教程,但是我一直在为一些应该是基本和微不足道的东西碰壁:我的片段着色器如何检索当前片段的颜色?

您可以通过说gl_FragColor = whatever来设置最终颜色,但显然这是一个仅输出值。如何获得输入的原始颜色,以便对其进行计算?这必须在某个地方变量,但是如果有人知道它的名字,他们似乎没有在我到目前为止遇到的任何教程或文档中记录它,它正在推动我。 / p>

7 个答案:

答案 0 :(得分:14)

片段着色器接收gl_Colorgl_SecondaryColor作为顶点属性。它还有四个不同的变量:gl_FrontColorgl_FrontSecondaryColorgl_BackColorgl_BackSecondaryColor它可以写入值。如果你想直接传递原始颜色,你可以这样做:

gl_FrontColor = gl_Color;
gl_FrontSecondaryColor = gl_SecondaryColor;
gl_BackColor = gl_Color;
gl_BackSecondaryColor = gl_SecondaryColor;

顶点着色器后面的管道中的固定功能会将这些功能钳位到范围[0..1],并确定顶点是正面还是背面。然后它会像往常一样插入所选的(正面或背面)颜色。然后,片段着色器将接收选定的,钳制的插值颜色gl_Colorgl_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;
}

将传输颜色,就像使用固定功能管道一样。

答案 1 :(得分:12)

如果你想进行多遍渲染,即如果你已经渲染到帧缓冲区并且想要使用之前渲染的第二个渲染过程,那么答案是:

  1. 将第一个渲染渲染为纹理
  2. 将此纹理绑定到第二遍
  3. 访问着色器中的私有渲染像素
  4. 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 ...
    }
    

    使用OpenGL处理​​渲染图像的另一种方法是OpenCL - > OpenCL互操作。这允许更多的CPU计算。

答案 2 :(得分:6)

如果你所谓的“片段的当前值”是片段着色器运行之前渲染目标中的像素颜色值,那么不,它不可用。

主要原因是,在您的片段着色器运行时,可能尚未知道。片段着色器并行运行,可能(取决于哪些硬件)影响相同的像素,并且从某种FIFO读取的单独块通常负责稍后将它们合并在一起。这种合并称为“混合”,并且还不是可编程管道的一部分。它是固定功能,但它确实有许多不同的方法可以将片段着色器生成的内容与像素的前一个颜色值组合在一起。

答案 3 :(得分:4)

您需要在当前像素坐标处对纹理进行采样,如下所示

vec4 pixel_color = texture2D(tex, gl_TexCoord[0].xy);

注意, - 正如我所见,在GLSL 4.00规范中不推荐使用texture2D - 只需寻找类似的纹理...获取函数。

有时候最好提供自己的像素坐标而不是gl_TexCoord [0] .xy - 在这种情况下写顶点着色器类似于:

varying vec2 texCoord;

void main(void)
{
   gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0 );
   texCoord = 0.5 * gl_Position.xy + vec2(0.5);     
}

在片段着色器中使用texCoord变量而不是gl_TexCoord [0] .xy。

祝你好运。

答案 4 :(得分:3)

片段着色器的整个点是决定输出颜色是什么。你如何做到这一点取决于你想要做什么。

您可以选择设置内容,以便根据顶点着色器的输出获得插值颜色,但更常见的方法是使用从中传递的纹理坐标在片段着色器中执行纹理查找。顶点着色器插值。然后,您可以根据您选择的光照计算以及着色器要执行的任何其他操作修改纹理查找的结果,然后将其写入gl_FragColor。

答案 5 :(得分:1)

着色器运行后,GPU管道可以立即访问基础像素信息。如果您的材料是透明的,管道的混合阶段将组合所有碎片。

通常,对象按照添加到场景的顺序进行混合,除非它们是由z缓冲算法订购的。您应首先添加不透明对象,然后按顺序添加透明对象。

例如,如果您想在场景中使用HUD覆盖,则应该创建一个具有适当透明纹理的屏幕四边形对象,并将其最后添加到场景中。

为透明对象设置SRC和DST混合功能,可以通过多种方式访问​​上一个混合。

您可以在此处使用输出颜色的alpha属性来进行真正的花式混合。这是访问帧缓冲输出(像素)的最有效方法,因为它可以在GPU管道的单个通道(图1)中工作。

enter image description here
图。 1 - 单通票

如果你真的需要multi pass(图2),那么你必须将帧缓冲输出定位到一个额外的纹理单元而不是屏幕,然后将这个目标纹理复制到下一个传递,依此类推,定位最后一次通过的屏幕。每次传递都需要至少两个上下文切换。

额外的复制和上下文切换会严重降低渲染性能。请注意,多线程GPU管道在这里没有太大帮助,因为多次传递本质上是序列化的。

enter image description here
图。 2 - 多次通过

我已经使用管道图进行口头描述以避免弃用,因为着色器语言(Slang / GLSL)可能会发生变化。

答案 6 :(得分:-3)

如何-DO-I-得到最当前色对的一片段

有人说不能这样做,但我说这对我有用:

//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将在着色器第一次在每个像素上运行时等于颜色缓冲区的清晰颜色,并且每次运行的输出将是每次连续运行时的新输入。

好吧,至少它对我有用。