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 change HDR color's intensity via shader

Discussion in 'Shaders' started by Dee_Lucky, May 17, 2018.

  1. Dee_Lucky

    Dee_Lucky

    Joined:
    Jun 26, 2015
    Posts:
    53
    Hi.

    I'm wondering how to change the HDR color's intensity via a shader. While I'm looking on the standard shader, and the emission HDR color it has the intensity and it's never referenced in shader's code.
    Is there a way to change it something like:
    emissiveColor.intensity =12;

    All the help will be appreciated.
    Thanks!
     
  2. Dee_Lucky

    Dee_Lucky

    Joined:
    Jun 26, 2015
    Posts:
    53
  3. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    823
  4. Dee_Lucky

    Dee_Lucky

    Joined:
    Jun 26, 2015
    Posts:
    53
    I'm talking about directly changing intensity in a shader, not with c#.
     
  5. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    823
    Still not quite sure what you want to do in the shader, but if you take a look at UnityStadardInput.cginc you will find
    Code (CSharp):
    1. half3 Emission(float2 uv)
    2. {
    3. #ifndef _EMISSION
    4.     return 0;
    5. #else
    6.     return tex2D(_EmissionMap, uv).rgb * _EmissionColor.rgb;
    7. #endif
    8. }
    which in turn is used in UnityStandardCore.cginc.

    You could change it to
    Code (CSharp):
    1. half3 Emission(float2 uv)
    2. {
    3. #ifndef _EMISSION
    4.     return 0;
    5. #else
    6.     return tex2D(_EmissionMap, uv).rgb * _EmissionColor.rgb * 12.0;
    7. #endif
    8. }
    if that is, what you're looking for.
     
  6. Dee_Lucky

    Dee_Lucky

    Joined:
    Jun 26, 2015
    Posts:
    53
    Hey,
    Well, I've tried it your way but it kind of does not work. Let me put the whole problem I've got here, so you can understand what I'm trying to achieve.
    So in my game, I want places covered with fog of war(seen once, but not seeing right now) to be uncolored (desaturated) and intensity of emission HDR color to be lowered so it appears to be low on power.

    Right now it looks like this:
    https://gyazo.com/9be1ef4fbfd5bc395ff42699aef89385

    As you can see, the colors are desaturated. I'm doing that by conversing their colors into HSV and setting the saturation value to near to zero values. But the second part of the problem - intensity does not get lower.
    Here you can see that it can be changed through inspector. This is what I want to achieve, but with changing it inside the shader.

    https://gyazo.com/1940f36986badf7d161134b830f9a6b5
     
    Last edited: May 18, 2018
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    HSV, ie: Hue, Saturation, Value, is also known as HSB, or Hue, Saturation, Brightness. The brightness or value is in effect the absolute intensity, lower that at the same time you're reducing the saturation and you'll make it darker. Alternatively, like in @Johannski 's example, just multiply that emission color by some value to increase or decrease the intensity.

    As for what the intensity value is in the HDR color picker actually is, that's a bit more complicated. Internally it's treated as an exposure value which they calculate as:

    Color * 2f ^ Exposure

    So if you have a color value of [1.0, 1.0, 1.0], then an intensity of -1 produces [0.5, 0.5, 0.5]. -2 produces [0.25, 0.25, 0.25], and on the flip an intensity of 1 is the equivalent of [2.0, 2.0, 2.0]. One thing to be mindful of though is that all of these values are being calculated in sRGB color space, referred to by Unity as "gamma" space. If you're using linear color space for your project, the value you get in the shader has already been transformed. If you want to perfectly match the effect of an intensity of "-2" in the color picker, you need to convert your color values to sRGB space, multiply by 2^-2 (or 0.25), and convert back to linear space. Luckily there are handy functions included with Unity for this:


    // if not using gamma color space, convert from linear to gamma
    #ifndef UNITY_COLORSPACE_GAMMA
    emissiveColor.rgb = LinearToGammaSpace(emissiveColor.rgb);
    #endif
    // apply intensity exposure
    emissiveColor.rgb *= pow(2.0, _foggedIntensity);
    // if not using gamma color space, convert back to linear
    #ifndef UNITY_COLORSPACE_GAMMA
    emissiveColor.rgb = GammaToLinearSpace(emissiveColor.rgb);
    #endif


    Or, you could simplify this with a cheap approximation:

    half intensityMul = pow(2.0, _foggedIntensity);
    #ifndef UNITY_COLORSPACE_GAMMA
    intensityMul = pow(intensityMul, 2.2);
    #endif
    emissiveColor.rgb *= intensityMul;


    This isn't perfect, especially for highly saturated non-primary colors, but since you're already desaturating the colors to nearly white it's kind of moot, as long as you do this after you apply the desaturation. You could also apply the intensity multiplier to the V before converting from HSV to RGB.

    If you want to know more about what the HDR color picker is doing, the source code for it is here:
    https://github.com/Unity-Technologi...6c1ddcc9f1705/Editor/Mono/GUI/ColorMutator.cs
     
    Last edited: May 18, 2018
  8. Dee_Lucky

    Dee_Lucky

    Joined:
    Jun 26, 2015
    Posts:
    53
    XYZhang_xn likes this.
  9. NeatWolf

    NeatWolf

    Joined:
    Sep 27, 2013
    Posts:
    924
    Hi there (again, for some ;) )!

    I'm almost on the same problem, but using Fragment/Vertex shaders.
    Using it with particles, I've seen that the gamma/linear color "fix" is now (Unity 2018.2) natively present in the Rendering submodule of each particle system. So, one of the issues should be already taken into account.

    Yet, in linear color space, simply by multiplying the fragment output for some linear "exposure" value (that is: return color * exposure) is not writing over-one color intensities appropriately, bloom doesn't read it, and causes visual artifacts when trails of such shader do overlap in additive mode.

    I also changed all the variable types to floats with no success.
    I'm also surprised we're not getting native HDR variant shaders for particles in Unity.

    I couldn't imagine that adding HDR support (via a Multiplier/Exposure value) to a basic vertex/frag builtin Unity shader would have been more complicated than just multiplicating the final pixel color (should I also multiply alpha?).

    Am I doing something wrong? I thought it was more straightforward (should I open another topic?)

    Best
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    For the topic of this thread, this doesn't actually help. Before (or with the check box for this disabled) the vertex color values are always in gamma space, which removes the step of needing to convert from linear to gamma to apply the exposure. If the intent is to perfectly mimic what the exposure setting does, it's best to leave the correction disabled.

    I use HDR values as part of the output from particle shaders quite regularly, and have done so for several years. If this isn't working for you then either there's a problem with your shader or your expectations. For example, multiplying the color by 2 in linear space isn't really that much brighter, try multiplying by 20 and see if you're seeing what you expect, or do the proper gamma space exposure as described above.

    Irrelevant for PC. Using fixed, or half doesn't do anything on PC as most desktop GPUs don't support anything but 32 bit floats in the shaders. This isn't wrong as fixed and half are defined as floating point types that have a minimum accuracy and range, and a full 32 bit float accomplishes that. The one caveat the mesh vertex colors, which non-instanced particle systems use to pass the particle color, is limited to a 8 bpc UNORM, ie: 0 to 255 color values normalized to 0.0 to 1.0 floats when read by the shader.

    The Particle Standard shaders have an HDR color tint. That's as good as it gets at the moment as far as official shaders are concerned. To be fair, the current particle system is going to be completely replaced in the near-ish future as they have plans for a node based particle system which the HD pipeline will support. All existing particle shaders and the current Shuriken particle system will likely be deprecated at some point in the future, and there seems to be a push inside Unity to stop putting resources towards upgrading the current system too much more.

    Really, yes, this should be a new topic as while it is related, particles are a somewhat unique case.
     
  11. Hukha

    Hukha

    Joined:
    Aug 12, 2013
    Posts:
    61
  12. fwalker

    fwalker

    Joined:
    Feb 5, 2013
    Posts:
    255
    I am missing something here: What happens if you want to change the intensity for a color that has already been defined in the material.
    I want to animate the Enimision color on the timeline. Problem is that Emission color already contains the intensity level. So how can I find out what the color is prior to the intensity being applied to it so that I can apply the new intensity?
     
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    If you're using a built in shader, it's not possible. As you said, the Emission color actually in the material's properties has the intensity (aka exposure) already baked in. The color picker itself separates the color value and intensity for user sanity, but you'll notice if you set an HDR color value with the picker, close the picker, and open it again the intensity and exact color values will be different. The color being shown on the object won't change, but the picked intensity won't be used.

    A really good example of this would be to pick a really dark grey color in the RGB / HSV settings, then bump up the intensity until it's nearly white.
    upload_2020-2-10_10-48-32.png

    When you close and reopen the color picker it'll show you you something closer to white with the intensity set back to 0.
    upload_2020-2-10_10-48-52.png

    Basically if the stored color value is especially darker than "1.0" it'll assume the color value is just that without any intensity. The same thing happens with values over 1, but it roughly aims for a HSV with a V of ~75 when opening up.
    upload_2020-2-10_10-52-11.png
    This is all in the script I linked to a few posts above if you want to know more about what the code is actually doing.


    The only way to replicate that on built-in shaders is to animate the intensity via a script rather then directly with an animation (though you could animate the intensity value the script is using).


    The other option, and the one I use, is I write custom shaders which actually have the emission color and intensity separated so the intensity can be directly driven by an animator without affecting the color. I then multiply the two values together in the shader. I also don't try to reproduce the intensity setting as it exists in the picker since, as also explained above, that requires converting the color value into sRGB space, applying the intensity as a power of 2 multiplier, and converting back to linear. I just multiply the color by a big number.
     
  14. LonLon1

    LonLon1

    Joined:
    Oct 7, 2020
    Posts:
    1
    Holy Jeff, I don't understand a single Word :D!

    Nevermind, just in case someone reads my answer in bad English(And knows nothing, like me and John):

    Just make a float Property in Shadergraph and multiply it with the color property before multiplying the color with the emission. Then access the float over mat.SetFloat(_multiplyFloat) and be happy forever :)
     
    Last edited: Dec 14, 2021
    961020217 and GuirieSanchez like this.
  15. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    406

    Live savior! I'll come back here when I get more points and rate this with a thumbs up! Thanks :)
     
  16. FungusMonkey

    FungusMonkey

    Joined:
    Jun 30, 2016
    Posts:
    41
    Yep.. Perfect and so easy. Just instead of hard coding value use a variable. I set mine with a slider.