GLSL:使用片段着色器进行对象翻译

Posted

技术标签:

【中文标题】GLSL:使用片段着色器进行对象翻译【英文标题】:GLSL : Object translation with fragment shader 【发布时间】:2015-01-28 16:26:32 【问题描述】:

如下图所示,我试图通过多画两次来表达轮廓:左右移动1个像素。

但是,我不知道这应该在顶点着色器还是片段着色器中运行。

是否可以在片段着色器中移动顶点(像素)?

如果不是,我应该在每一帧计算顶点的屏幕空间坐标吗?

【问题讨论】:

【参考方案1】:

对于传统的片段着色器输出,答案是明确而响亮的。片段着色器无法决定要渲染哪个像素。顶点着色器和片段着色器之间的固定函数步骤(光栅化)确定哪些片段被图元覆盖。然后为这些片段中的每一个调用片段着色器。它可以决定在这个片段位置写入输出缓冲区的值(颜色等),或者它可以决定根本不写任何东西(discard)。但它不会改变位置。

以下是我想到的一些选项。

图片

OpenGL 4.2 及更高版本中有一项功能在此区域添加了新选项:图像。您可以将纹理绑定为图像,然后使用内置的imageStore() 函数在着色器代码中写入它们。该函数将坐标和值作为参数,因此您可以将值写入图像中的任意位置。

使用它,您可以使用图像作为输出而不是传统的片段着色器输出,并向其写入多个值。或者使用混合,您仍然使用片段着色器输出进行主要渲染,将阴影部分写入图像,然后将两者与额外的渲染通道结合起来。

多个绘图调用

对于更传统的功能,您通常需要多次渲染几何图形,使用深度或模板测试将主要渲染与阴影效果相结合。例如,使用深度测试,您可以将形状以原始颜色渲染一次,然后再渲染两次,稍微左/右偏移,同时稍微增加深度,使阴影位于原始形状的后面。

几何着色器

我相信您可以使用几何着色器来生成每个图元的 3 个实例。因此,每个图元仍会渲染 3 次,但您不必实际进行 3 次不同的绘制调用。

图像后处理

为了达到您想要的效果,您可以将没有阴影的整个物体渲染到 FBO 中,从而在纹理中生成帧。然后你进行另一个绘制通道,在其中绘制一个窗口大小的四边形,并从包含你的帧的纹理中采样。您对纹理进行 3 次采样,然后将 3 个结果结合起来产生阴影效果。

只是为了勾勒这个(代码完全未经测试)。如果您使用带有 alpha 组件的纹理作为渲染目标,您可以检查 alpha 值以查看在渲染期间是否命中了给定像素。

// Texture produced as output from original render pass.
uniform texture2D Tex;
// Offset to add one pixel to texture coordinates, should be
// 1.0 / width of render target.
uniform float PixelOffset;
// Incoming texture coordinate from rendering window sized quad.
in vec2 TexCoord;
// Output.
out vec4 FragColor;

void main() 
    vec4 centerColor = texture(Tex, TexCoord);
    vec4 leftColor = texture(Tex, vec2(TexCoord.s, TexCoord.t - PixelOffset));
    vec4 rightColor = texture(Tex, vec2(TexCoord.s, TexCoord.t + PixelOffset));

    if (centerColor.a > 0.0) 
        // Fragment was rendered, use its color as output.
        FragColor = centerColor;
     else if (leftColor.a + rightColor.a > 0.0) 
        // Fragment is within 1 pixel left/right of rendered fragment,
        // color it black.
        FragColor = vec4(0.0, 0.0, 0.0, 1.0);
     else 
        // Neither rendered nor in shadow. Set output to background color,
        // or discard it. This would be for white background.
        FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    

结论/建议

直观地说,我自己喜欢图像后处理方法。我可能会先尝试一下。我认为下一个最优雅的解决方案是使用几何着色器复制图元。

【讨论】:

谢谢。我通过图像后处理解决了。顺便说一句,我可以在几何着色器中设置对象的绘制顺序吗?如果我使用几何着色器复制,它会显示深度测试问题。【参考方案2】:

不,一旦您进入片段着色器,输出位置已经被光栅化过程固定。

【讨论】:

【参考方案3】:

由于像素的位置已经由您在片段着色器中的时间确定,所以这不是一个选项。顶点着色器也帮不了你,因为它只能为每个传入的顶点推送一个输出顶点。

然而,几何着色器阶段可以为每个传入的顶点发射多个顶点。这可以让您克隆两个额外的顶点,每个顶点都有一个平移到原始顶点的左侧或右侧。

此资源有一些详细的现代示例:https://open.gl/geometry

【讨论】:

感谢几何着色器示例。我找到another example 了解更多信息。

以上是关于GLSL:使用片段着色器进行对象翻译的主要内容,如果未能解决你的问题,请参考以下文章

GLSL片段着色器 - 绘制简单的粗曲线

GLSL将颜色数据从片段着色器发送到顶点着色器似乎总是等于0

顶点着色器 glsl qt 中的纹理映射

片段着色器中设置的颜色未显示 GLSL 1.30

GLSL-片段着色器不同部分的精度不同

如何在 C++ 中为 GLSL 片段着色器实现 iGlobalTime?