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

How to sample HDR texture in shader?

Discussion in 'Shaders' started by briank, Feb 1, 2019.

  1. briank

    briank

    Joined:
    Mar 25, 2017
    Posts:
    74
    I've written a custom skybox shader and the texture I'm using is an HDR formatted texture. Sampling the texture directly via tex2d clearly leads to wrong colors (super bright or saturated) versus whatever shader Unity applies to the standard skybox shaders...

    How do I properly sample from an HDR texture?

    (I saw other threads allude to using '8.0 * color.a * color.rgb', but that doesn't look right either.)
     
  2. briank

    briank

    Joined:
    Mar 25, 2017
    Posts:
    74
    I tracked down the default skybox shader code. What I needed to do is call DecodeHDR and use some global parameters.

    Code (CSharp):
    1.  
    2.             half4 tex = tex2D (_MainTex, tc);
    3.             half3 c = DecodeHDR (tex, _MainTex_HDR);
    4.             c = c * _Tint.rgb * unity_ColorSpaceDouble.rgb;
     
    LaireonGames likes this.
  3. Julien-Lynge

    Julien-Lynge

    Joined:
    Nov 5, 2010
    Posts:
    142
    @briank did a great job tracking down code, but since this is an incomplete code snippet, I figured I'd fill in the blanks. Here's a fragment shader that decodes HDR (from the built-in Skybox.shader):

    Code (CSharp):
    1.     half4 skybox_frag (v2f i, sampler2D smp, half4 smpDecode)
    2.     {
    3.         half4 tex = tex2D (smp, i.texcoord);
    4.         half3 c = DecodeHDR (tex, smpDecode);
    5.         c = c * _Tint.rgb * unity_ColorSpaceDouble.rgb;
    6.         c *= _Exposure;
    7.         return half4(c, 1);
    8.     }
    and this is called with:

    Code (CSharp):
    1. half4 frag (v2f i) : SV_Target
    2. {
    3. return skybox_frag(i,_FrontTex, _FrontTex_HDR);
    4. }
    A little explanation:
    • _Tint is just a property defined by the skybox shader
    • If you just add _FrontTex_HDR to your shader, Unity will fill it in with the HDR data:
    Code (CSharp):
    1. sampler2D _FrontTex;
    2. half4 _FrontTex_HDR;
    • DecodeHDR and unity_ColorSpaceDouble are defined in UnityCG.cginc (you can download all the built-in shader code from Unity's download page). Here they are:
    Code (CSharp):
    1. #ifdef UNITY_COLORSPACE_GAMMA
    2. #define unity_ColorSpaceGrey fixed4(0.5, 0.5, 0.5, 0.5)
    3. #define unity_ColorSpaceDouble fixed4(2.0, 2.0, 2.0, 2.0)
    4. #define unity_ColorSpaceDielectricSpec half4(0.220916301, 0.220916301, 0.220916301, 1.0 - 0.220916301)
    5. #define unity_ColorSpaceLuminance half4(0.22, 0.707, 0.071, 0.0) // Legacy: alpha is set to 0.0 to specify gamma mode
    6. #else // Linear values
    7. #define unity_ColorSpaceGrey fixed4(0.214041144, 0.214041144, 0.214041144, 0.5)
    8. #define unity_ColorSpaceDouble fixed4(4.59479380, 4.59479380, 4.59479380, 2.0)
    9. #define unity_ColorSpaceDielectricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04) // standard dielectric reflectivity coef at incident angle (= 4%)
    10. #define unity_ColorSpaceLuminance half4(0.0396819152, 0.458021790, 0.00609653955, 1.0) // Legacy: alpha is set to 1.0 to specify linear mode
    11. #endif
    Code (CSharp):
    1. // Decodes HDR textures
    2. // handles dLDR, RGBM formats
    3. inline half3 DecodeHDR (half4 data, half4 decodeInstructions)
    4. {
    5.     // Take into account texture alpha if decodeInstructions.w is true(the alpha value affects the RGB channels)
    6.     half alpha = decodeInstructions.w * (data.a - 1.0) + 1.0;
    7.  
    8.     // If Linear mode is not supported we can skip exponent part
    9.     #if defined(UNITY_COLORSPACE_GAMMA)
    10.         return (decodeInstructions.x * alpha) * data.rgb;
    11.     #else
    12.     #   if defined(UNITY_USE_NATIVE_HDR)
    13.             return decodeInstructions.x * data.rgb; // Multiplier for future HDRI relative to absolute conversion.
    14.     #   else
    15.             return (decodeInstructions.x * pow(alpha, decodeInstructions.y)) * data.rgb;
    16.     #   endif
    17.     #endif
    18. }
     
    FunSTW, just_gos, m4d and 2 others like this.
  4. bunp

    bunp

    Joined:
    Feb 21, 2019
    Posts:
    70
    I am having this problem now except I am using Shadergraph. How do I sample an HDR texture and return an HDR value? Because the normal sample node is clamping the values to a maximum of 1.0
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    If you're not getting an HDR value, the texture you're sampling isn't an HDR texture.
     
  6. bunp

    bunp

    Joined:
    Feb 21, 2019
    Posts:
    70
    How can I make it an HDR texture?
    • URP 2022.2.17f1
    • I have a cube with a high intensity HDR color value material on it. This value is HDR.
    • I have a camera pointing at it that outputs to a render texture. The color format is RGB32SFLOAT so it supports the HDR range of values
    • I have a plane that displays the render texture.
    • I have a global post processing volume with bloom.
    The problem is somewhere along that line the color values get clamped to 0-1, the plane doesn't bloom glow and all that extra data is stripped out. Is there some way I can force or bypass the problem to be sure 100% the HDR values are getting encoded?

    Pictured:
    upload_2023-6-16_8-24-3.png
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    I would use the Frame Debugger to step through everything and look at what's happening. That should help you find where the problem is.

    Note: there is no "encoding" for HDR render textures. It's only for baked HDR like those from lightmaps, non-realtime cubemaps, or imported HDR images, that are encoded in any way. For a render texture, the value sampled from the texture should be HDR off the bat.