Hey guys,

I’m just wondering if theres a built in function or some logic that can adjust the contrast within a mapped texture. Basically so that when I adjust the value the brights get brighter and the darks get darker?

For example I have Noise texture from grey to white and I want to increase the contrast. I can’t just add brightness because it increases the dark parts too? If that makes sense

Anyway help would be great!

1 Like

Depends on what you mean by “contrast”.

The easiest way to do it would be:

``````half3 AdjustContrast(half3 color, half contrast) {
return saturate(lerp(half3(0.5, 0.5, 0.5), color, contrast));
}
``````

That’ll work for gamma space rendering. Passing in a contrast value of 0.0 will give you middle grey. A contrast of 100.0 will make almost everything black or white (or solid colors), but it’ll crush the lights and darks entirely even at lower contrast values. The above is fairly straight forward to understand if you’re familiar with linear interpolation or even alpha blending, at least for contrast values between 0.0 and 1.0. This just blends between grey and the color, but linear interpolation has the handy behavior of working just fine with values >1.0, or even negatives if you want to invert the colors. For example a contrast of 2 will make color values of 63/255 black, and 192/255 white.

Another slightly more complex method is:

``````half3 AdjustContrastCurve(half3 color, half contrast) {
return pow(abs(color * 2 - 1), 1 / max(contrast, 0.0001)) * sign(color - 0.5) + 0.5;
}
``````

Unlike the above method the lights and darks won’t be immediately crushed, however very low contrasts will look kind of funny as the fully black and white areas will remain fully black and white. This has a bit more going on, but the essence is we’re applying a power curve to the color, but applying the power curve pivoted around “0.5”. There are a few curious things going on if you look closely, like using an abs() to the color after remapping it from 0.0~1.0 to -1.0~1.0, but that’s because the power might remove the sign sometimes (like pow(x, 2)) but not all the time, so we remove it before the power, then reapply it afterwards with the sign() function. The max() is there to prevent a divide by zero.

Both of these only really work “correctly” as is if you’re using the gamma color space, but with out tweaks it may still give you acceptable results depending on what you like the look of. If you need this to work properly in linear space as well you’ll need to convert the color back and forth between the linear and gamma space representations.

Here’s the first method updated account for the color space

``````half3 AdjustContrast(half3 color, half contrast) {
#if !UNITY_COLORSPACE_GAMMA
color = LinearToGammaSpace(color);
#endif
color = saturate(lerp(half3(0.5, 0.5, 0.5), color, contrast));
#if !UNITY_COLORSPACE_GAMMA
color = GammaToLinearSpace(color);
#endif
return color;
}
``````

I’ll leave updating the other method up to you.

21 Likes

Awesome that explains it really well. Thanks for the help!

I know this is an old post but it helped me out so I wanted to say thanks again bgolus for the in-depth explanation, you’re a life saver.

1 Like