Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Fog + Glow = Glowing Fog

Discussion in 'Shaders' started by guavaman, Jun 14, 2012.

  1. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,490


    This image shows a fully fog-occluded scene (orange fog). The bright spots represent Glow (alpha) coming through the fog. This is because fog is only applied to rgb channels and not alpha as per http://unity3d.com/support/documentation/Components/SL-Fog.html. "Fogging does not modify a blended pixel's alpha value, only its RGB components." Obviously, this creates ugly artifacts if anything glowing is wayyy off in the distance.

    I need fog to modify the alpha channel. I can't find any functions that allow me to get the fog settings from Unity in a shader so I can . There used to be a function called PosFog which seems to be gone as of Unity3. This page under Custom Fog with Final Color Modifier has an example of doing a custom fog, but they're not using the fog settings from Unity.
     
  2. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,490
    Okay, there are a couple of ways to do it. According to this post there are no built-in variables for getting fog settings from Unity in a shader. Shader.SetGlobalFloat can be used to pass the fog information from RenderSettings using a script. (Edit: See next post for how to use it.)

    For a surface shader, you can calculate the fog in a vertex function, then multiply the .Alpha by the fog like so:
    Code (csharp):
    1.  
    2. ...
    3. #pragma surface surf Lambert vertex:vert
    4. ...
    5. float _FogStart;
    6. float _FogEnd;
    7.  
    8. struct Input {
    9.  ...
    10.  half fog;
    11. };
    12.  
    13. void surf (Input IN, inout SurfaceOutput o) {
    14.   ...
    15.   o.Alpha = _Color.a * emis * IN.fog;
    16. }
    17.  
    18. void vert (inout appdata_full v, out Input o) {
    19.   // Linear Alpha Fog
    20.   float4 pos = mul(UNITY_MATRIX_MV, v.vertex); // get pos in camera space
    21.   float distFromViewport = length(pos.xyz);
    22.   o.fog = saturate((_FogEnd - distFromViewport) / (_FogEnd - _FogStart)); // linear fog
    23.  
    24.   // NOTE: This example uses linear fog. You could support the various types of fog
    25.   // by doing some Shader.DisableKeyword / Shader.EnableKeyword, #ifdef stuff like the
    26.   // Pro Standard Assets\Water shader.
    27.  
    28.   // Linear and other fog formulas can be found at http://msdn.microsoft.com/en-us/library/windows/desktop/bb324452(v=vs.85).aspx
    29. }
    Or you could use the finalcolor function to apply the fog after the surface has been calculated:
    Code (csharp):
    1. #pragma surface surf ... finalcolor:finalcolor vertex:vert
    2. ...
    3. void vert (inout appdata_full v, out Input data) {
    4.     // Linear Fog
    5.     float4 pos = mul(UNITY_MATRIX_MV, v.vertex); // get pos in camera space
    6.     float distFromViewport = length(pos.xyz);
    7.     data.fog = saturate((_FogEnd - distFromViewport) / (_FogEnd - _FogStart)); // linear fog
    8. }
    9.  
    10. void finalcolor (Input IN, SurfaceOutput o, inout fixed4 color) {
    11.     fixed3 fogColor = fixed3(0, 0, 0); // black fog
    12.     color.a = lerp(fogColor, color.a, IN.fog);
    13. }
    14.  
    Or, if you prefer using passes you can share, this works on top of a surface shader:
    Code (csharp):
    1. // Alpha Channel Fog Pass (Deferred):
    2. Pass {
    3.   Blend Zero One, DstAlpha Zero
    4.   Name "AlphaFog_Deferred_Pass"
    5.   Tags {"LightMode" = "PrepassFinal"}
    6.   Fog { Mode Off }
    7.    
    8.   CGPROGRAM
    9.   #pragma exclude_renderers xbox360
    10.   #pragma vertex vert
    11.   #pragma fragment frag
    12.   #pragma fragmentoption ARB_precision_hint_fastest
    13.   #include "UnityCG.cginc"
    14.   #include "AutoLight.cginc"
    15.  
    16.   float _FogStart;
    17.   float _FogEnd;
    18.  
    19.   struct v2f {
    20.     float4 pos : SV_POSITION;
    21.     half fog;
    22.   };
    23.  
    24.   v2f vert (appdata_full v) {
    25.     v2f o;
    26.     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    27.    
    28.     // Linear fog
    29.     float4 pos = mul(UNITY_MATRIX_MV, v.vertex); // get pos in camera space
    30.     float distFromViewport = length(pos.xyz);
    31.     o.fog = saturate((_FogEnd - distFromViewport) / (_FogEnd - _FogStart)); // linear fog
    32.    
    33.     // http://msdn.microsoft.com/en-us/library/windows/desktop/bb324452(v=vs.85).aspx
    34.     // linearFog = (end - distFromViewport) / (end - start)
    35.    
    36.     return o;
    37.   }
    38.  
    39.   float4 frag (v2f i) : COLOR {
    40.     half4 c;
    41.     c.rgb = 0;
    42.     c.a = i.fog;
    43.     return c;
    44.   }
    45.   ENDCG
    46. }
    47.  
    48. // Alpha Channel Fog Pass (Forward):
    49. Pass {
    50.   Blend Zero One, DstAlpha Zero
    51.   Name "AlphaFog_Forward_Pass"
    52.   Tags {"LightMode" = "ForwardBase"}
    53.   Fog { Mode Off }
    54.    
    55.   CGPROGRAM
    56.   #pragma exclude_renderers xbox360
    57.   #pragma vertex vert
    58.   #pragma fragment frag
    59.   #pragma fragmentoption ARB_precision_hint_fastest
    60.   #include "UnityCG.cginc"
    61.   #include "AutoLight.cginc"
    62.  
    63.   float _FogStart;
    64.   float _FogEnd;
    65.  
    66.   struct v2f {
    67.     float4 pos : SV_POSITION;
    68.     half fog;
    69.   };
    70.  
    71.   v2f vert (appdata_full v) {
    72.     v2f o;
    73.     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    74.    
    75.     // Linear fog
    76.     float4 pos = mul(UNITY_MATRIX_MV, v.vertex); // get pos in camera space
    77.     float distFromViewport = length(pos.xyz);
    78.     o.fog = saturate((_FogEnd - distFromViewport) / (_FogEnd - _FogStart)); // linear fog
    79.    
    80.     // http://msdn.microsoft.com/en-us/library/windows/desktop/bb324452(v=vs.85).aspx
    81.     // linearFog = (end - distFromViewport) / (end - start)
    82.    
    83.     return o;
    84.   }
    85.  
    86.   float4 frag (v2f i) : COLOR {
    87.     half4 c;
    88.     c.rgb = 0;
    89.     c.a = i.fog;
    90.     return c;
    91.   }
    92.   ENDCG
    93. }
     
    Last edited: Jun 19, 2012
  3. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,490
    If anyone happens upon this thread, I thought I'd also post how to use the Shader.SetGlobalFloat.

    Shader.SetGlobalFloat can be used to set global values that your shaders can access without having to explicitly pass the fog value to each one. Simply have a script that runs on level start that sets the global float values for the current scene. In my case:

    Code (csharp):
    1. if(RenderSettings.fog) {
    2.     // Linear fog
    3.     if(RenderSettings.fogMode == FogMode.Linear) {
    4.         Shader.SetGlobalFloat("_FogStartDistance", RenderSettings.fogStartDistance);
    5.         Shader.SetGlobalFloat("_FogEndDistance", RenderSettings.fogEndDistance);
    6.     }
    7. }
    Then all shaders can access those values by simply not declaring them in the properties, but go ahead and declare them under the CGPROGRAM:

    Code (csharp):
    1. fixed _FogStartDistance; // global
    2. fixed _FogEndDistance; // global
    Your shaders can now access the global values.
     
    Last edited: Jun 19, 2012