Search Unity

Modify standard fog in UnityCG.cginc

Discussion in 'General Graphics' started by Recluse, Mar 14, 2019.

  1. Recluse

    Recluse

    Joined:
    May 16, 2010
    Posts:
    485
    Unity's global fog (Rendersettings.fog) is calculated on depth, not distance. This causes geometry near the edge of screen, to fade back into view as you turn the camera around. This effect is especially distracting in VR. Is there a way to modify UnityCG.cginc to calculate global fog radially, based on distance?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Yes and no.

    The way most shaders are setup they call a function along with the clip space position, and passing a "fog factor" as a single value from the vertex to the fragment shader. That fog factor is, kind of insanely, the clip space z, which it then reverses into linear depth in the fragment shader and calculates the fog from. While you could extract the view space position from the clip space, Unity does not actually provide an inverse projection matrix that matches the UNITY_MATRIX_P used to calculate the clip space position. Also, there's no way to override the UnityCG.cginc file with out replacing it manually in the Unity editor installation folders, not in the project, which means every time you upgrade you have to manually copy the file.

    Curiously, the older Legacy shaders all do radial distance fog, but by calculating the fog opacity from the per vertex distance and passing that to the fragment shader, which can lead to some weird artifacts, though it would at least be stable while turning your head.

    The good news is many built in Unity shaders do actually have access to either a world position and/or a view space position in the fragment shader already. The problem is what it's called is different in every shader, and some only have it when certain keywords are enabled on the shader.

    My hack-tastic solution was to make copies of the common shaders I knew we were going to use, and either hand modify them to pass the information I needed, or use CGINCLUDE blocks to change the fog macros. Here's approximately what I used for the Standard shader for example.

    Code (CSharp):
    1. CGINCLUDE
    2.     #include "UnityCG.cginc"
    3.     #if !defined(UNITY_PASS_META) && (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))
    4.         #ifdef UNITY_CALC_FOG_FACTOR
    5.             #undef UNITY_CALC_FOG_FACTOR
    6.         #endif
    7.  
    8.         #define UNITY_CALC_FOG_FACTOR(coord) UNITY_CALC_FOG_FACTOR_RAW(length(i.eyeVec))
    9.     #endif
    10. ENDCG
    This works because macros are inlined, and at the point where the Standard shader calls the UNITY_APPLY_FOG() macro there just happens to be an i.eyeVec available!

    edit: fix the example code not compiling
     
    Last edited: Oct 15, 2019
    richardkettlewell likes this.
  3. Recluse

    Recluse

    Joined:
    May 16, 2010
    Posts:
    485
    Thanks, I'll look into this. I'm not very experienced beyond using ShaderForge to create some custom shaders for my game. It'll be tricky if I have to modify each individual shaders I'm using. I have made a skybox shader which blends the sky and clouds with Unity's fog - I might try working on that to see if I can lose the Unity fog and do everything there.
     
  4. rostik1975

    rostik1975

    Joined:
    May 14, 2015
    Posts:
    44
    Could you please tell how should I modify UnityCG.cginc file in Unity installation folder, to make the Linear fog use radial distance from the camera?

    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))

    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)".

    Thank you.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    I cannot, because there isn't a way to do this in a way that works universally for all shaders. Since Unity's fog was not written with this functionality in mind, the data needed isn't guaranteed to be easily available, or consistently named.

    As for the error you're getting, try o.eyeVec instead of i.eyeVec. The function you're modifying is called in the vertex shader where my example assumed the fog factor was being called in the fragment shader. But know that that what you're trying to do won't produce radial fog. You can't transfer a single float value and get proper radial fog. You must transfer the actual float3 position value for radial fog to work correctly unless all of your meshes are highly tessellated. Plus only modifying the transferred data will break other stuff as the functions in the fragment shader that apply the fog will still be expecting values to be different than what you're giving it and will be applying various bits of math that no longer necessarily make sense.
     
    Last edited: Sep 9, 2019
  6. rostik1975

    rostik1975

    Joined:
    May 14, 2015
    Posts:
    44
    Thank you. I tried to implement your solution by modifying Standard shader copy:
    Code (CSharp):
    1. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    2.  
    3. CGINCLUDE
    4.     #include "UnityCG.cginc"
    5.     #if !defined(UNITY_PASS_META) && (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))
    6.         #ifdef UNITY_CALC_FOG_FACTOR
    7.             #undef UNITY_CALC_FOG_FACTOR
    8.         #endif
    9.  
    10.         #define UNITY_CALC_FOG_FACTOR(UNITY_CALC_FOG_FACTOR_RAW(length(i.eyeVec)))
    11.     #endif
    12. ENDCG
    13.  
    14. Shader "MyStandard"
    15. { ...
    16.  
    but I still get the error:
    Shader error in 'MyStandard': syntax error : unexpected token '(' at line 11 (on d3d11)

    Could you please tell me how exactly I should modify the shader, to have the radial distance fog work?
    Thank you very much!
     
    Last edited: Oct 14, 2019
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You can't put code before the
    Shader
    line. The Standard.shader already has a
    CGINCLUDE
    block near the start, the code between
    CGINCLUDE
    and
    ENDCG
    needs to go there, or inside the
    SubShader
    .
     
  8. rostik1975

    rostik1975

    Joined:
    May 14, 2015
    Posts:
    44
    I tried to put this part of code to CGINCLUDE block, but still have the error on this line:
    #define UNITY_CALC_FOG_FACTOR(UNITY_CALC_FOG_FACTOR_RAW(length(i.eyeVec)))

    Here is the altered shader:
    Code (CSharp):
    1. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    2.  
    3. Shader "MyStandard"
    4. {
    5.     Properties
    6.     {
    7.         _Color("Color", Color) = (1,1,1,1)
    8.         _MainTex("Albedo", 2D) = "white" {}
    9.      
    10.         _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
    11.  
    12.         _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
    13.         _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
    14.         [Enum(Metallic Alpha,0,Albedo Alpha,1)] _SmoothnessTextureChannel ("Smoothness texture channel", Float) = 0
    15.  
    16.         [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
    17.         _MetallicGlossMap("Metallic", 2D) = "white" {}
    18.  
    19.         [ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
    20.         [ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0
    21.  
    22.         _BumpScale("Scale", Float) = 1.0
    23.         _BumpMap("Normal Map", 2D) = "bump" {}
    24.  
    25.         _Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02
    26.         _ParallaxMap ("Height Map", 2D) = "black" {}
    27.  
    28.         _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
    29.         _OcclusionMap("Occlusion", 2D) = "white" {}
    30.  
    31.         _EmissionColor("Color", Color) = (0,0,0)
    32.         _EmissionMap("Emission", 2D) = "white" {}
    33.      
    34.         _DetailMask("Detail Mask", 2D) = "white" {}
    35.  
    36.         _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
    37.         _DetailNormalMapScale("Scale", Float) = 1.0
    38.         _DetailNormalMap("Normal Map", 2D) = "bump" {}
    39.  
    40.         [Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0
    41.  
    42.  
    43.         // Blending state
    44.         [HideInInspector] _Mode ("__mode", Float) = 0.0
    45.         [HideInInspector] _SrcBlend ("__src", Float) = 1.0
    46.         [HideInInspector] _DstBlend ("__dst", Float) = 0.0
    47.         [HideInInspector] _ZWrite ("__zw", Float) = 1.0
    48.     }
    49.  
    50.     CGINCLUDE
    51.         #define UNITY_SETUP_BRDF_INPUT MetallicSetup
    52.         #include "UnityCG.cginc"
    53.         #if !defined(UNITY_PASS_META) && (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))
    54.             #ifdef UNITY_CALC_FOG_FACTOR
    55.                 #undef UNITY_CALC_FOG_FACTOR
    56.             #endif
    57.             #define UNITY_CALC_FOG_FACTOR(UNITY_CALC_FOG_FACTOR_RAW(length(i.eyeVec)))
    58.         #endif
    59.     ENDCG
    60.  
    61.  
    62.     SubShader
    63.     {
    64.         Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }
    65.         LOD 300
    66.  
    67.         // ------------------------------------------------------------------
    68.         //  Base forward pass (directional light, emission, lightmaps, ...)
    69.         Pass
    70.         {
    71.             Name "FORWARD"
    72.             Tags { "LightMode" = "ForwardBase" }
    73.  
    74.             Blend [_SrcBlend] [_DstBlend]
    75.             ZWrite [_ZWrite]
    76.          
    77.             CGPROGRAM
    78.             #pragma target 3.0
    79.  
    80.             // -------------------------------------
    81.  
    82.             #pragma shader_feature _NORMALMAP
    83.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    84.             #pragma shader_feature _EMISSION
    85.             #pragma shader_feature _METALLICGLOSSMAP
    86.             #pragma shader_feature ___ _DETAIL_MULX2
    87.             #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    88.             #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
    89.             #pragma shader_feature _ _GLOSSYREFLECTIONS_OFF
    90.             #pragma shader_feature _PARALLAXMAP
    91.  
    92.             #pragma multi_compile_fwdbase
    93.             #pragma multi_compile_fog
    94.             #pragma multi_compile_instancing
    95.  
    96.             #pragma vertex vertBase
    97.             #pragma fragment fragBase
    98.             #include "UnityStandardCoreForward.cginc"
    99.          
    100.             ENDCG
    101.         }
    102.         // ------------------------------------------------------------------
    103.         //  Additive forward pass (one light per pass)
    104.         Pass
    105.         {
    106.             Name "FORWARD_DELTA"
    107.             Tags { "LightMode" = "ForwardAdd" }
    108.             Blend [_SrcBlend] One
    109.             Fog { Color (0,0,0,0) } // in additive pass fog should be black
    110.             ZWrite Off
    111.             ZTest LEqual
    112.  
    113.             CGPROGRAM
    114.             #pragma target 3.0
    115.  
    116.             // -------------------------------------
    117.  
    118.             #pragma shader_feature _NORMALMAP
    119.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    120.             #pragma shader_feature _METALLICGLOSSMAP
    121.             #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    122.             #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
    123.             #pragma shader_feature ___ _DETAIL_MULX2
    124.             #pragma shader_feature _PARALLAXMAP
    125.  
    126.             #pragma multi_compile_fwdadd_fullshadows
    127.             #pragma multi_compile_fog
    128.  
    129.             #pragma vertex vertAdd
    130.             #pragma fragment fragAdd
    131.             #include "UnityStandardCoreForward.cginc"
    132.  
    133.             ENDCG
    134.         }
    135.         // ------------------------------------------------------------------
    136.         //  Shadow rendering pass
    137.         Pass {
    138.             Name "ShadowCaster"
    139.             Tags { "LightMode" = "ShadowCaster" }
    140.  
    141.             ZWrite On ZTest LEqual
    142.  
    143.             CGPROGRAM
    144.             #pragma target 3.0
    145.  
    146.             // -------------------------------------
    147.  
    148.  
    149.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    150.             #pragma shader_feature _METALLICGLOSSMAP
    151.             #pragma shader_feature _PARALLAXMAP
    152.             #pragma multi_compile_shadowcaster
    153.             #pragma multi_compile_instancing
    154.  
    155.             #pragma vertex vertShadowCaster
    156.             #pragma fragment fragShadowCaster
    157.  
    158.             #include "UnityStandardShadow.cginc"
    159.  
    160.             ENDCG
    161.         }
    162.         // ------------------------------------------------------------------
    163.         //  Deferred pass
    164.         Pass
    165.         {
    166.  
    167.             Name "DEFERRED"
    168.             Tags { "LightMode" = "Deferred" }
    169.  
    170.             CGPROGRAM
    171.             #pragma target 3.0
    172.             #pragma exclude_renderers nomrt
    173.  
    174.  
    175.             // -------------------------------------
    176.  
    177.             #pragma shader_feature _NORMALMAP
    178.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    179.             #pragma shader_feature _EMISSION
    180.             #pragma shader_feature _METALLICGLOSSMAP
    181.             #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    182.             #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
    183.             #pragma shader_feature ___ _DETAIL_MULX2
    184.             #pragma shader_feature _PARALLAXMAP
    185.  
    186.             #pragma multi_compile_prepassfinal
    187.             #pragma multi_compile_instancing
    188.  
    189.             #pragma vertex vertDeferred
    190.             #pragma fragment fragDeferred
    191.  
    192.             #include "UnityStandardCore.cginc"
    193.  
    194.             ENDCG
    195.         }
    196.  
    197.         // ------------------------------------------------------------------
    198.         // Extracts information for lightmapping, GI (emission, albedo, ...)
    199.         // This pass it not used during regular rendering.
    200.         Pass
    201.         {
    202.             Name "META"
    203.             Tags { "LightMode"="Meta" }
    204.  
    205.             Cull Off
    206.  
    207.             CGPROGRAM
    208.             #pragma vertex vert_meta
    209.             #pragma fragment frag_meta
    210.  
    211.             #pragma shader_feature _EMISSION
    212.             #pragma shader_feature _METALLICGLOSSMAP
    213.             #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    214.             #pragma shader_feature ___ _DETAIL_MULX2
    215.             #pragma shader_feature EDITOR_VISUALIZATION
    216.  
    217.             #include "UnityStandardMeta.cginc"
    218.             ENDCG
    219.         }
    220.     }
    221.  
    222.     SubShader
    223.     {
    224.         Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }
    225.         LOD 150
    226.  
    227.         // ------------------------------------------------------------------
    228.         //  Base forward pass (directional light, emission, lightmaps, ...)
    229.         Pass
    230.         {
    231.             Name "FORWARD"
    232.             Tags { "LightMode" = "ForwardBase" }
    233.  
    234.             Blend [_SrcBlend] [_DstBlend]
    235.             ZWrite [_ZWrite]
    236.  
    237.             CGPROGRAM
    238.             #pragma target 2.0
    239.          
    240.             #pragma shader_feature _NORMALMAP
    241.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    242.             #pragma shader_feature _EMISSION
    243.             #pragma shader_feature _METALLICGLOSSMAP
    244.             #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    245.             #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
    246.             #pragma shader_feature _ _GLOSSYREFLECTIONS_OFF
    247.             // SM2.0: NOT SUPPORTED shader_feature ___ _DETAIL_MULX2
    248.             // SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP
    249.  
    250.             #pragma skip_variants SHADOWS_SOFT DIRLIGHTMAP_COMBINED
    251.  
    252.             #pragma multi_compile_fwdbase
    253.             #pragma multi_compile_fog
    254.  
    255.             #pragma vertex vertBase
    256.             #pragma fragment fragBase
    257.             #include "UnityStandardCoreForward.cginc"
    258.  
    259.             ENDCG
    260.         }
    261.         // ------------------------------------------------------------------
    262.         //  Additive forward pass (one light per pass)
    263.         Pass
    264.         {
    265.             Name "FORWARD_DELTA"
    266.             Tags { "LightMode" = "ForwardAdd" }
    267.             Blend [_SrcBlend] One
    268.             Fog { Color (0,0,0,0) } // in additive pass fog should be black
    269.             ZWrite Off
    270.             ZTest LEqual
    271.          
    272.             CGPROGRAM
    273.             #pragma target 2.0
    274.  
    275.             #pragma shader_feature _NORMALMAP
    276.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    277.             #pragma shader_feature _METALLICGLOSSMAP
    278.             #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    279.             #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
    280.             #pragma shader_feature ___ _DETAIL_MULX2
    281.             // SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP
    282.             #pragma skip_variants SHADOWS_SOFT
    283.          
    284.             #pragma multi_compile_fwdadd_fullshadows
    285.             #pragma multi_compile_fog
    286.          
    287.             #pragma vertex vertAdd
    288.             #pragma fragment fragAdd
    289.             #include "UnityStandardCoreForward.cginc"
    290.  
    291.             ENDCG
    292.         }
    293.         // ------------------------------------------------------------------
    294.         //  Shadow rendering pass
    295.         Pass {
    296.             Name "ShadowCaster"
    297.             Tags { "LightMode" = "ShadowCaster" }
    298.          
    299.             ZWrite On ZTest LEqual
    300.  
    301.             CGPROGRAM
    302.             #pragma target 2.0
    303.  
    304.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    305.             #pragma shader_feature _METALLICGLOSSMAP
    306.             #pragma skip_variants SHADOWS_SOFT
    307.             #pragma multi_compile_shadowcaster
    308.  
    309.             #pragma vertex vertShadowCaster
    310.             #pragma fragment fragShadowCaster
    311.  
    312.             #include "UnityStandardShadow.cginc"
    313.  
    314.             ENDCG
    315.         }
    316.  
    317.         // ------------------------------------------------------------------
    318.         // Extracts information for lightmapping, GI (emission, albedo, ...)
    319.         // This pass it not used during regular rendering.
    320.         Pass
    321.         {
    322.             Name "META"
    323.             Tags { "LightMode"="Meta" }
    324.  
    325.             Cull Off
    326.  
    327.             CGPROGRAM
    328.             #pragma vertex vert_meta
    329.             #pragma fragment frag_meta
    330.  
    331.             #pragma shader_feature _EMISSION
    332.             #pragma shader_feature _METALLICGLOSSMAP
    333.             #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    334.             #pragma shader_feature ___ _DETAIL_MULX2
    335.             #pragma shader_feature EDITOR_VISUALIZATION
    336.  
    337.             #include "UnityStandardMeta.cginc"
    338.             ENDCG
    339.         }
    340.     }
    341.  
    342.     FallBack "VertexLit"
    343.     CustomEditor "StandardShaderGUI"
    344. }
    345.  
    Thank you.
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You're right, I had the original post wrong. Try this.
    Code (csharp):
    1.     CGINCLUDE
    2.         #include "UnityCG.cginc"
    3.         #if !defined(UNITY_PASS_META) && (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))
    4.             #ifdef UNITY_CALC_FOG_FACTOR
    5.                 #undef UNITY_CALC_FOG_FACTOR
    6.             #endif
    7.             #define UNITY_CALC_FOG_FACTOR(coord) UNITY_CALC_FOG_FACTOR_RAW(length(i.eyeVec))
    8.         #endif
    9.     ENDCG
     
    st-VALVe likes this.
  10. rostik1975

    rostik1975

    Joined:
    May 14, 2015
    Posts:
    44
    Thank you very much. There are no errors now, but the effect is still the same - objects looks differently fogged depending on view direction.
     

    Attached Files:

    • 1.jpg
      1.jpg
      File size:
      72.5 KB
      Views:
      516
    • 2.jpg
      2.jpg
      File size:
      41.1 KB
      Views:
      530
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Tried it locally, and it works as expected.
    upload_2019-10-16_9-0-12.png

    Either you're not using your new shader, or you're using the deferred rendering path which does the fog with a post process.
     
  12. rostik1975

    rostik1975

    Joined:
    May 14, 2015
    Posts:
    44
    Yes, I'm using the deferred path with post-processing. So there is no chance to resolve this on shader level without switching to forward path?
    Thank you.
     
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    There is a way to solve it at the shader level. But you have to modify the right shader(s). When you're using deferred rendering you have to modify the post process shader that does the fog. Unfortunately the built in fog shader doesn't get all of the information needed to actually do this properly so it actually requires modifying some of the post processing scripts as well, or just writing your own fog post process. There used to be a couple of assets that did this already, but I think most of them have disappeared from the asset store as Unity kept changing the post processing stack.

    Some older free assets like KinoFog or SSMS should still work, though they don't nicely interface with the newer post processing stack stuff.
    https://github.com/keijiro/KinoFog
    https://github.com/OCASM/SSMS