[SOLVED] Procedurally modify color brightness in shader


Using a noise texture I want to adjust the brightness of the terrain texture in the shader to make an artistic effect. So for example, instead of grass and dirt having the same color saturation and brightness all over the place I want to smoothly modify the color to have darker/brighter grass.

I am doing this by converting RGB color to HSV in the fragment shader and adjusting the brightness and then converting it back to the RGB model. (get the rgb2hsv code from StackOverflow)

vec3 rgb2hsv(vec3 c) {
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);

vec3 hsv2rgb(vec3 c) {
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);

and using it like this

vec4 colorNoise = texture(m_NoiseMap, texCoord * 6.0);
vec3 hsb = rgb2hsv(color.rgb);
float b = 1.0 + (colorNoise.x * 0.5);
hsb.z *= b;
color.rgb = hsv2rgb(hsb);

I am curious to know if it is possible to do this without converting to HSV color possibly using some other trick?


Or is it possible to cache the result and reuse it later inside the shader? because it is purely based on diffuse color and noise texture and does not depend on lighting or so…

And yes, I am a shader noob!

Without knowing more about the effect you are trying to achieve, changing the brightness of a color can often by done just by multiplying the color vector:

color.rgb *= 0.5;

…will halve the ‘brightness’ for example. So you might just multiply by your noise value.

Later where?

1 Like


Multiplying did not get the result I was looking for but powering with noise gets very close to the effect I wanted. :slightly_smiling_face:

          float s = smoothstep(0.0, 1.0, noise.x) / 4.0;
          color.rgb = pow(color.rgb, vec3(0.7 + s));


In the same shader. Something like a map in java. So I calculate it once for the same x, and y texture coordinates at runtime and cache it! but I guess it is very unlike how shaders work

That is very unlike how shaders work. Plus, it’s super likely that x,y this frame is nothing like the same x,y next frame.

1 Like

My old-school brain avoids pow but it’s probably fine, I guess.

What did the old code + results look like? Was it just like color.rgb *= noise.x?

          float s = smoothstep(0.0, 1.0, colorNoise.x);
          color.rgb *= vec3(0.7 + s);

see the dark areas looks like there is a shadow on them.


another screenshot

using power

using multiply:

Just a note, first you don’t need the vec3() at all.

Second, 0.7 + s is going to go greater than 1.0 unless you scale s.

color.rgb *= 0.7 + s * 0.3

I think the pow() version won’t do that unless rgb was already > 1.0 somewhere. Haven’t really thought about it… but it could be that the straight multiply version is actually making the whole image overall brighter. (Edit: Looking at your images again, it’s definitely true, second image is much brighter overall.)

…else I’d not expect it to ever by any darker than 0.7 would give you. And that seems pretty subtle normally.

1 Like