The camera now updates a shader with the positions, strengths and colors of nodes in the light group. The sources cast light around them on the z layer they are on. They do not work for Tilemaps yet and can't be occluded. The light of different sources does not get stronger if they overlap.
80 lines
2.7 KiB
Plaintext
80 lines
2.7 KiB
Plaintext
shader_type canvas_item;
|
|
render_mode unshaded;
|
|
|
|
uniform mat4 global_transform;
|
|
uniform sampler2D light_data;
|
|
uniform int n_lights = 0;
|
|
uniform vec4 dark_color : hint_color = vec4(0.25, 0.0625, 0.25, 1.0);
|
|
uniform vec4 default_light_color : hint_color;
|
|
uniform float light_level : hint_range(0.0, 1.0) = 0.0;
|
|
uniform float offset_modifier : hint_range(0.0, 8.0) = 1.0;
|
|
uniform int n_light_bands : hint_range(1, 13) = 7;
|
|
uniform bool will_smooth_shade = false;
|
|
uniform float band_decay_rate : hint_range(0.0, 1.0, 0.05) = 0.5;
|
|
uniform float light_strength_modifier : hint_range(0.0, 1.0) = 1.0;
|
|
|
|
varying vec2 world_position;
|
|
|
|
void vertex() {
|
|
world_position = (global_transform * vec4(VERTEX, 0.0, 1.0)).xy;
|
|
}
|
|
|
|
void fragment() {
|
|
// floor() the world_position so that it matches the native resolution
|
|
vec2 frag_position = floor(world_position);
|
|
float m_value = 0.0; // 0.0 == dark, 1.0 == light
|
|
vec4 light_color = default_light_color;
|
|
// Iterate through every light source.
|
|
for(int i = 0; i < n_lights; i++) {
|
|
// Get the data for this light source as passed in via texture
|
|
vec4 texel = texelFetch(light_data, ivec2(i, 0), 0);
|
|
|
|
// How far the light source extends
|
|
float radius = texel.a;
|
|
// How bright the light source is
|
|
float strength = texel.b;
|
|
// Distance from this pixel to the light source then normalize
|
|
float dist = distance(texel.xy, frag_position);
|
|
dist = min(dist / radius, 1.0);
|
|
|
|
// offset so that light source doesn't fade immediately
|
|
dist = max((dist * offset_modifier) - (offset_modifier - 1.0), 0.0);
|
|
|
|
float value = 0.0;
|
|
if(will_smooth_shade) {
|
|
value = (1.0 - dist) * strength * light_strength_modifier;
|
|
}
|
|
else {
|
|
// decay offset so that max value == 1.0
|
|
float offset = pow(band_decay_rate, float(n_light_bands));
|
|
for(int p = 0; p < n_light_bands; p++) {
|
|
// Get max radius for this light band
|
|
float radius_check = 1.0 - pow(band_decay_rate, float(p + 1)) + offset;
|
|
// if pixel is less than the band's radius, then it's in the pth band
|
|
if(dist < radius_check) {
|
|
// Set it's value to the position of the band before this one
|
|
value = (pow(band_decay_rate, float(p)) - offset)
|
|
* strength * light_strength_modifier;
|
|
// Nearest band was found, so break the loop
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
value = clamp(value, 0.0, 1.0);
|
|
if(value > m_value) {
|
|
m_value = value;
|
|
light_color = texelFetch(light_data, ivec2(i, 1), 0);
|
|
}
|
|
}
|
|
|
|
// mix darkness with light based on light level
|
|
vec4 ambient_color = mix(dark_color, light_color, m_value);
|
|
// apply global light level
|
|
ambient_color.a *= 1.0 - light_level;
|
|
// get screen color for this pixel
|
|
vec4 screen_color = texture(SCREEN_TEXTURE, SCREEN_UV);
|
|
|
|
// apply multiply blend mode
|
|
COLOR = screen_color * ambient_color;
|
|
|
|
} |