如何模糊片段着色器的结果?

时间:2012-01-07 00:08:33

标签: algorithm shader blur cg

我正在开发一个基于某些蒙版图像生成小云的着色器。现在它运作良好,但我觉得结果是缺少一些东西,我认为模糊会很好。我记得一个基本的模糊算法,你必须应用一个带有范数1矩阵的卷积(矩阵越大,结果越大)和一个图像。问题是,我不知道如何将着色器的当前结果视为图像。所以基本上我想保持着色器不变,但让它变得模糊。任何想法?,如何将卷积算法集成到着色器?或者有人知道其他算法吗?

Cg代码:

    float Luminance( float4 Color ){
    return 0.6 * Color.r + 0.3 * Color.g + 0.1 * Color.b;
}

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv_MainTex : TEXCOORD0;
            };

            float4 _MainTex_ST;

            v2f vert(appdata_base v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            sampler2D _MainTex;
            sampler2D _Gradient;
            sampler2D _NoiseO;
            sampler2D _NoiseT;

            float4 frag(v2f IN) : COLOR {

                half4 nO = tex2D (_NoiseO, IN.uv_MainTex);
                half4 nT = tex2D (_NoiseT, IN.uv_MainTex);
                float4 turbulence = nO + nT;
                float lum = Luminance(turbulence);
                half4 c = tex2D (_MainTex, IN.uv_MainTex);
                if (lum >= 1.0f){

                    float pos = lum - 1.0f;
                    if( pos > 0.98f ) pos = 0.98f;
                    if( pos < 0.02f ) pos = 0.02f;
                    float2 texCord = (pos, pos);
                    half4 turb = tex2D (_Gradient, texCord);
                    //turb.a = 0.0f;
                    return turb;
                }
                else return c;
            }

1 个答案:

答案 0 :(得分:1)

在我看来,这个着色器模拟了一个类似backbuffer的纹理(通过sampler2D _MainTex传递)和生成的云亮度(由float lum表示)映射到渐变之间的alpha测试。这使得事情变得更加棘手,因为你不能伪造模糊,让alpha混合处理剩下的事情。您还需要更改alpha测试例程以模拟alpha混合,或相应地重新构建渲染管道。我们将首先处理模糊云。

您需要问自己的第一个问题是,您是否需要屏幕空间模糊。看到这个片段着色器的机制,我想不会 - 你想模糊实际模型上的云。鉴于此,它应该足以模糊底层纹理并导致模糊结果 - 除了你模仿alpha剪辑,所以你会得到粗糙的边缘。问题是如何处理这些粗糙的边缘。这就是alpha混合的用武之地。

您可以使用lerp()函数在turb颜色和c颜色之间使用lerp(线性插值)来模拟Alpha混合(取决于您使用的着色器语言)。你可能想要一些看似return lerp(c, turb, 1 - pos);而不是return turb;的东西......我希望你能不断调整它,直到你理解并开始得到你想要的结果。 (例如,您可能更喜欢lerp(c, turb, 1 - pow(pos,4))

事实上,您可以在修改纹理之前尝试最后一步(只需添加lerp),以了解Alpha混合将为您做什么。

修改:我没有考虑_NoiseO_NoiseT采样器不断变化的情况,所以简单地告诉你模糊它们是最有用的建议。您可以使用多次点击过滤器模拟模糊。最简单的方法是采用均匀间隔的样品,对它们进行加权,然后将它们相加,得到最终颜色。 (通常你会希望权重本身总和为1.)

这就是说,您可能会或可能不会在_NoiseO_NoiseT纹理本身上执行此操作 - 您可能希望创建一个屏幕空间模糊,而这可能看起来更有趣观众。在这种情况下,同样的概念适用,但您需要对每个抽头的偏移坐标进行计算,然后执行加权求和。

例如,如果我们选择第一种情况并且我们想要从_Noise0采样器中采样并稍微模糊一下,我们可以使用此框滤波器(其中所有权重相同并且总和为1,从而执行平均值):

// Untested code.
half4 nO  = 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(         0,          0))
          + 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(         0, g_offset.y))
          + 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(g_offset.x,          0))
          + 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(g_offset.x, g_offset.y))

或者,如果我们希望整个云输出看起来模糊,我们将云生成部分包含在一个函数中,而不是tex2D()来调用它。

// More untested code.
half4 genCloud(float2 tc) {
    half4 nO = tex2D (_NoiseO, IN.uv_MainTex);
    half4 nT = tex2D (_NoiseT, IN.uv_MainTex);
    float4 turbulence = nO + nT;
    float lum = Luminance(turbulence);
    float pos = lum - 1.0;
    if( pos > 0.98f ) pos = 0.98f;
    if( pos < 0.02f ) pos = 0.02f;
    float2 texCord = (pos, pos);
    half4 turb = tex2D (_Gradient, texCord);
    // Figure out how you'd generate your alpha blending constant here for your lerp
    turb.a = ACTUAL_ALPHA;
    return turb;
}

多次点击过滤看起来像:

// And even more untested code.
half4 cloudcolor = 0.25 * genCloud(IN.uv_MainTex + float2(         0,          0))
                 + 0.25 * genCloud(IN.uv_MainTex + float2(         0, g_offset.y))
                 + 0.25 * genCloud(IN.uv_MainTex + float2(g_offset.x,          0))
                 + 0.25 * genCloud(IN.uv_MainTex + float2(g_offset.x, g_offset.y))
return lerp(c, cloudcolor, cloudcolor.a);

然而如果您使云功能过于复杂,那么执行此操作的计算速度会相对较慢。如果您受光栅操作和纹理读取(将纹理/缓冲区数据传输到内存或从内存传输)的约束,除非您使用更先进的模糊技术(通过ping-pnded缓冲区成功进行下采样,这很有用),否则这无关紧要。因为它们有很多水龙头,所以很昂贵的模糊/过滤器)。但是,只要获得你想要的外观,性能就是另一个完整的考虑因素。