Unity自带的全局雾效有以下几个缺点:
- 需要为每个自定义Shader按规则书写雾效处理代码
- 自带的全局雾效无法实现一些自定义效果,比如:基于高度的雾效 - 可以用来做出悬浮的水雾效果;不规则的雾效(结合噪声图实现) - 可以为雾增加随机性和不规则性;动态变化的雾、基于纹理的雾 等等
总体而言,就是Unity自带的全局雾效只能满足最基础的效果,较为局限。
因此所谓的噪声雾效,其实就是在我们之前已经实现的屏幕后处理效果中的全局雾效中结合噪声和内置时间参数,去实现出不规则、动态的全局雾效,让我们的雾效更具动态感,真实感!
1、基本原理
噪声雾效可以基于屏幕后处理效果的全局雾效进行修改,通过添加噪声纹理结合Shader内置时间变量实现雾的不均匀以及动态效果
关键点:
- 不均匀效果的实现
可以利用柏林噪声算法生成的噪声纹理灰度图来制作不均匀感。从灰度图中采样得到0~1范围的值,再通过减去0.5的方式将其区间变为-0.5~0.5之间,并用这个系数参与最后的雾的混合因子计算中,从而让均匀的雾变得不均匀。
- 动态效果的实现
类似水波效果,自定义x轴和y轴的两个速度变量。利用Shader内置时间参数_Time.y 得到累积变化。用该速度变量从噪声纹理中偏移采样,从而达到动态效果。
2、实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NoiseFog : PostEffectBase {
//雾的颜色
public Color fogColor = Color.gray;
//雾的浓度
[Range(0, 3)]
public float fogDensity = 1f;
//雾开始的距离
public float fogStart = 0f;
//雾最浓时的距离
public float fogEnd = 5;
//4x4的矩阵 用于传递 4个向量参数
private Matrix4x4 rayMatrix;
public Texture noiseTexture;
public float noiseAmount;
public float fogXSpeed;
public float fogYSpeed;
// Start is called before the first frame update
void Start() {
Camera.main.depthTextureMode = DepthTextureMode.Depth;
}
protected override void UpdateProperty() {
if (material != null) {
float fov = Camera.main.fieldOfView / 2f;
float near = Camera.main.nearClipPlane;
float aspect = Camera.main.aspect;
float halfH = near * Mathf.Tan(fov * Mathf.Deg2Rad);
float halfW = halfH * aspect;
Vector3 toTop = Camera.main.transform.up * halfH;
Vector3 toRight = Camera.main.transform.right * halfW;
Vector3 TL = Camera.main.transform.forward * near + toTop - toRight;
Vector3 TR = Camera.main.transform.forward * near + toTop + toRight;
Vector3 BL = Camera.main.transform.forward * near - toTop - toRight;
Vector3 BR = Camera.main.transform.forward * near - toTop + toRight;
float scale = TL.magnitude / near;
TL = TL.normalized * scale;
TR = TR.normalized * scale;
BL = BL.normalized * scale;
BR = BR.normalized * scale;
rayMatrix.SetRow(0, BL);
rayMatrix.SetRow(1, BR);
rayMatrix.SetRow(2, TR);
rayMatrix.SetRow(3, TL);
material.SetColor("_FogColor", fogColor);
material.SetFloat("_FogDensity", fogDensity);
material.SetFloat("_FogStart", fogStart);
material.SetFloat("_FogEnd", fogEnd);
material.SetMatrix("_RayMatrix", rayMatrix);
material.SetTexture("_FogNoise", noiseTexture);
material.SetFloat("_NoiseAmount", noiseAmount);
material.SetFloat("_FogXSpeed", fogXSpeed);
material.SetFloat("_FogYSpeed", fogYSpeed);
}
}
}
Shader "ShaderProj/21/NoiseFog"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_FogColor ("FogColor", Color) = (1,1,1,1)
_FogDensity ("FogDensity", Float) = 1
_FogStart ("FogStart", Float) = 0
_FogEnd ("FogEnd", Float) = 10
// 决定不均匀效果的噪声纹理
_FogNoise ("FogNoise", 2D) = ""{}
// 噪声系数控制偏移范围
_NoiseAmount ("NoiseAmount", Float) = 1
// 用于控制动态移动速度
_FogXSpeed ("FogXSpeed", Float) = 0.1
_FogYSpeed ("FogYSpeed", Float) = 0.1
}
SubShader
{
ZTest Always
Cull Off
ZWrite Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float2 uv_depth : TEXCOORD1;
//顶点射线 指向四个角的方向向量 (传递到片元时 会自动进行插值 运算)
float4 ray : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _MainTex_TexelSize;
sampler2D _CameraDepthTexture;
fixed4 _FogColor;
fixed _FogDensity;
fixed _FogStart;
fixed _FogEnd;
float4x4 _RayMatrix;
sampler2D _FogNoise;
float _NoiseAmount;
float _FogXSpeed;
float _FogYSpeed;
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv_depth = o.uv;
int index = 0;
if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5)
index = 0;
else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5)
index = 1;
else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5)
index = 2;
else
index = 3;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y<0)
{
o.uv_depth = 1 - o.uv_depth;
index = 3 - index;
}
#endif
o.ray = _RayMatrix[index];
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.ray;
float2 speed = _Time.y * float2(_FogXSpeed, _FogYSpeed);
float noise = (tex2D(_FogNoise, i.uv + speed).r - 0.5) * _NoiseAmount;
float f = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
f = saturate(f * _FogDensity * (1 + noise));
fixed3 color = lerp(tex2D(_MainTex, i.uv).rgb, _FogColor.rgb, f);
return fixed4(color.rgb, 1);
}
ENDCG
}
}
}