[Unity Shaders] Fog Effect Simulation and shadersunity simulation in Unity
Preface
Everyone familiar with Unity knows that Unity can perform basic fog effect simulation. The so-called fog effect is that the object looks like a certain color (usually gray) away from our perspective ). The implementation of this technology is actually very simple, it is to mix the fog color and the color of the object based on the distance between the object and the camera.
There are two ways to set the fog effect in Unity. One of the simplest is to directly enable the global fog effect, which is configured in Edit-> Render Settings, as shown in:
You only need to open the check box next to the "Fog" option. Some settings are included: the fog color, the method used to simulate the fog, and the fog concentration (only useful when the exponential method is used ), the distance and end point affected by fog (valid only when the linear method is used ). The most important thing is the method used to simulate Fog, that is, the "Fog Mode" option. It can be used in three ways:
Another method is to use the Fog command in the shader. Here is a description on the official website.
Three Modes
The Linear, Exponential, and Exp2 modes use different formulas to calculate the impact factors of fog. This influence factor is used as a parameter for the mixed fog color and the original color of the object to calculate the final mixed color. For example, we use the following statement for calculation:
float3 afterFog = mix(_FogColor.rgb, col.rgb, fogFactor);
If the influence factor is 1, it indicates that there is no fog effect at all; if it is 0, it indicates that it is completely covered by fog. The three modes use the following formulas:
- Linear:
Dmax and Dmin indicate the start and end points of the fog.
- Exponential:
Where d indicates the fog concentration.
- Exp2:
Where d indicates the fog concentration.
Z in the three equations indicates the distance from the camera.
To fully understand the implementation principles of the fog effect and the differences between the three methods, we will simulate the fog effect in Fragment Shader.
Fog effect simulated by Unity
We use the following simple cartoon apple scenario (little apple is my favorite ...) To test the fog effect. Original scenario:
The distance from the smallest apple to the camera is about 25 units.
After the global fog Effect of Unity is enabled, three methods are used for simulation. The results are as follows:
Their fog efficiency configuration is as follows:
We will explain the meanings of these parameters later. Now we only need to know that "Fog Density" is useful only when "Fog Mode" is "Exponential" or "Exp2, "Linear Fog Start" and "Linear Fog End" are only useful when "Fog Mode" is "Linear. Note that the above "Linear Fog Start" and "Linear Fog End" parameters are set based on the condition that "the distance from the camera to the smallest apple is about 25 units, only to make the fog effect more obvious.
Now, we can visually understand the similarities and differences between the three methods.
Internal principle of Fog implementation
To fully understand the fog effect algorithm, we now add the implementation of the fog effect algorithm to the existing shader of apple.
The effect of using the three modes of the above Code is as follows:
The fog shader is used in the main part of apple, and the leaf and stem are not used ~ It can be seen that the simulation effect is basically the same as that of Unity.
The complete code is as follows:
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);// Exp2float 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 OnCGPROGRAM#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 spaceo.pos = mul( UNITY_MATRIX_MVP, v.vertex); o.normal = mul((float3x3)_Object2World, SCALED_NORMAL);//Get the UV coordinateso.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 texturefloat4 c = tex2D (_MainTex, i.uv); //Merge the coloursc.rgb = (floor(c.rgb*_Tooniness)/_Tooniness);//Based on the ambient lightfloat3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;//Work out this distance of the lightfloat atten = LIGHT_ATTENUATION(i);//Angle to the lightfloat 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 colourlightColor += _LightColor0.rgb * (diff * atten); //Product the final colorc.rgb = lightColor * c.rgb * 2;return SimulateFog(i.viewSpacePos, c);} ENDCG} } FallBack "Diffuse" }
Conclusion
The Unity document says:
Note that if you use fragment programs, Fog settings of the shader will still be applied. on platforms where there is no fixed function Fog functionality, Unity will patch shaders at runtime to support the requested Fog mode.
That is to say, even if we use our own Fragment Shader, it will still be affected by the Unity Render Settings. Even the custom Vertex & Fragment Shader in Unity does not know the number of layers of upper-layer functions encapsulated by Unity. If global fog effect is enabled, Unity will probably use the fog command in the Fixed rendering pipeline behind it to simulate the fog effect. Therefore, for platforms that do not support fog efficiency functions of fixed pipelines, it will use its own shader (similar to the above) to simulate fog efficiency.
Reference: OpenGL 4 Sharding Language Cookbook