Search Unity

Spherical fog distance

Discussion in 'Shaders' started by Marrt, Jul 25, 2017.

  1. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Unity renders fog based on depth. This leads to the "fog-wall" rotating with the camera.

    Is there a cheap way(mobile) of using a radial distance as depth? How would i tell Unity to render fog differently anyhow, i am just starting shaders?

    It doesn't have to be perfectly radial, only approximated, i just want to reduce the effect of buildings popping in and out when rotating the camera:

    fog.gif


    Maybe something that uses the pixel position (ComputeScreenPos) and factors it in, so i can increase the fog value at the edges of the screen. but i have no idea on how to go about this
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Using distance instead of depth is fairly simple (and more correct.) It's fairly cheap too, so I don't know why this isn't the default, because it prevents this type of issue. As far as I know, there is no distance mode for the Unity fog, so you'll have to write it yourself. The common way to do this is to add it as a postprocess effect on the Camera. (But you can also add it directly to the shader itself in some cases.)

    Basic formula is:
    oneminusfog = exp ( -distance ( camera, pixel ) * fog_density )
    result = lerp ( fog_color, result, oneminusfog )
     
    Marrt likes this.
  3. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    It's even cheaper if you use linear distance instead of exp if you are emulating old school visual.
     
  4. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Yes, I was in doubt to mention that. There are other (cheaper) blending ways you can still base on the same distance metric. But exponential is the physical way to do it.
     
  5. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    I did the addedum because the game looks retro, but could be an artifact of small compressed gif :p
     
  6. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Thanks for the answer guys, as of now i am struggling on where to put this stuff.

    I've got my copy of the diffuse legacy terrain shader. There is that line that says "#pragma multi_compile_fog" that contols fog.

    - i guess i must overwrite this line for each shader i want to affect with it. Something like #pragma multi_myNewFog
    - How and where do i define that "multi_myNewFog"
     
    Last edited: Jul 27, 2017
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    It's a bit more complex than than just replacing the #pragma, and you may not even want to do that depending on how you want to implement this.

    My solution was to replace the UnityCG.cginc file with one that implemented distance fog for everything (Unity uses depth fog). For a custom shader you'll want to pass the world or view position from the vertex shader and calculate the distance in the fragment shader and then lerp to a color based on that distance. Assuming you don't care about different fog types, you can remove the #pragma line all together. I would suggest using the unity_FogColor and unity_FogParams still just for simplicity, but you should look in UnityCG.cginc to see what Unity is doing for fog.
     
  8. FlaxenFlash

    FlaxenFlash

    Joined:
    Oct 15, 2015
    Posts:
    28
    jacobian and Marrt like this.
  9. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Wow, Thanks! This is exactly what i also needed, i just downloaded the package, lets see if this effect is mobile friendly...

    EDIT: Nope, the skybox blending is a postprocessing effect, so it wont do any good on mobile, but the radial fog seems to be just what i need, now i just need to extract it
     
    Last edited: Jul 28, 2017
  10. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Small followup:

    I don't know if i did something that caused it but it seems that vertexLit shaders use radial fog by default
     
  11. nbg_yalta

    nbg_yalta

    Joined:
    Oct 3, 2012
    Posts:
    378
    Hi, i've faced same problem, do you have solution? Thanks
     
  12. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    I tinkered around with shader programming. You would have to create your own fog-shader or change the FOG-Code in the "UnityCG.cginc" file. I did not get far with it. I shifted this task back in my schedule because most of my shaders are vertex anyway. But this is the work in progress testing shader i got so far:

    Code (CSharp):
    1.  
    2. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    3.  
    4. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    5.  
    6. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    7.  
    8. // Simplified VertexLit shader. Differences from regular VertexLit one:
    9. // - no per-material color
    10. // - no specular
    11. // - no emission
    12.  
    13. // - default "Mobile/VertexLit"
    14.  
    15. Shader "InfinityTerrain/ManualFog"
    16. {
    17.     Properties
    18.     {
    19.         _MainTex ("Texture", 2D) = "white" {}
    20.        
    21.     }
    22.     SubShader
    23.     {
    24.         Tags { "RenderType"="Opaque" }
    25.         LOD 100
    26.  
    27.         Pass
    28.         {
    29.             Tags { "LightMode" = "Vertex" }
    30.             //Tags { "LightMode" = "ForwardBase" }
    31.         //    Cull Back
    32.          //   Lighting On
    33.  
    34.             CGPROGRAM
    35.  
    36.             #pragma vertex vert
    37.             #pragma fragment frag
    38.             // make fog work
    39.             #pragma multi_compile_fog
    40.  
    41.             #pragma glsl//tex2Dlod
    42.  
    43.             #include "UnityCG.cginc"
    44.  
    45.             struct appdata
    46.             {
    47.                 fixed4 vertex : POSITION;
    48.                 fixed2 uv : TEXCOORD0;
    49.                                             fixed3 normal : NORMAL;
    50.                                             fixed3 color : COLOR;
    51.             };
    52.  
    53.             struct v2f
    54.             {
    55.                 fixed2 uv : TEXCOORD0;
    56. //UNITY_FOG_COORDS(1)
    57.                 fixed4 vertex : SV_POSITION;
    58.                                             fixed3 color : COLOR;
    59.             };
    60.  
    61.             sampler2D _MainTex;
    62.             fixed4 _MainTex_ST;
    63.            
    64.             v2f vert (appdata v)
    65.             {
    66.                 v2f o;
    67.                 o.vertex    = UnityObjectToClipPos(v.vertex);
    68.                 o.uv        = TRANSFORM_TEX(v.uv, _MainTex);        //#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)   //?v.uv.xy *_MainTex##_ST.xy +MainTex##_ST.zw;//
    69.                
    70.                 o.color        = ShadeVertexLights(v.vertex,v.normal);
    71.                
    72.             //Texture
    73.             //read tex in vert shader already, no need to do it in frag shader
    74.                 fixed4 tex = tex2Dlod (_MainTex, fixed4(v.uv.xy,0,0));
    75.                 o.color = tex *o.color;
    76.  
    77.  
    78.             //Distance
    79.             //    o.fogDepth.x = length(mul (UNITY_MATRIX_MV, v.vertex).xyz);
    80.             //    o.fogDepth.y = o.fogDepth.x * unity_FogDensity;
    81.             //
    82.             //    fixed dist    = length(mul (UNITY_MATRIX_MV, v.vertex).xyz);
    83.             //    fixed fac    = dist * 0.0025;
    84.  
    85.                 fixed dist    = length(ObjSpaceViewDir(v.vertex));
    86.                 fixed fac    = clamp( (dist) * 0.0125, 0, 1);
    87.            
    88.             //    o.color = fixed3(fac,fac,fac);
    89.                 o.color = lerp( o.color,        unity_FogColor    , fac );
    90.  
    91.  
    92.             //    UNITY_LIGHTMODEL_AMBIENT
    93.  
    94.             //debug
    95.             //    o.color = lerp( o.color,        fixed3(1,0,0)    , fac );
    96.  
    97.             //check edge popping
    98.             //    if(fac<1){fac = 0;}
    99.             //    o.color = lerp( fixed3(1,0,0),    unity_FogColor    , fac );
    100.            
    101.  
    102.            
    103.  
    104. //UNITY_TRANSFER_FOG(o,o.vertex);
    105.                
    106.                 return o;
    107.             }
    108.          //   fixed4
    109.             fixed3 frag (v2f i) : SV_Target
    110.             {
    111.                 return i.color;
    112.                
    113.                 //    fixed4 col;
    114.                 //    col.rgb = i.color;//fog debug
    115.                    
    116.  
    117.  
    118.                 //    col = tex2D(_MainTex, i.uv);    // sample the texture
    119.                 //    col.rgb = col.rgb  * i.color;//add lighting https://forum.unity.com/threads/vertexlit-surface-or-cg-shader.209649/
    120.  
    121.                    
    122.  
    123. //UNITY_APPLY_FOG(i.fogCoord, col);    // apply fog
    124.                 //    return col;
    125.             }
    126.             ENDCG
    127.         }
    128.     }
    129. }
    130.  
    131.  
    132.  
    133. /* FROM "UnityCG.cginc"
    134. // ------------------------------------------------------------------
    135. //  Fog helpers
    136. //
    137. //  multi_compile_fog Will compile fog variants.
    138. //  UNITY_FOG_COORDS(texcoordindex) Declares the fog data interpolator.
    139. //  UNITY_TRANSFER_FOG(outputStruct,clipspacePos) Outputs fog data from the vertex shader.
    140. //  UNITY_APPLY_FOG(fogData,col) Applies fog to color "col". Automatically applies black fog when in forward-additive pass.
    141. //  Can also use UNITY_APPLY_FOG_COLOR to supply your own fog color.
    142.  
    143. // In case someone by accident tries to compile fog code in one of the g-buffer or shadow passes:
    144. // treat it as fog is off.
    145. #if defined(UNITY_PASS_PREPASSBASE) || defined(UNITY_PASS_DEFERRED) || defined(UNITY_PASS_SHADOWCASTER)
    146. #undef FOG_LINEAR
    147. #undef FOG_EXP
    148. #undef FOG_EXP2
    149. #endif
    150.  
    151. #if defined(UNITY_REVERSED_Z)
    152.     //D3d with reversed Z => z clip range is [near, 0] -> remapping to [0, far]
    153.     //max is required to protect ourselves from near plane not being correct/meaningfull in case of oblique matrices.
    154.     #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) max(((1.0-(coord)/_ProjectionParams.y)*_ProjectionParams.z),0)
    155. #elif UNITY_UV_STARTS_AT_TOP
    156.     //D3d without reversed z => z clip range is [0, far] -> nothing to do
    157.     #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) (coord)
    158. #else
    159.     //Opengl => z clip range is [-near, far] -> should remap in theory but dont do it in practice to save some perf (range is close enought)
    160.     #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) (coord)
    161. #endif
    162.  
    163. #if defined(FOG_LINEAR)
    164.     // factor = (end-z)/(end-start) = z * (-1/(end-start)) + (end/(end-start))
    165.     #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = (coord) * unity_FogParams.z + unity_FogParams.w
    166.                                 #elif defined(FOG_EXP)
    167.                                     // factor = exp(-density*z)
    168.                                     #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.y * (coord); unityFogFactor = exp2(-unityFogFactor)
    169.                                 #elif defined(FOG_EXP2)
    170.                                     // factor = exp(-(density*z)^2)
    171.                                     #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.x * (coord); unityFogFactor = exp2(-unityFogFactor*unityFogFactor)
    172.                                 #else
    173.                                     #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = 0.0
    174.                                 #endif
    175.  
    176. #define UNITY_CALC_FOG_FACTOR(coord) UNITY_CALC_FOG_FACTOR_RAW(UNITY_Z_0_FAR_FROM_CLIPSPACE(coord))
    177.  
    178. #define UNITY_FOG_COORDS_PACKED(idx, vectype) vectype fogCoord : TEXCOORD##idx;
    179.  
    180. #if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
    181.     #define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1)
    182.  
    183.     #if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE)
    184.         // mobile or SM2.0: calculate fog factor per-vertex
    185.         #define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord.x = unityFogFactor
    186.     #else
    187.         // SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel
    188.         #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z
    189.     #endif
    190. #else
    191.     #define UNITY_FOG_COORDS(idx)
    192.     #define UNITY_TRANSFER_FOG(o,outpos)
    193. #endif
    194.  
    195. #define UNITY_FOG_LERP_COLOR(col,fogCol,fogFac) col.rgb = lerp((fogCol).rgb, (col).rgb, saturate(fogFac))
    196.  
    197.  
    198. #if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
    199.     #if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE)
    200.         // mobile or SM2.0: fog factor was already calculated per-vertex, so just lerp the color
    201.         #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_FOG_LERP_COLOR(col,fogCol,(coord).x)
    202.     #else
    203.         // SM3.0 and PC/console: calculate fog factor and lerp fog color
    204.         #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_CALC_FOG_FACTOR((coord).x); UNITY_FOG_LERP_COLOR(col,fogCol,unityFogFactor)
    205.     #endif
    206. #else
    207.     #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol)
    208. #endif
    209.  
    210. #ifdef UNITY_PASS_FORWARDADD
    211.     #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,fixed4(0,0,0,0))
    212. #else
    213.     #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,unity_FogColor)
    214. #endif
    215. */
    216.  
    217.  
    218. /*{
    219.  
    220.     //https://forum.unity.com/threads/solved-accessing-fog-parameters-in-cg-shader.249886/
    221.     Properties {
    222.         _MainTex ("Base (RGB)", 2D) = "black" {}
    223.     }
    224.  
    225.     SubShader {
    226.         Tags { "RenderType" = "Opaque" }
    227.         Cull off
    228.     //   Lighting Off
    229.         Blend SrcAlpha OneMinusSrcAlpha
    230.         Fog {Mode Off}
    231.  
    232.         Pass {
    233.             CGPROGRAM
    234.             #pragma target 2.0
    235.             #pragma vertex vert
    236.             #pragma fragment frag
    237.             //#pragma exclude_renderers d3d11 xbox360 ps3 flash d3d11_9x
    238.      
    239.             #include "UnityCG.cginc"
    240.      
    241.     //uniform fixed3 unity_FogColor;
    242.             uniform half unity_FogDensity;
    243.             sampler2D _MainTex;
    244.      
    245.             struct vertexInput {
    246.                 float4 vertex : POSITION;
    247.                 float2 texcoord : TEXCOORD0;
    248.                                                 float3 normal : NORMAL;
    249.                                                 fixed4 color : COLOR;
    250.             };
    251.      
    252.             struct v2f {
    253.                 float4 scrPos : SV_POSITION;
    254.                 half2 srcUVs: TEXCOORD0;
    255.                 half2 fogDepth: TEXCOORD1; // linear depth in x and depth multiplied by density in y
    256.                                                 float3 color : COLOR;
    257.             };
    258.      
    259.      
    260.      
    261.             v2f vert (vertexInput v)
    262.             {
    263.                 v2f o;
    264.                 o.scrPos = UnityObjectToClipPos(v.vertex);
    265.                 //o.srcUVs = TRANSFORM_TEX(v.texcoord, _MainTex);
    266.                 o.srcUVs = v.texcoord;
    267.          
    268.             o.color = ShadeVertexLights(v.vertex,v.normal);
    269.  
    270.                 // this is UNITY_FOG_COORDS
    271.               //  o.fogDepth.x = length(mul (UNITY_MATRIX_MV, v.vertex).xyz);
    272.               //  o.fogDepth.y = o.fogDepth.x * unity_FogDensity;
    273.  
    274.                 o.fogDepth.x = length(mul (UNITY_MATRIX_MV, v.vertex).xyz);
    275.                 o.fogDepth.y = o.fogDepth.x * unity_FogDensity;
    276.  
    277.                 return o;
    278.             }
    279.      
    280.             fixed4 frag (v2f i) : COLOR
    281.             {
    282.                 fixed3 clr = tex2D(_MainTex, i.srcUVs).rgb;
    283.          
    284.                 clr.rgb = clr.rgb  * i.color;
    285.  
    286.  
    287.                 // Exp2 mode:
    288.                 //float fogAmt = i.fogDepth.y * i.fogDepth.y;
    289.                // fogAmt = exp(-fogAmt);
    290.  
    291.  
    292.                 float fogAmt = i.fogDepth.y;
    293.  
    294.  
    295.  
    296.                 clr = lerp( unity_FogColor, clr, fogAmt);
    297.          
    298.                return fixed4(
    299.                    clr,
    300.                    1.0
    301.                );
    302.             }
    303.             ENDCG
    304.         }
    305.     }
    306.  
    307. }*/
    308.  
     
    Last edited: Jul 30, 2018
    nbg_yalta likes this.
  13. rostik1975

    rostik1975

    Joined:
    May 14, 2015
    Posts:
    44
    How exactly should I modify UnityCG,cginc file to make the Linear fog use radial distance from the camera?
    Should I edit this part:

    Code (CSharp):
    1. #if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
    2.     #define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1)
    3.  
    4.     #if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE)
    5.         // mobile or SM2.0: calculate fog factor per-vertex
    6.         #define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord.x = unityFogFactor
    7.     #else
    8.         // SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel
    9.         #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z
    10.     #endif
    11. #else
    12.     #define UNITY_FOG_COORDS(idx)
    13.     #define UNITY_TRANSFER_FOG(o,outpos)
    14. #endif
    15.  
    and replace
    UNITY_CALC_FOG_FACTOR((outpos).z)
    with
    UNITY_CALC_FOG_FACTOR_RAW(length(i.eyeVec))
    ?

    Thank you very much.
     
  14. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    I don't know, i ended up using only vertex shaders in my project where radial fog is used by default.

    You could just try what you written above and report your success.
    Here is how you would include a custom cginc file (if you don't want to modify your default one)
    https://forum.unity.com/threads/include-custom-cginc-files.94518/
     
  15. rostik1975

    rostik1975

    Joined:
    May 14, 2015
    Posts:
    44
    Thank you. But unfortunately this gives me an error: "Shader error in 'Standard': undeclared identifier 'i' at /Programs/Unity5.6.4/Editor/Data/CGIncludes/UnityStandardCore.cginc(401) (on d3d11)"

    In the part of code starting after this line:
    // SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel
    I tried to replace
    #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z
    with
    #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = length(i.eyeVec)

    or with
    #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = UNITY_CALC_FOG_FACTOR_RAW(length(i.eyeVec))
     
  16. harpingseal

    harpingseal

    Joined:
    Sep 3, 2020
    Posts:
    57
    Vertex shader calculate stuff per vertex,If your poly count is high,Youll get a sphere fog,If its low,Youll get bugged one
     
  17. Wim-Wouters

    Wim-Wouters

    Joined:
    Sep 26, 2012
    Posts:
    36
    Any update on this? Trying to create a custom fog solution that has radial falloff, but also allows me to define vertical gradient colors (just 2: from-to).
    Working with URP, got it working as post processing effect, runs very smooth in WebGL, even on mobile, but seems to heavy to run in standalone VR. Very strange.... idea's?
     
  18. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Post processing is very expensive in VR.
     
  19. POOKSHANK

    POOKSHANK

    Joined:
    Feb 8, 2022
    Posts:
    305
    You could add a custom fog pass in a shader, I think it's much easier than dealing with unity's fog as I just don't like the native's controls and can control all my behaviors in one script and update to all cams and whatnot easily
     
  20. MUGIK

    MUGIK

    Joined:
    Jul 2, 2015
    Posts:
    481
    In the case of URP it's fairly easy to make for "spherical".
    Embed UniversalRP package to be able to modify it.
    Navigate to "Packages/$UniversalRP$/ShaderLibrary/ShaderVariablesFunctions.hlsl"
    Find InitializeInputDataFog function and change it to this:
    Code (CSharp):
    1. real InitializeInputDataFog(float4 positionWS, real vertFogFactor)
    2. {
    3.     real fogFactor = 0.0;
    4. #if defined(_FOG_FRAGMENT)
    5.     #if (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))
    6.         // Compiler eliminates unused math --> matrix.column_z * vec
    7.         //float viewZ = -(mul(UNITY_MATRIX_V, positionWS).z); unity-provided implementation
    8.         float viewZ = length(mul(UNITY_MATRIX_V, positionWS)); // spherical fog factor
    9.         // View Z is 0 at camera pos, remap 0 to near plane.
    10.         float nearToFarZ = max(viewZ - _ProjectionParams.y, 0);
    11.         fogFactor = ComputeFogFactorZ0ToFar(nearToFarZ);
    12.     #endif
    13. #else
    14.     fogFactor = vertFogFactor;
    15. #endif
    16.     return fogFactor;
    17. }
     
    Marrt likes this.