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

isairisair Posts: 3Member
edited June 25 in Shaders

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.

Comments

  • isairisair Posts: 3Member

    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.

  • isairisair Posts: 3Member
    edited July 5

    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);
        }
    }
    

Leave a Comment

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