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

Question Smooth out Color Gradient of Firewatch Fog Type Shader?

Discussion in 'Shaders' started by ChiliStudio, Sep 16, 2020.

  1. ChiliStudio

    ChiliStudio

    Joined:
    Jan 19, 2018
    Posts:
    33
    I'm making a custom post-processing shader in HDRP that uses a color ramp to determine the color of the pixel(fog), based on camera depth. It can have pretty cool looking results:

    I'm looking to smooth it out though, as gradients can only support a number of colors.

    There is a terrible banding occurring in parts of different gradients:
    Adding noise to the gradient ramp (png) isn't really an option.
    I've tried dithering, but it doesn't fix the problem well and looks ugly:

    This is the first shader I have made, so if anyone could offer help, that would be awesome. Thank you!

    Here is my code:
    Code (CSharp):
    1. Shader "FogLayers"
    2.  
    3. {
    4.  
    5.     HLSLINCLUDE
    6.  
    7.  
    8.     #pragma target 4.5
    9.  
    10.     #pragma only_renderers d3d11 ps4 xboxone vulkan metal switch
    11.  
    12.     #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
    13.  
    14.     #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
    15.  
    16.     #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
    17.  
    18.     #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/FXAA.hlsl"
    19.  
    20.     #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/RTUpscale.hlsl"
    21.  
    22.     struct Attributes
    23.  
    24.     {
    25.  
    26.         uint vertexID : SV_VertexID;
    27.  
    28.         UNITY_VERTEX_INPUT_INSTANCE_ID
    29.  
    30.     };
    31.  
    32.     struct Varyings
    33.  
    34.     {
    35.  
    36.         float4 positionCS : SV_POSITION;
    37.  
    38.         float2 texcoord   : TEXCOORD0;
    39.  
    40.         UNITY_VERTEX_OUTPUT_STEREO
    41.  
    42.     };
    43.  
    44.     Varyings Vert(Attributes input)
    45.  
    46.     {
    47.  
    48.         Varyings output;
    49.  
    50.         UNITY_SETUP_INSTANCE_ID(input);
    51.  
    52.         UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
    53.  
    54.         output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
    55.  
    56.         output.texcoord = GetFullScreenTriangleTexCoord(input.vertexID);
    57.  
    58.         return output;
    59.  
    60.     }
    61.  
    62.  
    63.     // List of properties to control your post process effect
    64.  
    65.     float _Intensity;
    66.     float _Distance;
    67.     float _Accumulation;
    68.     float _Falloff;
    69.     float _FalloffDistance;
    70.     float _MaxFalloffDistance;
    71.     float _MaxDistance;
    72.     float _Blend;
    73.     float _Fade;
    74.     float _DetailDistance;
    75.     float _DitherIntensity;
    76.  
    77.  
    78.  
    79.     TEXTURE2D_X(_InputTexture);
    80.     TEXTURE2D(_Ramp1);
    81.     TEXTURE2D(_Ramp2);
    82.     TEXTURE2D(_DitherTexture);
    83.  
    84.  
    85.     float4 CustomPostProcess(Varyings input) : SV_Target
    86.  
    87.     {
    88.      
    89.  
    90.  
    91.         UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
    92.  
    93.      
    94.  
    95.         uint2 positionSS = input.texcoord * _ScreenSize.xy;
    96.  
    97.         float depth = LoadCameraDepth(positionSS);
    98.         float linearEyeDepth = LinearEyeDepth(depth, _ZBufferParams);
    99.  
    100.         float3 outColor = LOAD_TEXTURE2D_X(_InputTexture, positionSS).xyz;
    101.  
    102.  
    103.         float2 uvgray = float2(clamp(linearEyeDepth * _FalloffDistance, 0, 1000), 0);
    104.  
    105.  
    106.         float2 ditherCoordinate = input.positionCS % float2(2048, 2048);
    107.         float ditherValue = LOAD_TEXTURE2D_X(_DitherTexture, ditherCoordinate).r;
    108.  
    109.         float3 grayScaleCompensation = lerp(LOAD_TEXTURE2D_X(_Ramp1, uvgray).xyz, LOAD_TEXTURE2D_X(_Ramp2, uvgray).xyz, _Blend) * (ditherValue * (_DitherIntensity) + 1);
    110.  
    111.         float4 fog = float4(0,0,0,0);
    112.         fog.xyz = float4(outColor, 1);
    113.  
    114.         if(linearEyeDepth < _MaxFalloffDistance){
    115.  
    116.             fog.xyz *= clamp(depth *  _DetailDistance, 0, 1);
    117.             fog.xyz += grayScaleCompensation * _Falloff * clamp(linearEyeDepth / _Fade, 0, _Accumulation);
    118.         }
    119.  
    120.      
    121.      
    122.  
    123.         if(linearEyeDepth > _MaxDistance)
    124.             return outColor;
    125.         else
    126.             return fog;
    127.  
    128.  
    129.     }
    130.  
    131.     ENDHLSL
    132.  
    133.     SubShader
    134.  
    135.     {
    136.  
    137.         Pass
    138.  
    139.         {
    140.  
    141.             Name "FogLayers"
    142.  
    143.             ZWrite Off
    144.  
    145.             ZTest Always
    146.  
    147.             Blend Off
    148.  
    149.             Cull Off
    150.  
    151.             HLSLPROGRAM
    152.  
    153.                 #pragma fragment CustomPostProcess
    154.  
    155.                 #pragma vertex Vert
    156.  
    157.             ENDHLSL
    158.  
    159.         }
    160.  
    161.     }
    162.  
    163.     Fallback Off
    164.  
    165. }
    166.  
     
    Last edited: Sep 16, 2020
  2. NorthStar79

    NorthStar79

    Joined:
    May 8, 2015
    Posts:
    88
    Just make sure that color space used by engine is selected as "Linear" instead of "Gama"
    You can check this in "player settings> Color Space"
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,238
    There's a lot of different places where the banding can come from. Could be the ramp texture you're using is compressed, or otherwise too low precision. An uncompressed 32 bit (8 bit per channel) RGBA texture can still band, especially with subtle color gradients due to the fact there's not enough precision in the texture to prevent multiple successive texels from having the same color value. Sometimes that can be worked around by using a smaller ramp texture, as the GPU will always decode textures to 32 bit floats per channel. However you may also want to author your ramps as 16 bit per channel RGBAHalf textures.

    Then there's issues of post processing. If you're doing the fog before color correction, which you should be, the post processing can accentuate the banding that would have otherwise not been visible. Again, this comes down to the precision of the ramp being used. Without color correction, it might look totally fine, but with color correction the lack of precision can be accentuated.

    Also, it should be noted that dithering is useful, but only if you have a higher precision value you're dithering from. Otherwise you're just going to have dithered banding. You also have to be super careful when doing dithering to do so as close to the final displayed color space as possible. Really, the HDRP's own post processing should be doing its own dithering when converting from the internal HDR to the LDR display image. There shouldn't be a need for you to do it as long as your fog ramp is high enough precision.
     
    spakment and ChiliStudio like this.
  4. ChiliStudio

    ChiliStudio

    Joined:
    Jan 19, 2018
    Posts:
    33
    Last edited: Sep 17, 2020