4

我正在开发一个基于一些蒙版图像生成小云的着色器。现在它运作良好,但我觉得结果缺少一些东西,我认为模糊会很好。我记得一个基本的模糊算法,你必须应用一个具有范数为 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;
            }
4

1 回答 1

1

在我看来,这个着色器正在模拟类似后缓冲区的纹理(通过 传递)和映射到渐变上sampler2D _MainTex的生成的云亮度(由 表示)之间的 alpha 测试。float lum这让事情变得更加棘手,因为你不能只是假装模糊而让 alpha 混合处理其余的事情。您还需要更改您的 alpha 测试例程以模拟 alpha 混合,或相应地重组您的渲染管道。我们将首先处理模糊云。

您需要问自己的第一个问题是是否需要屏幕空间模糊。看到这个片段着色器的机制,我认为不会——你想模糊实际模型上的云。鉴于此,模糊底层纹理并产生模糊结果就足够了——除非你正在模拟 alpha 裁剪,所以你会得到粗糙的边缘。问题是如何处理这些粗糙的边缘。这就是阿尔法混合的用武之地。

turb您可以使用 lerp() 函数在颜色和颜色之间使用 lerp(线性插值)来模拟 alpha 混合c(取决于您使用的着色器语言)。你可能想要一些看起来像的东西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);

但是,如果您使云功能过于复杂,那么执行此操作的计算速度会相对较慢。如果您受到光栅操作和纹理读取(将纹理/缓冲区数据传入和传出内存)的限制,除非您使用更高级的模糊技术(例如通过乒乓缓冲区成功进行下采样,有用用于昂贵的模糊/过滤器,因为它们有很多抽头)。但性能是获得您想要的外观的另一个整体考虑因素。

于 2012-01-17T17:45:03.937 回答