Search Unity

Share: Volume Fog Shader

Discussion in 'Shaders' started by shallwaycn, Aug 13, 2012.

  1. shallwaycn

    shallwaycn

    Joined:
    Apr 3, 2012
    Posts:
    202
    seems there has no this kind of shader, so i'm glad to share mine.

    what is volume fog? see these pictures below:

    out of the volume fog:


    inside the volume fog:


    it's a ideally solution to make part of your game world with fog. like this one:


    so, here is the shader: note that this shader only support sphere object, and you should pass the sphere's center to FogParam.xyz, and pass the sphere's radius to FogParam.w. And the example are included in Xffect Editor's Free Editon too.

    Code (csharp):
    1. Shader "Xffect/volume_fog" {
    2. Properties {
    3.     _FogColor ("Fog Color", Color) = (1,1,1,1)
    4. }
    5.  
    6. Category {
    7.     Tags { "Queue"="Transparent+99" "IgnoreProjector"="True" "RenderType"="Transparent" }
    8.     Blend SrcAlpha OneMinusSrcAlpha
    9.     Cull Off Lighting Off ZWrite Off
    10.     ZTest Always
    11.  
    12. SubShader {
    13.     Pass {
    14.  
    15. CGPROGRAM
    16. #pragma target 3.0
    17. #pragma vertex vert
    18. #pragma fragment frag
    19. #include "UnityCG.cginc"
    20.  
    21. inline float CalcVolumeFogIntensity(float3 sphereCenter, float sphereRadius, float3 cameraPosition, float3 viewDirection, float backDepth, float maxDistance, float density)
    22. {
    23.     float3 local = cameraPosition - sphereCenter;
    24.     float  fA      = dot(viewDirection, viewDirection);
    25.     float  fB      = 2 * dot(viewDirection, local);
    26.     float  fC      = dot(local, local) - sphereRadius * sphereRadius;
    27.     float  fD      = fB * fB - 4 * fA * fC;
    28.     if ( fD < 0.0f )
    29.         return 0;
    30.  
    31.   float recpTwoA = 0.5 / fA;
    32.  
    33.   float dist;
    34.  
    35.   if (fD == 0.0f)
    36.   {
    37.     dist = backDepth;
    38.   }
    39.   else
    40.   {
    41.     float DSqrt = sqrt(fD);
    42.     dist = (-fB - DSqrt) * recpTwoA;
    43.   }
    44.  
    45.   dist = min(dist, maxDistance);
    46.   backDepth = min(backDepth, maxDistance);
    47.  
    48.   float sample = dist;
    49.   float fog = 0;
    50.   float step_distance = ( backDepth - dist ) / 10;
    51.   for ( int seg = 0; seg < 10; seg++ )
    52.   {
    53.     float3 position = cameraPosition + viewDirection * sample;
    54.     fog += 1 - saturate( length( sphereCenter - position ) / sphereRadius );
    55.     sample += step_distance;
    56.   }
    57.   fog /= 10;
    58.   fog  = saturate( fog * density );
    59.   return fog;
    60. }
    61.  
    62. fixed4 _FogColor;
    63. sampler2D _CameraDepthTexture;
    64. uniform float4 FogParam;
    65.  
    66. struct v2f {
    67.     float4 pos : SV_POSITION;
    68.     float3 view : TEXCOORD0;
    69.     float4 projPos : TEXCOORD1;
    70. };
    71.  
    72.  
    73. v2f vert (appdata_base v)
    74. {
    75.     v2f o;
    76.     float4 wPos = mul (_Object2World, v.vertex);
    77.     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    78.     o.view = wPos.xyz - _WorldSpaceCameraPos;
    79.     o.projPos = ComputeScreenPos (o.pos);
    80.  
    81.     // move projected z to near plane if point is behind near plane
    82.     float inFrontOf = ( o.pos.z / o.pos.w ) > 0;
    83.     o.pos.z *= inFrontOf;
    84.     return o;
    85. }
    86.  
    87. half4 frag (v2f i) : COLOR
    88. {
    89.     half4 color = half4(1,1,1,1);
    90.     float depth = LinearEyeDepth (UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos))));
    91.     float backDist = length(i.view);
    92.     float3 viewDir = normalize(i.view);
    93.     float fog = CalcVolumeFogIntensity(FogParam.xyz, FogParam.w, _WorldSpaceCameraPos, viewDir, backDist, depth,_FogColor.a);
    94.    
    95.     color.rgb = _FogColor.rgb;
    96.     color.a = fog;
    97.     return color;
    98. }
    99. ENDCG
    100.  
    101.         }
    102.     }
    103. }
    104. Fallback "VertexLit"
    105. }
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class XftVolumeFogObject : MonoBehaviour {
    5.     protected MeshRenderer FogObject;
    6.     protected float Radius = 10f;
    7.     public Material VolumFogMaterial;
    8.     public Color FogColor = Color.white;
    9.  
    10.     void Awake()
    11.     {
    12.         FogObject = gameObject.GetComponent<MeshRenderer>();
    13.         if (FogObject == null)
    14.         {
    15.             Debug.LogError("Volume Fog Object must have a MeshRenderer Component!");
    16.         }
    17.     }
    18.  
    19.     void OnEnable()
    20.     {
    21.         //Note: In forward lightning path, the depth texture is not automatically generated.
    22.         if (Camera.main.depthTextureMode == DepthTextureMode.None)
    23.             Camera.main.depthTextureMode = DepthTextureMode.Depth;
    24.     }
    25.  
    26.  
    27.  
    28.     void Start()
    29.     {
    30.         FogObject.material = VolumFogMaterial;
    31.         FogObject.material.SetColor("_FogColor", FogColor);
    32.         Radius = (transform.localScale.x + transform.localScale.y + transform.localScale.z) / 6;
    33.         FogObject.material.SetVector("FogParam", new Vector4(transform.position.x, transform.position.y, transform.position.z, Radius));
    34.     }
    35.  
    36.     void Update ()
    37.     {
    38.  
    39.     }
    40. }
     
  2. HolBol

    HolBol

    Joined:
    Feb 9, 2010
    Posts:
    2,887
    Does this work with Unity free? I'd assume so, but just checking.

    EDIT: dw, Pro only.
     
    Last edited: Aug 13, 2012
  3. shallwaycn

    shallwaycn

    Joined:
    Apr 3, 2012
    Posts:
    202
    @Fishman92:
    I just find that too, but why?.

    May be the unity free can't generate depth texture?
     
  4. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    You know, you can do it much simpler like this(I read the original hlsl paper in a german site but i dont remember the adress now):

    Its not very optimised as im posting here the very first draft and exact copy of the original paper, (Because i have the optimised one in a commercial pack)
    What you need to know is:
    P = Center of your sphere
    r = radius of your sphere

    This is just a simple no light fragment shader which only has the necessary fog math and a simple texture on the object.
    So, just copy the fog part into your surface shaders or anything else to make it fancier.
     
  5. shallwaycn

    shallwaycn

    Joined:
    Apr 3, 2012
    Posts:
    202
    @aubergine:
    I just took a look at your shader, but i think it's not the same with mine.
    you know, the main problem for volume objects is sampling the fogs from distance, in your shader, the distance is calculated from vertex, it's very simple indeed, but the results is not good.
    anyway, thanks for your advice, it does need some optimization:), i just translated it from my old HLSL shader.
     
  6. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    I didnt really check yours carefully but it sure looks elegant.

    On my version; you can remove the worldposition calculation from the fog and do it in object space (P=0,0,0 and a radius of 1) and also remove the color factor as well. Add it to a spherical object and do some alpha blending and there you have your very fine spherical volume fog object for a very cheap price.

    As i said, its just the basic fog function there, its your job to make it look better.
     
  7. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    ZJP likes this.