• Godot HelpShaders
  • CanvasItem shader to make light only effect sprites above its y coords

Hi, I'm making a game where y is the depth of all sprites that are not ground tiles. Achieving correct render order was easy, however I'm struggling with lighting. Basically I want sprites that are closer to the camera (higher y value of the origin) than the main light source I have in the game (the player) to not receive any light. To achieve this I have written the following shader:

shader_type canvas_item;

uniform vec2 light_position = vec2(0, 0);
varying vec2 view_position;

void vertex() {
	view_position = (EXTRA_MATRIX * (WORLD_MATRIX * vec4(VERTEX, 0.0, 1.0))).xy;
}

void light() {
	if (view_position.y > light_position.y) {
		LIGHT = vec4(0, 0, 0, 0);
	}
}

And I'm passing light_position to the shader from the light source itself. Using the following script:

extends Light2D


var over_material


func _ready():
	over_material = get_node("OverMaterialHolder").material


func _process(_delta):
	var transform = get_global_transform_with_canvas()
	over_material.set_shader_param("light_position", transform.get_origin())

I have tried a few different variations of this but none of them work properly and I seem to be stuck. Could anybody point me to the right direction? It'd be much appreciated.

Attached is a screenshot from a buggy implementation I've made that happened to work in some positions of the player, to better illustrate what I want to achieve consistently.

10 days later

Had some free time tonight so ended up resolving this. The code will need going over to get it to its best shape but here is the gist of it.

Script attached to the light source:

extends Light2D


var over_material: Material


func _ready():
	over_material = get_node("MaterialHolder").material

func _process(_delta):
	var screen_coordinates = get_viewport_transform() * global_position
	over_material.set_shader_param("light_position", screen_coordinates)

Shader attached to the overground tiles:

shader_type canvas_item;

uniform vec2 light_position = vec2(0, 0);
varying vec2 position; // position of the sprite in screen pixel coordinates

void vertex() {
	vec2 local_position_bottom = vec2(0.5, 1.0);
	position = (WORLD_MATRIX * vec4(local_position_bottom, 0.0, 1.0)).xy;
}

void light() {
	if (position.y > light_position.y) {
		LIGHT = vec4(0, 0, 0, 0);
	}
}
2 years later