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

发布于 2024-08-31 07:05:28 字数 232 浏览 11 评论 0原文

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

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

I'm trying to wrap my head around shaders in GLSL, and I've found some useful resources and tutorials, but I keep running into a wall for something that ought to be fundamental and trivial: how does my fragment shader retrieve the color of the current fragment?

You set the final color by saying gl_FragColor = whatever, but apparently that's an output-only value. How do you get the original color of the input so you can perform calculations on it? That's got to be in a variable somewhere, but if anyone out there knows its name, they don't seem to have recorded it in any tutorial or documentation that I've run across so far, and it's driving me up the wall.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

掐死时间 2024-09-07 07:05:28

片段着色器接收 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;
}

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

The fragment shader receives gl_Color and gl_SecondaryColor as vertex attributes. It also gets four varying variables: gl_FrontColor, gl_FrontSecondaryColor, gl_BackColor, and gl_BackSecondaryColor that it can write values to. If you want to pass the original colors straight through, you'd do something like:

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

Fixed functionality in the pipeline following the vertex shader will then clamp these to the range [0..1], and figure out whether the vertex is front-facing or back-facing. It will then interpolate the chosen (front or back) color like usual. The fragment shader will then receive the chosen, clamped, interpolated colors as gl_Color and gl_SecondaryColor.

For example, if you drew the standard "death triangle" like:

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();

Then a vertex shader like this:

void main(void) {
    gl_Position = ftransform();
    gl_FrontColor = gl_Color;
}

with a fragment shader like this:

void main() {
    gl_FragColor = gl_Color;
}

will transmit the colors through, just like if you were using the fixed-functionality pipeline.

清浅ˋ旧时光 2024-09-07 07:05:28

如果您想要进行多通道渲染,即如果您已渲染到帧缓冲区并想要使用先前渲染的第二个渲染通道,那么答案是:

  1. 将第一个通道渲染到纹理,
  2. 为第二个通道绑定此纹理pass
  3. 访问着色器中单独渲染的像素

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< /a> 使用 OpenGL -> OpenCL 互操作。这允许更多类似 CPU 的计算。

If you want to do mult-pass rendering, i.e. if you have rendered to the framebuffer and want to to a second render pass where you use the previous rendering than the answer is:

  1. Render the first pass to a texture
  2. Bind this texture for the second pass
  3. Access the privously rendered pixel in the shader

Shader code for 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 ...
}

Another methods of processing a rendered image would be OpenCL with OpenGL -> OpenCL interop. This allows more CPU like computationing.

柠檬色的秋千 2024-09-07 07:05:28

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

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

If what you're calling "current value of the fragment" is the pixel color value that was in the render target before your fragment shader runs, then no, it is not available.

The main reason for that is that potentially, at the time your fragment shader runs, it is not known yet. Fragment shaders run in parallel, potentially (depending on which hardware) affecting the same pixel, and a separate block, reading from some sort of FIFO, is usually responsible to merge those together later on. That merging is called "Blending", and is not part of the programmable pipeline yet. It's fixed function, but it does have a number of different ways to combine what your fragment shader generated with the previous color value of the pixel.

深府石板幽径 2024-09-07 07:05:28

您需要在当前像素坐标处对纹理进行采样,就像这样

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

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

另外,有时最好提供自己的像素坐标而不是 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。

祝你好运。

You need to sample texture at current pixel coordinates, something like this

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

Note,- as i've seen texture2D is deprecated in GLSL 4.00 specification - just look for similar texture... fetch functions.

Also sometimes it is better to supply your own pixel coordinates instead of gl_TexCoord[0].xy - in that case write vertex shader something like:

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);     
}

And in fragment shader use that texCoord variable instead of gl_TexCoord[0].xy.

Good luck.

柠北森屋 2024-09-07 07:05:28

片段着色器的全部目的是决定输出颜色是什么。如何做到这一点取决于您想要做什么。

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

The entire point of your fragment shader is to decide what the output color is. How you do that depends on what you are trying to do.

You might choose to set things up so that you get an interpolated color based on the output of the vertex shader, but a more common approach would be to perform a texture lookup in the fragment shader using texture coordinates passed in the from the vertex shader interpolants. You would then modify the result of your texture lookup according to your chosen lighting calculations and whatever else your shader is meant to do and then write it into gl_FragColor.

小忆控 2024-09-07 07:05:28

GPU 管道可以在着色器运行后立即访问底层像素信息。如果您的材质是透明的,管道的混合阶段将合并所有片段。

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

例如,如果您希望在场景上覆盖 HUD,则只需创建一个具有适当透明纹理的屏幕四边形对象,然后将其添加到最后的场景中。

为透明对象设置 SRC 和 DST 混合函数可以让您以多种不同的方式访问先前的混合。

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

输入图片此处描述
图。 1 - 单通道

如果您确实需要多通道(图 2),那么您必须将帧缓冲区输出定位到额外的纹理单元而不是屏幕,并将该目标纹理复制到下一个通道,依此类推,在最后一个通道中定位屏幕。每次传递至少需要两次上下文切换。

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

在此处输入图像描述
图。 2 - 多通道

由于着色器语言(Slang/GLSL)可能会发生变化,因此我采用了带有管线图的口头描述来避免弃用。

The GPU pipeline has access to the underlying pixel info immediately after the shaders run. If your material is transparent, the blending stage of the pipeline will combine all fragments.

Generally objects are blended in the order that they are added to a scene, unless they have been ordered by a z-buffering algo. You should add your opaque objects first, then carefully add your transparent objects in the order to be blended.

For example, if you want a HUD overlay on your scene, you should just create a screen quad object with an appropriate transparent texture, and add this to your scene last.

Setting the SRC and DST blending functions for transparent objects gives you access to the previous blend in many different ways.

You can use the alpha property of your output color here to do really fancy blending. This is the most efficient way to access framebuffer outputs (pixels), since it works in a single pass (Fig. 1) of the GPU pipeline.

enter image description here
Fig. 1 - Single Pass

If you really need multi pass (Fig. 2), then you must target the framebuffer outputs to an extra texture unit rather than the screen, and copy this target texture to the next pass, and so on, targeting the screen in the final pass. Each pass requires at least two context switches.

The extra copying and context switching will degrade rendering performance severely. Note that multi-threaded GPU pipelines are not much help here, since multi pass is inherently serialized.

enter image description here
Fig. 2 - Multi Pass

I have resorted to a verbal description with pipeline diagrams to avoid deprecation, since shader language (Slang/GLSL) is subject to change.

生生漫 2024-09-07 07:05:28

how-do-i-get-the-current-color-of-a-fragment

有人说这是不可能的,但我说这对我有用:

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

好吧,至少它对我有用。

how-do-i-get-the-current-color-of-a-fragment

Some say it cannot be done, but I say this works for me:

//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); 
}

After that call, gl_FragColor will equal the color buffer's clear color the first time the shader runs on each pixel, and the output each run will be the new input upon each successive run.

Well, at least it works for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文