0 votes

Hi!

I have some textures I would like to apply a grey effect eventually.
To explain myself better, I want to apply this effect to some textures when disabling or enabling them.

There are a lot of different textures and I would like to accomplish this by code (if possible) instead of creating a "disabled_texture".

Any ideas? Someone suggested shaders but I don't know how they work and the documentation I found does not help me at all.

Greetings

asked Jul 16, 2016 in Engine by Not_a_Robot (85 points)

2 Answers

+2 votes
Best answer
  1. Create a new CanvasItemMaterial:

  2. Edit the new CanvasItemMaterial:

  3. Create a new CanvasItemShader:

  4. Use this code as Fragment Shader:
    COLOR.rgb = vec3(dot(COLOR.rgb, vec3(0.299, 0.587, 0.114)));
    Here you'll find an explanation of the calculation above.

Enabling/Disabling grayscaling at runtime

You can extend your shader code to have an uniform variable to control whether applying the grayscale filter or not. This would look like this:

uniform bool grayscale = false;

if (grayscale) {
    COLOR.rgb = vec3(dot(COLOR.rgb, vec3(0.299, 0.587, 0.114)));
}

After changing your shader code to the above, you should have a new option in your CanvasItemMaterial:

answered Jul 16, 2016 by timoschwarzer (700 points)
selected Jul 16, 2016 by Not_a_Robot

Better solution than mine! Colours don't have the same gamma contribution and this really makes use of it (for example, green is brighter than blue on a CRT screen)

Small warning though: use if with care in a shader, for some graphic cards it means compiling as many binaries as combinations since GPUs work differently than CPUs.

Thank you for your answer! Really great effect, what I was searching for!

Is this process possible to be made in GDScript? I would like to create the shader and attach it to all TextureFrames I have.

I would like to know too, is this the only way to get that effect? I expected something like "TextureFrame.set_color("grey")", because as far as I know, shaders are code that are always running and I'm afraid about that.

Grey is not a color, it is the absence of color (or the presence of all of them, depends on the brightness :p), so you cannot really do set_color(grey).

When you do set_color you'll apply a tint over the base texture, but you cannot remove colors this way. You could just use that if your texture is monochrome, but otherwise you need a shader.

The GPU executes shaders tens of thousands times faster than GDScript, and this shader is so simple it might even be faster than the default one. Don't worry too much about that :)

This shader is nothing for a GPU...
So, the optimal way would be writing that shader, save the shader (NOT the material) as .shd (Go into shader edtiting mode and click the save icon at the upper left hand of the inspector). Then set the disabled-property with GDScript

I rewrote this shader in VIsualShader for Godot 3.1 over on my blog. Thanks for making such a simple shader that I could use as a base for learning about VisualShader!

+1 vote

Here is a simple fragment shader that will turn your sprite into greyscale:

// Get the color from the texture
vec4 col = tex(TEXTURE,UV);

// Compute greyscale color (mean of red, green and blue)
float grey = (col.r + col.g + col.b) * 0.333;

// Apply greyscale color (same for red, green and blue, then we keep the same alpha)
COLOR = vec4(grey, grey, grey, col.a);

To apply this shader, go to the Material property of your sprite (or any CanvasItem), create a new material, and assign it a new shader. Then you'll be able to write the fragment shader inside :)

answered Jul 16, 2016 by Zylann (26,049 points)

This results in grayscale, however it is not as accurate to human vision as the formula in my answer. See here ;-)

Thank you Zylann! ;)

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.