Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Shader with Brightness Limit

Discussion in 'Shaders' started by Zullar, Jan 5, 2020.

  1. Zullar

    Zullar

    Joined:
    May 21, 2013
    Posts:
    651
    I am working on a top-down ARPG.


    I use lighting as part of the game to limit what you can see
    -Only in front of your character (or allied characters)
    -Near torches
    -Near graphic effects that emit light (i.e. a fireball)

    However I am running into a problem where light sometimes becomes too bright and white's out the graphics. This happens for 2 reasons.
    1: Light follows the intensity = 1/x^2 inverse square law where it is extremely bright near its source and then dims proportional to distance squared as you move away from your point source. (EDIT THIS IS INCORRECT)
    2: Multiple light sources overlapping

    #1 and #2 are how things behave in the real word and aren't bugs... but they don't make the game look like I want. So really what I'm looking for is non-realistic lighting. Specifically...

    A: The ability to create a custom light source. Like a point light but with intensity that fades out more gradually than a point light.
    B: or a custom shader that doesn't white out so much when things get really really bright.

    Before I get started digging into this can of worms does anybody know if A or B is possible (or has already been done). Or can anybody suggest a solution to start digging into? Thanks in advance.

    EDIT: It seems neither A or B is the proper way to solve this problem... but instead using Camera Post Processing
     
    Last edited: Jan 6, 2020
    gattusoarthur likes this.
  2. Zullar

    Zullar

    Joined:
    May 21, 2013
    Posts:
    651
    For option B (a custom shader that doesn't get too bright) I was wondering if you could make a shader that
    -At 20% brightness renders as 20% bright
    -At 50% brightness renders as 50% bright
    -At 100% brightness renders as 100% bright
    -At 200% brightness (when 2 light sources stack on top of each other) render as 120% bright instead of 200% bright
    -At 300% brightness (when 3 light sources stack on top of each other) render as 135% bright instead of 300% bright

    Or something along those lines.
     
    gattusoarthur likes this.
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Unless you’re using the LWRP/URP/HDRP, or are only using per vertex lights, this is not true. Unity’s built in pipelines use completely arbitrary falloff curves based purely on the light’s range. If you were using a proper inverse square this issue would likely be happening with just a single light, not require overlapping lights.

    If you’re using the deferred rendering path, you can write any kind of light you want. There used to be several assets on the store that added things like area light approximations or inverse square falloff, etc. to augment or replace the built in deferred lights. This is because with deferred rendering the lighting calculation shaders and the object shaders are totally separate things and can be easily mixed and matched without necessarily having to replace all of the in-scene shader (assuming all surfaces use the same shading model and your light types can use the existing gbuffer data as is).

    Where things get complicated is even with deferred rendering, if you have anything that needs to be rendered as forward (like anything transparent, or not strictly following the expected shading model) you need a fully custom shader to handle that. A forward rendered shader has the object’s surface properties and the lighting calculations bundled together. Those assets I mentioned earlier almost all had the one “minor” caveat that they didn’t work on anything transparent.

    So, if you want a custom lighting setup and want to be able to have transparent things affected by the light the same way, then you have to use a custom shader on everything.

    But there’s one more big wrench in this plan. The way Unity’s lighting system works, deferred or forward, has no knowledge of more than one light at a time. You can’t write a shader that you put on an object that’ll do the kind of brightness adjustments you want because Unity’s lighting system doesn’t ever let the shaders know if there’s more than one light affecting an object!


    Luckily, there is an easy solution. Render using HDR enabled on your camera and use a post process that scales the brightness over “1” down so it doesn’t blow out so quickly. You don’t even necessarily need to write one since using the post processing stack’s color grading effect should give you enough control to get something much closer to your goal.
     
    Zullar likes this.
  4. Zullar

    Zullar

    Joined:
    May 21, 2013
    Posts:
    651
    @bgolus
    Thank you so much. You've helped me a lot over the years I appreciate it!

    Will look into camera post process and see if I can get it to work.
     
  5. Zullar

    Zullar

    Joined:
    May 21, 2013
    Posts:
    651
    I put in a camera post process yesterday with a custom shader.

    Before: No Post Process
    upload_2020-1-7_11-26-44.png

    After (Method 1): Limiting total color magnitude. (if color.mag > X then color.mag = X)
    upload_2020-1-7_11-20-36.png

    After (Method 2): Color.mag = Color.mag ^ X * brightness. With exponent X < 1 dimmer colors become brighter and brighter colors become dimmer. This makes the center of the point light dimmer... and fringes of point light brighter. Also a single light source becomes brighter than before and two overlapping bright sources become dimmer than before. Unfortunately a lot of contrast is lost.
    upload_2020-1-7_11-24-55.png

    Method 1 and Method 2 seem OK but not great.

    Anyhow thought I'd share.
     
    MarkenhXu and CodeRonnie like this.
  6. Double_Boy

    Double_Boy

    Joined:
    Feb 27, 2019
    Posts:
    1
    Just curious what post processing effect did you use to achieve this? dealing with a similar issue
     
  7. Zullar

    Zullar

    Joined:
    May 21, 2013
    Posts:
    651
    It's been a while so I don't quite remember what I did. But I found my old script. I think I put it on a camera post-processor. It didn't work that great and never found a good solution for this issue.

    Code (csharp):
    1.  
    2. Shader "Hidden/Custom/BatLightClip"
    3. {
    4.     HLSLINCLUDE
    5.  
    6. #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl"
    7.  
    8.     TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex);
    9.     float _Brightness; //bat
    10.     float _Exponent; //bat
    11.     float _Blend;
    12.     float _SoftClip;
    13.  
    14.     float4 BatFrag(VaryingsDefault i) : SV_Target
    15.     {
    16.         float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord);
    17.  
    18.         if (_Exponent != 1)
    19.         {
    20.             float colorMag = sqrt(color.r*color.r + color.g*color.g + color.b*color.b);
    21.             float newColorMag = pow(colorMag, _Exponent);
    22.             float colorMult = newColorMag / colorMag;
    23.             color = float4(colorMult*color.r, colorMult*color.g, colorMult*color.b, 1); //color = GammaToLinearSpace(color); linear space to gammma space or something inbetween (0.4545 is linear --> gamma?)
    24.         }
    25.  
    26.         if (_Blend > 0) //can be set to 0 to ignore
    27.         {
    28.             //float luminance = dot(color.rgb, float3(0.2126729, 0.7151522, 0.0721750));
    29.             float colorMag = sqrt(color.r*color.r + color.g*color.g + color.b*color.b);
    30.             if (colorMag > _Blend)
    31.             {
    32.                 float dividerRatio = colorMag / _Blend;
    33.                 color = float4(color.r / dividerRatio, color.g / dividerRatio, color.b / dividerRatio, 1);// color / dividerRatio;
    34.             }
    35.         }
    36.      
    37.         if (_Brightness != 1)
    38.         {
    39.             color = float4(_Brightness*color.r, _Brightness*color.g, _Brightness*color.b, 1); //bat
    40.         }
    41.  
    42.         if (_SoftClip > 0 )
    43.         {
    44.             float rgbMax = max(color.r, max(color.g, color.b));
    45.             float rgbMaxNew = rgbMax / (rgbMax + 1);
    46.             float colorMult = rgbMaxNew / rgbMax;
    47.             color = float4(color.r * colorMult, color.g * colorMult, color.b * colorMult, 1);
    48.         }
    49.  
    50.      
    51.         return color;
    52.     }
    53.  
    54.     ENDHLSL
    55.  
    56.     SubShader
    57.     {
    58.         Cull Off
    59.         ZWrite Off
    60.         ZTest Always
    61.  
    62.         Pass
    63.         {
    64.             HLSLPROGRAM
    65. #pragma vertex VertDefault
    66. #pragma fragment BatFrag
    67.             ENDHLSL
    68.         }
    69.     }
    70. }
    71.  
    and

    Code (csharp):
    1.  
    2. [Serializable]
    3. [PostProcess(typeof(BatLightClipRenderer), PostProcessEvent.AfterStack, "BatCustom/BatLightClipDropdownDescription")]
    4. public sealed class BatLightClipSettings : PostProcessEffectSettings
    5. {
    6.     [Range(0f, 5f), Tooltip("Brightness")] //bat
    7.     public FloatParameter brightness = new FloatParameter { value = 1f }; //bat
    8.  
    9.     [Range(0f, 3f), Tooltip("Exponent")] //bat
    10.     public FloatParameter exponent = new FloatParameter { value = 1f }; //bat
    11.  
    12.     [Range(0f, 5f), Tooltip("Light Clip Intensity")]
    13.     public FloatParameter blend = new FloatParameter { value = 0f };
    14.  
    15.     [Range(0f, 2f), Tooltip("Soft Clip")]
    16.     public FloatParameter softClip = new FloatParameter { value = 0f }; //New 2021_03_01
    17. }
    18.  
    19. public sealed class BatLightClipRenderer : PostProcessEffectRenderer<BatLightClipSettings>
    20. {
    21.     public override void Render(PostProcessRenderContext postProcessRenderContext)
    22.     {
    23.         PropertySheet propertySheet = postProcessRenderContext.propertySheets.Get(Shader.Find("Hidden/Custom/BatLightClip"));
    24.         propertySheet.properties.SetFloat("_Brightness", settings.brightness);
    25.         propertySheet.properties.SetFloat("_Exponent", settings.exponent);
    26.         propertySheet.properties.SetFloat("_Blend", settings.blend);
    27.         propertySheet.properties.SetFloat("_SoftClip", settings.softClip);
    28.         postProcessRenderContext.command.BlitFullscreenTriangle(postProcessRenderContext.source, postProcessRenderContext.destination, propertySheet, 0);
    29.     }
    30. }
    31.  
     
    KarlKarl2000 likes this.