这一篇博客主要介绍如何实现shader中的雾效模拟特效。
其实在unity中有相关的组件,可以开启全局雾状特效,在Edit->Render Settings里配置如图——
上图包含了一些设置:雾的颜色,模拟雾采用的方法,雾的浓度(只在采用指数方法时有用),受雾影响的距离起点和终点(只在采用线性方法时有效)。其中,比较重要的是模拟雾采用的方法,即“Fog Mode”这一选项。它可以选择三种方法:
三种模式
Linear、Exponential和Exp2这三种模式实际上是使用了不同的公式计算雾的影响因子。这个影响因子会作为混合雾的颜色和物体原始颜色的参数,来计算最终的混合颜色。
如果影响因子为1,则表明完全没有雾效;如果为0,则表示完全被雾覆盖。而三种模式使用的公式分别如下所示:
Linear
其中Dmax和Dmin分别表示受雾影响的距离起点和终点。Exponential
其中d表示雾的浓度。Exp2
三个等式中的z,表示距离摄像机的远近。
以上三种模式的效果图,如下所示——
原始图片——
它们的雾效配置如下所示:
内部原理实现
1.在Properties块中添加雾效的几个设置参数:
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Ramp ("Ramp Texture", 2D) = "white" {}
_Tooniness ("Tooniness", Range(0.1,20)) = 4
_Outline ("Outline", Range(0,1)) = 0.1
_FogColor("Fog Color", Color) = (1, 0, 0, 0)
_FogIntensity("Fog Intensity", float) = 0.1
_FogStart("Fog Start", float) = 0
_FogEnd("Fog End", float) = 300
}
2.添加模拟雾效的函数:
float4 SimulateFog(float4 pos, float4 col)
{
pos.w = 0.0;
float dist = length(pos);
// float dist = pos.z;
// Linear
// float fogFactor = (_FogEnd - abs(dist)) / (_FogEnd - _FogStart);
// fogFactor = clamp(fogFactor, 0.0, 1.0);
// Exponential
// float fogFactor = exp(-abs(_FogIntensity * dist));
// fogFactor = clamp(fogFactor, 0.0, 1.0);
// Exp2
float fogFactor = exp(-(_FogIntensity * dist) * (_FogIntensity * dist));
fogFactor = clamp(fogFactor, 0.0, 1.0);
float3 afterFog = mix(_FogColor.rgb, col.rgb, fogFactor);
return float4(afterFog, col.a);
}
以上,函数参数pos是指在view space中顶点的位置,因为只有在这个坐标系中,摄像机的位置总是位于原点。在计算距离摄像机远近时,我们有两种可选方式:一种直接使用pos.z得到近似值,一种是使用真正距离摄像机的距离,即计算xyz平方和后开根号的结果。第二种方法由于使用了计算根号这种操作,因此在性能上略微比第一种查一点,但效果也更真实。
源码
Shader "Custom/FogSimulation" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Ramp ("Ramp Texture", 2D) = "white" {}
_Tooniness ("Tooniness", Range(0.1,20)) = 4
_Outline ("Outline", Range(0,1)) = 0.1
_FogColor("Fog Color", Color) = (1, 0, 0, 0)
_FogIntensity("Fog Intensity", float) = 0.1
_FogStart("Fog Start", float) = 0
_FogEnd("Fog End", float) = 300
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _Ramp;
float4 _MainTex_ST;
float _Tooniness;
float _Outline;
float4 _FogColor;
float _FogIntensity;
float _FogStart;
float _FogEnd;
float4 SimulateFog(float4 pos, float4 col)
{
pos.w = 0.0;
float dist = length(pos);
// float dist = pos.z;
// Linear
// float fogFactor = (_FogEnd - abs(dist)) / (_FogEnd - _FogStart);
// fogFactor = clamp(fogFactor, 0.0, 1.0);
// Exponential
// float fogFactor = exp(-abs(_FogIntensity * dist));
// fogFactor = clamp(fogFactor, 0.0, 1.0);
// Exp2
float fogFactor = exp(-(_FogIntensity * dist) * (_FogIntensity * dist));
fogFactor = clamp(fogFactor, 0.0, 1.0);
float3 afterFog = mix(_FogColor.rgb, col.rgb, fogFactor);
return float4(afterFog, col.a);
}
ENDCG
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Front
Lighting Off
ZWrite On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : POSITION;
float4 viewSpacePos : TEXCOORD0;
};
v2f vert (a2v v)
{
v2f o;
float4 pos = mul( UNITY_MATRIX_MV, v.vertex);
float3 normal = mul( (float3x3)UNITY_MATRIX_IT_MV, v.normal);
normal.z = -0.5;
pos = pos + float4(normalize(normal),0) * _Outline;
o.pos = mul(UNITY_MATRIX_P, pos);
o.viewSpacePos = mul( UNITY_MATRIX_MV, v.vertex);
return o;
}
float4 frag(v2f i) : COLOR
{
return SimulateFog(i.viewSpacePos, float4(0, 0, 0, 1));
}
ENDCG
}
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Back
Lighting On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityShaderVariables.cginc"
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
};
struct v2f
{
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float3 normal : TEXCOORD1;
float4 viewSpacePos : TEXCOORD2;
LIGHTING_COORDS(3,4)
};
v2f vert (a2v v)
{
v2f o;
//Transform the vertex to projection space
o.pos = mul( UNITY_MATRIX_MVP, v.vertex);
o.normal = mul((float3x3)_Object2World, SCALED_NORMAL);
//Get the UV coordinates
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
o.viewSpacePos = mul( UNITY_MATRIX_MV, v.vertex);
// pass lighting information to pixel shader
TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
}
float4 frag(v2f i) : COLOR
{
//Get the color of the pixel from the texture
float4 c = tex2D (_MainTex, i.uv);
//Merge the colours
c.rgb = (floor(c.rgb*_Tooniness)/_Tooniness);
//Based on the ambient light
float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;
//Work out this distance of the light
float atten = LIGHT_ATTENUATION(i);
//Angle to the light
float diff = dot (normalize(i.normal), normalize(_WorldSpaceLightPos0.xyz));
diff = diff * 0.5 + 0.5;
//Perform our toon light mapping
diff = tex2D(_Ramp, float2(diff, 0.5));
//Update the colour
lightColor += _LightColor0.rgb * (diff * atten);
//Product the final color
c.rgb = lightColor * c.rgb * 2;
return SimulateFog(i.viewSpacePos, c);
}
ENDCG
}
}
FallBack "Diffuse"
}