Unity Shader学习笔记(28)噪声纹理、消融效果、水波效果、噪声雾效

发表于2018-01-02
评论0 6.3k浏览
本篇文章给大家介绍几种噪声效果,让大家了解噪声纹理、消融效果、水波效果、噪声雾效这些效果的实现。

噪声纹理

通过纹理深浅可以作为一些变换的阈值,或者透明度等。= 


消融效果(Dissolve)

常用与角色死亡、地图烧毁时效果。

Burn Amount从0变化到1。 

Properties {
    _BurnAmount ("Burn Amount", Range(0.0, 1.0)) = 0.0              // 消融程度 0为正常效果
    _LineWidth("Burn Line Width", Range(0.0, 0.2)) = 0.1            // 消融效果时的线宽
    _MainTex ("Base (RGB)", 2D) = "white" {}                        // 漫反射纹理
    _BumpMap ("Normal Map", 2D) = "bump" {}                         // 法线纹理
    _BurnFirstColor("Burn First Color", Color) = (1, 0, 0, 1)       // 火焰边缘颜色值
    _BurnSecondColor("Burn Second Color", Color) = (1, 0, 0, 1)
    _BurnMap("Burn Map", 2D) = "white"{}                            // 噪声纹理
}
SubShader {
    Tags { "RenderType"="Opaque" "Queue"="Geometry"}
    Pass {
        Tags { "LightMode"="ForwardBase" }
        Cull Off    // 关掉剔除,正反面都渲染
        ...
        fixed4 frag(v2f i) : SV_Target {
            ...
            fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
            clip(burn.r - _BurnAmount);     // 如果小于0,该像素会被剔除
            //_LineWidth宽度范围模拟渐变。t为0是正常颜色,为1时位于消融的边界
            fixed t = 1 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);
            fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);  //两个烧焦颜色做插值
            burnColor = pow(burnColor, 5);  // 加强颜色模拟烧焦痕迹
            UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
            fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t * step(0.0001, _BurnAmount));
            return fixed4(finalColor, 1);
        }
        ENDCG
    }
    // 阴影投射,直接剔除掉阈值内的像素即可
    Pass {
        Tags { "LightMode" = "ShadowCaster" }
        ...
        fixed4 frag(v2f i) : SV_Target {
            fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
            clip(burn.r - _BurnAmount);     // 剔除掉透明的
            SHADOW_CASTER_FRAGMENT(i)
        }
        ENDCG
    }
}

水波效果

噪声作为高度图,修改水面法线。使用Schilick菲涅尔反射实现:fresnel = pow(1 - max(0, v·n),4 )。

菲涅尔动态水面效果: 

Properties {
    _Color ("Main Color", Color) = (0, 0.15, 0.115, 1)
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _WaveMap ("Wave Map", 2D) = "bump" {}                           // 噪声法线纹理
    _Cubemap ("Environment Cubemap", Cube) = "_Skybox" {}           // 立方体纹理
    _WaveXSpeed ("Wave Horizontal Speed", Range(-0.1, 0.1)) = 0.01  // 法线平移速度
    _WaveYSpeed ("Wave Vertical Speed", Range(-0.1, 0.1)) = 0.01
    _Distortion ("Distortion", Range(0, 100)) = 10                  // 折射扭曲度
}
SubShader {
    // 所有不透明物体渲染完后
    Tags { "Queue"="Transparent" "RenderType"="Opaque" }
    // 抓取屏幕Pass 存入_RefractionTex 
    GrabPass { "_RefractionTex" }
    Pass {
        ...
        #pragma multi_compile_fwdbase
        ...
        sampler2D _RefractionTex;               // 对应GrabPass
        float4 _RefractionTex_TexelSize;
        ...
        v2f vert(a2v v) {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.scrPos = ComputeGrabScreenPos(o.pos);         // 抓取屏幕图像的采样坐标
            ...// 计算世界空间下切线法线等,构造转换矩阵
            return o;
        }
        fixed4 frag(v2f i) : SV_Target {
            float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
            fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
            float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);
            // 对法线两次采样(模拟两层交叉水面波动),相加得到切线空间下的法线方向。
            fixed3 bump1 = UnpackNormal(tex2D(_WaveMap, i.uv.zw + speed)).rgb;
            fixed3 bump2 = UnpackNormal(tex2D(_WaveMap, i.uv.zw - speed)).rgb;
            fixed3 bump = normalize(bump1 + bump2);
            // 计算偏移量,切线空间
            float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
            i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;            // offset * i.scrPos.z 模拟水越深,折射越大
            fixed3 refrCol = tex2D( _RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
            // 计算法线到世界空间
            bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
            fixed4 texColor = tex2D(_MainTex, i.uv.xy + speed);
            fixed3 reflDir = reflect(-viewDir, bump);
            fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb * _Color.rgb;
            // Schilick菲涅尔近似等式
            fixed fresnel = pow(1 - saturate(dot(viewDir, bump)), 4);
            fixed3 finalColor = reflCol * fresnel + refrCol * (1 - fresnel);
            return fixed4(finalColor, 1);
        }
        ENDCG
    }
}


噪声雾效

前面实现过雾效在同一高度的浓度是一致的,这里使用噪声模拟雾的浓度,实现非均匀雾效。 

在之前实现的雾效添加一些属性。

Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _FogDensity ("Fog Density", Float) = 1.0
    _FogColor ("Fog Color", Color) = (1, 1, 1, 1)
    _FogStart ("Fog Start", Float) = 0.0
    _FogEnd ("Fog End", Float) = 1.0
    _NoiseTex ("Noise Texture", 2D) = "white" {}        // 噪声纹理
    _FogXSpeed ("Fog Horizontal Speed", Float) = 0.1
    _FogYSpeed ("Fog Vertical Speed", Float) = 0.1
    _NoiseAmount ("Noise Amount", Float) = 1            // 噪声数量,0为不使用噪声
}
fixed4 frag(v2f i) : SV_Target {
    float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
    float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
    float2 speed = _Time.y * float2(_FogXSpeed, _FogYSpeed);
    float noise = (tex2D(_NoiseTex, i.uv + speed).r - 0.5) * _NoiseAmount;  // 随时间偏移雾效,-0.5是把范围控制在[-0.5,0.5]
    float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart); 
    fogDensity = saturate(fogDensity * _FogDensity * (1 + noise));  // (1 + noise):浓度倍数范围在[0.5, 1.5]内。
    fixed4 finalColor = tex2D(_MainTex, i.uv);
    finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity);
    return finalColor;
}

Unity Shader学习笔记系列教程:

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引