UnityURP云层效果

原理:

先看效果:

原理很简单,就是一个面片做顶点偏移

实现:

声明需要使用的参数,这里我采用的是ShaderGraph的程序噪声生成,也可以采样一张噪声图。

    Properties
    {
        _NoiseScale ("噪声大小", float) = 4.1
        _Height ("云层高度", float) = 0.06
        _Density ("云层密度", Range(0.01, 1.0)) = 0.5
        _SpeedX ("水平流动速度", float) = 0.0
        _SpeedY ("垂直流动速度", float) = 0.1
        _FresnelPower ("FresnelPower", float) = 1.24
        _Color1 ("Color1", Color) = (0.4389818, 0.7886792, 0.7637008, 1.0)
        _Color2 ("Color2", Color) = (0.7301887, 0.9488779, 1.0, 1.0)
    }

声明必要的变体和结构,这不用多说,云层是透明的,所以需要归为透明物体

 SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True" }

        Pass
        {
            Cull Off
            Blend SrcAlpha OneMinusSrcAlpha
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            CBUFFER_START(UnityPerMaterial)

                half _NoiseScale;
                half _Height;
                half _Density;
                half _FresnelPower;
                half _SpeedX;
                half _SpeedY;
                half4 _Color1;
                half4 _Color2;
                
            CBUFFER_END

            struct Attributes
            {
                float4 positionOS : POSITION;
            };

            struct Varyings
            {
                float4 positionHS : SV_POSITION;
            };

        
            Varyings vert(Attributes v)
            {
                Varyings o;

                float3 positionWS = TransformObjectToWorld(v.positionOS.xyz);
                return o;
            }

            half4 frag(Varyings i) : SV_Target
            {
               
                return 1;
            }
            ENDHLSL
        }
    }

然后我们开始增加效果,首先声明程序生成噪声的方法(这里我直接使用了ShaderGarph中SimpleNoise的方法源码)

  float2 unity_gradientNoise_dir(float2 p)
            {
                p = p % 289;
                float x = (34 * p.x + 1) * p.x % 289 + p.y;
                x = (34 * x + 1) * x % 289;
                x = frac(x / 41) * 2 - 1;
                return normalize(float2(x - floor(x + 0.5), abs(x) - 0.5));
            }

            float unity_gradientNoise(float2 p)
            {
                float2 ip = floor(p);
                float2 fp = frac(p);
                float d00 = dot(unity_gradientNoise_dir(ip), fp);
                float d01 = dot(unity_gradientNoise_dir(ip + float2(0, 1)), fp - float2(0, 1));
                float d10 = dot(unity_gradientNoise_dir(ip + float2(1, 0)), fp - float2(1, 0));
                float d11 = dot(unity_gradientNoise_dir(ip + float2(1, 1)), fp - float2(1, 1));
                fp = fp * fp * fp * (fp * (fp * 6 - 15) + 10);
                return lerp(lerp(d00, d01, fp.y), lerp(d10, d11, fp.y), fp.x);
            }

            float GradientNoise_float(float2 UV, float Scale)
            {
                return unity_gradientNoise(UV * Scale) + 0.5;
            }

 声明Fresnel方法来制作云层根据视角变化颜色的特点(同样使用了ShaderGraph中的方法)

       float FresnelEffect_float(float3 Normal, float3 ViewDir, float Power)
            {
                return pow((1.0 - saturate(dot(normalize(Normal), normalize(ViewDir)))), Power);
            }

声明好方法后,我们开始编写顶点Shader,原理已经说过是对顶点偏移来模拟云层上下运动的效果,所以我们需要让顶点朝着法线方向偏移一定高度,并且根据噪声的强度对顶点进行偏移,同样为了模拟云层的流动,我们需要对噪声点进行流动处理。

            Varyings vert(Attributes v)
            {
                Varyings o;

                float3 positionWS = TransformObjectToWorld(v.positionOS.xyz);
                float2 speed = float2(_SpeedX, _SpeedY) * _Time.y;

                float noise1 = GradientNoise_float(speed + positionWS.xz, _NoiseScale);
                float noise2 = GradientNoise_float(speed, _NoiseScale);
                float final_noise = noise1 + noise2;

                float3 positionOS = v.positionOS.xyz + final_noise * normalize(v.normalOS) * _Height;
                o.positionHS = TransformObjectToHClip(positionOS);

                float3 normalWS = TransformObjectToWorldNormal(v.normalOS);
                float3 viewWS = normalize(_WorldSpaceCameraPos.xyz - positionWS);
                float fresnel = FresnelEffect_float(normalWS, viewWS, _FresnelPower);
                o.help.x = fresnel * final_noise;

//为了模拟密度效果,采用深度图来判断强度
                float4 positionSS = ComputeScreenPos(o.positionHS);
                o.help.yzw = float3(positionSS.xy / positionSS.w, positionSS.w);
                return o;
            }

片元Shader

           half4 frag(Varyings i) : SV_Target
            {
                half4 depth_map = tex2D(_CameraDepthTexture, i.help.yz);
                float scene_depth = LinearEyeDepth(depth_map.r, _ZBufferParams);
                half alpha = saturate((scene_depth - (i.help.w - 1)) * _Density);

                half4 final_Color = half4(lerp(_Color1.rgb, _Color2.rgb, i.help.x), alpha);
                return final_Color;
            }

记住别忘记声明深度图

   SAMPLER(_CameraDepthTexture);

全代码

 Properties
    {
        _NoiseScale ("噪声大小", float) = 4.1
        _Height ("云层高度", float) = 0.06
        _Density ("云层密度", Range(0.01, 1.0)) = 0.5
        _SpeedX ("水平流动速度", float) = 0.0
        _SpeedY ("垂直流动速度", float) = 0.1
        _FresnelPower ("FresnelPower", float) = 1.24
        _Color1 ("Color1", Color) = (0.4389818, 0.7886792, 0.7637008, 1.0)
        _Color2 ("Color2", Color) = (0.7301887, 0.9488779, 1.0, 1.0)
    }
    SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True" }

        Pass
        {
            Cull Off
            Blend SrcAlpha OneMinusSrcAlpha
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            SAMPLER(_CameraDepthTexture);

            CBUFFER_START(UnityPerMaterial)

                half _NoiseScale;
                half _Height;
                half _Density;
                half _FresnelPower;
                half _SpeedX;
                half _SpeedY;
                half4 _Color1;
                half4 _Color2;
                
            CBUFFER_END

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS : NORMAL;
            };

            struct Varyings
            {
                float4 positionHS : SV_POSITION;
                float4 help : TEXCOORD0;
            };

            float2 unity_gradientNoise_dir(float2 p)
            {
                p = p % 289;
                float x = (34 * p.x + 1) * p.x % 289 + p.y;
                x = (34 * x + 1) * x % 289;
                x = frac(x / 41) * 2 - 1;
                return normalize(float2(x - floor(x + 0.5), abs(x) - 0.5));
            }

            float unity_gradientNoise(float2 p)
            {
                float2 ip = floor(p);
                float2 fp = frac(p);
                float d00 = dot(unity_gradientNoise_dir(ip), fp);
                float d01 = dot(unity_gradientNoise_dir(ip + float2(0, 1)), fp - float2(0, 1));
                float d10 = dot(unity_gradientNoise_dir(ip + float2(1, 0)), fp - float2(1, 0));
                float d11 = dot(unity_gradientNoise_dir(ip + float2(1, 1)), fp - float2(1, 1));
                fp = fp * fp * fp * (fp * (fp * 6 - 15) + 10);
                return lerp(lerp(d00, d01, fp.y), lerp(d10, d11, fp.y), fp.x);
            }

            float GradientNoise_float(float2 UV, float Scale)
            {
                return unity_gradientNoise(UV * Scale) + 0.5;
            }

            float FresnelEffect_float(float3 Normal, float3 ViewDir, float Power)
            {
                return pow((1.0 - saturate(dot(normalize(Normal), normalize(ViewDir)))), Power);
            }

            Varyings vert(Attributes v)
            {
                Varyings o;

                float3 positionWS = TransformObjectToWorld(v.positionOS.xyz);
                float2 speed = float2(_SpeedX, _SpeedY) * _Time.y;

                float noise1 = GradientNoise_float(speed + positionWS.xz, _NoiseScale);
                float noise2 = GradientNoise_float(speed, _NoiseScale);
                float final_noise = noise1 + noise2;

                float3 positionOS = v.positionOS.xyz + final_noise * normalize(v.normalOS) * _Height;
                o.positionHS = TransformObjectToHClip(positionOS);

                float3 normalWS = TransformObjectToWorldNormal(v.normalOS);
                float3 viewWS = normalize(_WorldSpaceCameraPos.xyz - positionWS);
                float fresnel = FresnelEffect_float(normalWS, viewWS, _FresnelPower);
                o.help.x = fresnel * final_noise;

                float4 positionSS = ComputeScreenPos(o.positionHS);
                o.help.yzw = float3(positionSS.xy / positionSS.w, positionSS.w);
                return o;
            }

            half4 frag(Varyings i) : SV_Target
            {
                half4 depth_map = tex2D(_CameraDepthTexture, i.help.yz);
                float scene_depth = LinearEyeDepth(depth_map.r, _ZBufferParams);
                half alpha = saturate((scene_depth - (i.help.w - 1)) * _Density);

                half4 final_Color = half4(lerp(_Color1.rgb, _Color2.rgb, i.help.x), alpha);
                return final_Color;
            }
            ENDHLSL
        }
    }