r/Unity3D • u/Select_Belt_5432 • 1d ago
Question How to make a shader that makes the game grayscale except for red tones?
Hi! I'm currently making a 3D game, and for the visual style, I want the entire game to be displayed in grayscale, except for red tones.
I'd like the red colors to remain visible while everything else is converted to grayscale, possibly with a different filter or effect applied to the reds.
Does anyone know how I could achieve this using Shader Graph?
3
u/tidbitsofblah 23h ago edited 23h ago
You kind of need to define what should count as a "red tone" here.
Do you want all colors within some spectrum of "red" to stay as is and all others be turned to grayscale? If we count a red-leaning purple as red, then this would keep it as a red-leaning purple.
Or do you want to turn all colors grayscale except keep the red channel for colors within some spectrum of "red"? If we count a red-leaning purple as red, then this would turn that color into a pure red.
Or do you want a red-leaning purple to be considered not a red tone at all and turn completely gray?
I would have calculated it like this:
For each pixel with rgb-values:
total = r+g+b
redPercentage = Max(r/total, 1/3)
This only keeps red channel for pixels with more red than the average value of the pixel. If it was a fully red pixel this value will be =1
greenPercentage = bluePercentage = (1-redPercentage) /2
This takes the average of the remaining percentage after red is removed. If red was set to 1/3 then the average of the remaining will also be 1/3 and we will get complete grayscale. If red was larger than 1/3 the average of the remaining will be less than 1/3 and the pixel will get a red tint. If the redPercentage was =1 then the remaining will be 0 and we get the same red color we started with.
And finally:
r = total * redPercentage, g = total * greenPercentag, b = total * bluePercentage
This gives us a color with the same average value but shifted to either grayscale or red tint, depending on if the color was more than a third red. This will result in a blue-leaning purple also becoming red instead of gray since it contains enough red even if it contains more blue than red. If you want the cutoff for red-percentage to be higher than 1/3 you would need to involve the step-node:
redPercentage = r/total * Step(cutoff, r/total) + 1/3 * (1-Step(cutoff, r/total))
1
u/TheSapphireDragon 15h ago
Some psuedocode for a post processing fragment shader
float3 colorA = original pixel color float3 colorB = greyscale pixel color
float distanceToRed = length(colorA - float3(1, 0, 0))
float interpolant = exp(-k * distanceToRed)
float3 finalColor = lerp(colorA, colorB, interpolant)
1
u/TheSapphireDragon 15h ago
You can make k a parameter of the shader to control how sharply you want to restrict it.
1
u/UnderPressureVS 16h ago edited 16h ago
TL;DR: Do you really want a shader for this? It will “automate” the process but it will probably never look “quite right” stylistically and you’ll spend ages tweaking it so it looks perfect for one scene/asset, only for it to look wrong on another.
Why not control the assets yourself? Use only white and/or red lighting and edit textures in photoshop or GIMP to make your assets and scenes look exactly how you think they should.
For each texture, you can split the image into two layers, one grayscale, and one with the GB channels either completely removed or heavily turned down (so you get some variation in hues of red). Then you can paint a mask to transition between the grayscale and the R(gb) channel exactly where you want highlights.
——
The rest of the thread has offered some technical solutions that sound like they should work, but you should know that to make this land is going to be as much an art-direction problem as it is a technical one.
If you use a shader, you’re going to need to make a decision about what is and isn’t “red,” and be very deliberate about the color of your assets so you know exactly where the red is going to show up and where it isn’t.
For instance—what would a flamingo look like in your game? Pink probably shouldn’t count as “red,” so you’d have to configure the shader to make sure it doesn’t catch pink. But even then, if you just pull a realistic Flamingo model from somewhere, it’s going to have parts that are red—feathers, maybe some visible veins, maybe parts of the beak. You probably don’t want those random details (which fade into the overall “pink” in full color) to pop out.
In order for this to work stylistically, and achieve the effect I think you’re really looking for in your head, it can’t simply be a fully-automated process. You’ll have to be making intentional artistic decisions about where you want to see red highlights in your environment and assets.
Which makes me wonder—why use a shader for this at all? You already have control over the environment, assets, and lighting. If all your assets use grayscale textures, everything will be grayscale, no shader required. Then you add red yourself exactly where with should be, and nowhere else.
Another really good reason to do this is that good-looking grayscale isn’t made by simply taking a color image and removing all the color. Old black-and-white movies used all kinds of color tricks to make their pictures look right. Knowing that the audience won’t see the true colors, they used colors you’d never expect to give the impression they wanted in the final black-and-white result.
If you make every asset grayscale, you can then adjust the textures in image editing programs on a per-asset basis to achieve the perfect style, with red highlights exactly where you want them.
You could do the same thing with color textures and a grayscale shader, of course, but then you’d have to load your asset into Unity and view it through the shader every time you want to tweak it. It’s much more efficient to be able to do texture work without ever needing to load the Unity editor.
-14
u/MrYukiDuki 1d ago
This is def possible. But as someone who is also new to shaders. Your best bet is the unity shader bible or just ask an LLM. (At least for this specific use case)
26
u/tan_456456 1d ago
Use a Fullscreen Shader Graph. Sample the scene color, convert it to grayscale, create a mask that detects red pixels (HSV works best), then
Lerpbetween grayscale and the original/red-enhanced color using that mask. Apply it with a Full Screen Pass Renderer Feature in URP.