Skip to content

Instantly share code, notes, and snippets.

@Volcanoscar
Forked from wiseoldduck/greyscale.frag
Created January 19, 2017 08:28
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save Volcanoscar/4a9500d240497d3c0228f663593d167a to your computer and use it in GitHub Desktop.
Save Volcanoscar/4a9500d240497d3c0228f663593d167a to your computer and use it in GitHub Desktop.
A simple glsl color -> greyscale shader, using luminosity method
// fragment shader
//
// RGBA color to RGBA greyscale
//
// smooth transition based on u_colorFactor: 0.0 = original, 1.0 = greyscale
//
// http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/
// "The luminosity method is a more sophisticated version of the average method.
// It also averages the values, but it forms a weighted average to account for human perception.
// We’re more sensitive to green than other colors, so green is weighted most heavily. The formula
// for luminosity is 0.21 R + 0.72 G + 0.07 B."
varying vec2 v_texCoord;
uniform sampler2D CC_Texture0;
uniform float u_colorFactor;
void main()
{
vec4 sample = texture2D(CC_Texture0, v_texCoord);
float grey = 0.21 * sample.r + 0.71 * sample.g + 0.07 * sample.b;
gl_FragColor = vec4(sample.r * u_colorFactor + grey * (1.0 - u_colorFactor), sample.g * u_colorFactor + grey * (1.0 - u_colorFactor), sample.b * u_colorFactor + grey * (1.0 - u_colorFactor), 1.0);
}
@av01d
Copy link

av01d commented Nov 16, 2021

All of these don't handle opacity correctly. Mine does, and it is WebGL 1.0, suitable for all browsers:

varying vec2 texCoord;
uniform sampler2D CC_Texture0;
uniform float u_colorFactor;

void main() {
   vec4 sample = texture2D(CC_Texture0, texCoord);
   float gray = 0.21 * sample.r + 0.71 * sample.g + 0.07 * sample.b;
   gl_FragColor = vec4(sample.rgb * (1.0 - u_colorFactor) + (gray * u_colorFactor), sample.a);
}

u_colorFactor is a float between 0 and 1.

@geextahslex
Copy link

geextahslex commented Feb 29, 2024

Hi, I'm looking for help with a glsl shader. I want to combine float a and float b to get I2 but I have no idea how to implement it.
I want the low information (like srgb 0-180) from float a and the high information (like srgb 180-255) from float b to get I2. So like legs from a and torso from b I'm a noob in glsl but I will try out any suggestions.

Any help would be appreciated :)

This is the shader

vec3 tone_mapping_ictcp(vec3 ICtCp) {
    float a = Y_to_ST2084(curve(ST2084_to_Y(ICtCp.x) / L_sdrr, 250 / L_sdrr) * L_sdrr);
    float b = Y_to_ST2084(curve(ST2084_to_Y(ICtCp.x) / L_sdrr, 500 / L_sdrr) * L_sdrr);
?? float mask = smoothstep(0.75, 1.0, clamp(length(b), 0.0, 1.0));
?? float I2 = mix(a, b, mask);
    ICtCp.yz *= mix(1.0, min(ICtCp.x / I2, I2 / ICtCp.x), sigma);
    ICtCp.x   = I2;
    return ICtCp;
}

SCREENSHOTS

For demonstration purposes I color coded the picture. The green should come from float a (dark/low luminance) and the yellow should come from float b (bright/high luminance)

color coded
500 3 44
normal
500 3

@ccritchfield
Copy link

ccritchfield commented Mar 14, 2024

Hi, I'm looking for help with a glsl shader. I want to only output a specific srgb range/threshold with smoothstep. But I have no idea how to implement it. I need only the peak information so like smoothstep 230/245-255 srgb.

Any help would be appreciated :)

I don't know what part of your shader you're trying to use the smoothstep on, but smoothstep is used just like mix (glsl) or lerp (hlsl) would be used. It's just smoothstep provides a smooth curve instead of a linear return.

if you google "glsl smoothstep vs mix" you'll see various articles explaining this. Clicking "images" will show some simple graphics that show how mix is just a 0 to 1 45 degree line while smoothstep slowly ramps up smoothly then slowly tapers off smoothly.

Both are used / called the same way.

Let's use OP's desaturation / grayscale code as example.

We have a vec3 RGB
We created a float grayscale
Function gets called by providing an amount to "saturate/desaturate".

OP spelled-out the mix function...

origRGB * ( 1.0 - amount ) + grayscale * amount

That's just mix() spelled out...

mix( origRGB, grayscale, amount )

And you can easily make it smoothstep by just swapping mix w/ smoothstep...

smoothstep( origRGB, grayscale, amount )

If you're using HLSL, then mix() is replaced with lerp(), but smoothstep() is still smoothstep()

lerp( origRGB, grayscale, amount )
smoothstep( origRGB, grayscale, amount )

Most folks use a mix/lerp function when they need the "linearness" of 0 to 1 mixing.
And, b/c it's a cheaper function call than using smoothstep.

Smoothstep is usually used when you have a narrower range of options, but want to smooth out that transition instead of creating start contrast.

EG: if you were trying to mix from black to white RGB's, you could do that with mix/lerp and it'd provide a nice range of grays.
But, if you were trying to mix between an already very gray amount to a bit more gray amount... and let's say stretch that transition out across a large planed surface... if you did a linear mix you'd probably see lines where the colors sharply changed. But, with a smoothstep you'd get a much nicer blending between the colors.

In your situation, you're saying you need to output things at the high end...like you're trying to return 245 to 255, but using a value you calculated that is from 0 to 255.

You'd just use

smoothstep( 245, 255, calculated value 0 to 255)

And that lets you calculate a very wide value, but use it to reduce to a very narrow output.

There's probably better guides that explain this better.
But, what you need to focus on is...

smoothstep is used like mix
smoothstep provides a smoother transition
smoothstep is a more expensive operation (so don't just replace all your mix() with smoothsteps)

Mix/Lerp are usually "good enough" for most situations. You have to really be looking for a reason to use smoothstep. But, a lot of advanced graphics have definitely found a use for it, especially when trying to avoid banding and other issues with color transitions.

Side note, what OP said about their code is a good point, too. We have to target code to whatever platform / use-case we're using. If we're doing a GLSL game, then we might have some intrinsics to abuse. But, some platforms need us to spell-out code, since they don't have the intrinsics.

Also, wanted to point out that the way OP coded their desaturation function... you can input negative amounts to (ab)use it as a color over-saturation function. This is great when you want to punch-up the colors on things. Ben Cloward showed how abusing the desaturate node in Unreal Engine by inputting negative amounts could be used to create rain effects... over-saturatre the color then darken it. I used a version of desaturate like what OP coded above to implement that in some game shader code I'm tweaking, and it works great.

@geextahslex
Copy link

geextahslex commented Mar 16, 2024

@ccritchfield Hi I changed my post, I hope it's more clear now what I am looking for :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment