Implementing color masks in Godot

Header image In many graphics editor (ex.: Krita, Photoshop) there is such thing as color masks. This is a convenient way to change the color of your image or parts of it, trim image to specific shape, etc.

Image without mask Image with mask

As you can see, the mask is defined by two parameters: * Color (in this case it's #6798b0) * Opacity of the mask layer (33%)

Today, we will try to use color masks in Godot to implement the effect of aerial perspective. This is a very useful effect as it enables you to save some resources since you don't need to have separate sprites for “close” and “far” layers. Example of effect in Godot

Godot already has modulate property in every CanvasItem instance, but there is a problem with alpha-channel: if we set it, then the whole layer become transparent instead of just mask.

To fix this we'll write a shader. It will take a mask as an argument and apply it to image. Here is how we specify our mask in the editor: Gradient selector

This is the gradient with only one component (vertical line in center). We can it's color with this selector: Color selector

As you can see, it has all needed parameters: RGB and alpha-chanel.

Here is the shader itself, with comments:

shader_type canvas_item;
// Disable unneeded features
render_mode blend_disabled, unshaded;

// We'll take out mask as input. In editor it'll look like a gradient selector
uniform sampler2D mask: hint_white;

// This function is used to manipulate  every pixel of the texture - right what we want
void fragment() {
	// First, we need to convert our input mask into texture
	vec4 mask_tex = texture(mask,UV);
	// Then, we need to get our actual texture
	vec4 img_tex = texture(TEXTURE, UV);
	// The resulting color will be a mix of thous two texture. In what proportion? It depends in the alpha-channel of our mask.
	COLOR.rgb = mix(img_tex, mask_tex, mask_tex.a).rgb;
	// The last thing is we should not draw mask in points where original texture is transparent.
	COLOR.a = img_tex.a;