How can I make a shader interact with lights like a CanvasModulate?

hiulithiulit Posts: 6Member
edited September 2019 in Shaders

I have this shader to "tint" the screen, like a CanvasModulate would do. Now, why would I create a shader if there is already a thing that does the same?
Well, because I've added some variables, such as brightness, contrast, saturation, etc to have better control over the "tinting" :)

Here's the shader with default values, like if it was a CanvasModulate.

Here's the shader with some tweaked settings. Much, much better IMHO.

Anyway, let's cut to the chase.

Here you can see an image using a CanvasModulate with a blue color and some colored lights. The lights are like "cut" from the blue. That's what I like about the CanvasModulate.

Here's the image with the shader and the same lights:

And here's the shader code:

    shader_type canvas_item;

    uniform vec4 dawn_color : hint_color = vec4(0.86, 0.70, 0.70, 1.0);
    uniform vec4 day_color : hint_color = vec4(1.0, 1.0, 1.0, 1.0);
    uniform vec4 dusk_color : hint_color = vec4(0.59, 0.66, 0.78, 1.0);
    uniform vec4 night_color : hint_color = vec4(0.07, 0.09, 0.38, 1.0);

    uniform float brightness : hint_range(0.0, 10.0, 0.01) = 1.0;
    uniform float contrast : hint_range(0.0, 10.0, 0.01) = 1.0;
    uniform float saturation : hint_range(0.0, 10.0, 0.01) = 1.0;
    uniform float pop_strength : hint_range(0.0, 10.0, 0.01) = 1.0;
    uniform float pop_threslhold : hint_range(0.0, 10.0, 0.01) = 1.0;

    uniform bool overlay = false;

    void fragment() {
        vec3 base_col = texture(SCREEN_TEXTURE, SCREEN_UV).rgb;
        vec3 out_col = base_col;
        vec4 final_color;

        float grey = dot(base_col, vec3(0.299, 0.587, 0.114));

        if (overlay) {
            if (grey > 0.5) {
                out_col = 1.0 - (1.0 - 2.0 * (base_col - 0.5)) * (1.0 * night_color.rgb);
            } else {
                out_col = 2.0 * base_col * night_color.rgb;

        out_col = mix(vec3(grey), out_col, saturation);
        out_col = (out_col - 0.5) * contrast + 0.5;
        out_col = out_col + pop_strength * max(grey - pop_threslhold, 0.0);
        out_col = out_col * brightness;

        if (AT_LIGHT_PASS) {
            final_color = vec4(1.0);
        } else {
            final_color = vec4(out_col * night_color.rgb, 1.0);

        COLOR = final_color;

How can I achieve the same CanvasModulate's lighting effect with the shader?

Here's a little demo project if you want to test it
You can toggle between the ColorRect with the shader and the CanvasModulate.



  • hiulithiulit Posts: 6Member

    Here is the latest version of the shader

    I'm getting closer but I can't do it properly.

  • hiulithiulit Posts: 6Member

  • TwistedTwiglegTwistedTwigleg Posts: 5,344Admin

    I've been playing around with it and by using a separate Viewport for the lights, I've gotten something close-ish:

    I tried just modifying the shader, but the closest I got was a similar result to the latest shader. I was unable to find a solution that removed the tint from the modulation, so I decided to go a different route and use a Viewport instead. I do not know if using a Viewport will be helpful though, as it would require all of your lights to be a separate Viewport.

  • TwistedTwiglegTwistedTwigleg Posts: 5,344Admin

    Well, I sort of got the same results:

    It is much closer to the reference than my previous attempts. Unfortunately, I had to change the light values of the modulate properties of the lights to get those values. That said, by tweaking the modulate and self_modulate values, there is quite a bit of flexibility in how lights are rendered.

    Here is a link to the project on my Google drive.

    Hopefully it helps! :smile:

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file