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

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 http://s000.tinyupload.com/index.php?file_id=67293910729717953392. You can toggle between the ColorRect with the shader and the CanvasModulate.

Thanks!

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.

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:

Write a Reply...