原理:
先看效果:
原理很简单,就是一个面片做顶点偏移
实现:
声明需要使用的参数,这里我采用的是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
}
}