2D lights and shadows sample

It’s working in Safari 14.1.2 with shadows for me. FWIW I actually find the performance in Safari is better than that in Chrome.

1 Like

works for me know (I’m not sure what it was and why)
UPD: Hmm, I don’t understand what’s going on

1 Like

Amazing! Thank you!
I use it on my game. It works! I’ll read the render light config to apply on my own render later.
Two questions:

  1. Why the light stay on scene after a restart / reload (video) and how to remove it?

  1. I should create two separated tilemaps to achieve a good result (one with the material and one without). Is there any other solution? Extra tilemap will affect the performance? (this game is really simple so I don’t mind but I’d like to know it)

thank you again!

Ah, you found a bug! The lightsource.script did not remove the light when deleted. I’ve pushed a new version of the sample project that fixes this.

If you want only certain tiles to cast shadows then yes, you need two tilemaps, one with the light_occluder_tile_map.material and one without.

As with all other components each additional component you add will have an impact. But usually it’s only a small impact. You should measure it using the profiler to be sure.

Yes, maybe. The tilemap material (or rather the shader program) could perhaps be modified such that it doesn’t render tilemap layers with a certain z-value. You could create your tilemap so that layers with a z-value larger than some value will cast shadows while others won’t.

3 Likes

I changet 0.5 to 1 in default_size = default_size * 0.5 and now everything works fine. I don’t know why, but 0.5 is not the correct value for my display.

There are also some strange things in the scene editor. Hm…

1 Like

Yeah, it was a quick tweak to make some kind of best guess, but I think it’s better to pass a size that works for your project like this instead:

lights.init({ render_target_size = 512 })

I set render_target_size to 1368 in the example project, is that correct? This is the maximum game resolution in the project settings.

There is also a point that if you set the lighting radius of 1024 or more then there will be noticeable inaccuracies in the raycasting of black figures, they become rude. How would it be possible to adjust their accuracy higher?

It’s going to be pretty expensive to calculate lights with this kind of radius on such a massive render target.

The problem with precision of shadows is caused by a light diameter which is larger than the render target.

1 Like

Two additional articles that might help someone:

10 Likes

This sample has been updated with some additional functionality and fixes:

  • Lights of different sizes are now rendering properly
  • The light falloff property was previously ignored
  • The light falloff is now applied only to the alpha channel
  • Lights can now be temporarily disabled

A big thank you to @Haath for contribution of light falloff fixes.

The HTML5 demo project has been updated: Lights 1.0

19 Likes

Is there any way to change the way sprites are rendered in shadows, I.E. making sprites/pixels invisible when they are covered by a shadow?

1 Like

Depends what exact effect you would like to have, but one idea is that you draw shadows and occluders after you draw other sprites (so the sprites are covered by shadows). For more smooth effect you can change tint of sprites covered by shadows depending on their position after Occluder, but there are multiple solutions for this that come too my mind, don’t know which one would be the best :sweat_smile: I wonder if there are some other interesting solutions

This repository is mostly just a sample for inspiration. For specialized functionality like this you should play with the render script and shaders. I’ve actually already experimented with exactly what you’re describing to create some sort of fog-of-war that hides shadowed objects.

varying mediump vec2 var_texcoord0;

uniform lowp sampler2D tex0;
uniform lowp sampler2D tex1;
uniform lowp vec4 tint0;
uniform lowp vec4 tint1;

void main()
{
    vec4 tint0_pm = vec4(tint0.xyz * tint0.w, tint0.w);
    vec4 tint1_pm = vec4(tint1.xyz * tint1.w, tint1.w);

    vec4 color0 = texture2D(tex0, var_texcoord0.xy) * tint0_pm;
    vec4 color1 = texture2D(tex1, var_texcoord0.xy) * tint1_pm;

    if (color0.a > 0.1 && color1.a < 0.5)
    {
        discard;
    }

    gl_FragColor = color0;
}

Here I’m drawing the shadow-clipped sprites together with the lights, and discard pixels where the lights texture (color1) has alpha less than 0.5, which in this case is slightly above the ambient light alpha of my game.

6 Likes

Edit: This appears to be my video card, as the example here also renders that way. I opened that link on another PC with an nVidia card, and it’s fine. I have an AMD RX 6800. I’m open to solutions, but I suspect this would be hard to troubleshoot without the same card/drivers. I’m on the latest drivers.

This doesn’t seem to be rendering correctly for me on the latest Defold 1.4.8. It’s as if the shadows are too close to the light source. I’ve included a screenshot of the sample from the repo and my own project where the same effect is happening. Is there a quick fix for this? I’m using this in a game jam that ends this weekend, so will need to find an alternative if I can’t get this to work. I’m not familiar with shader programming, and I’ll get lost deep in the woods for hours if I try to figure it out now.

Many thanks to everyone who’s contributed to this and some other extensions/libraries I’m using (I will surely list them in the credits). It’s been really easy to import and adapt them, and I’m surprised how fast this is coming along. This is my first game jam, and maybe I’ll finally publish my first game.

image

1 Like

I made a change to the shadow_map.fp that fixes this for AMD and doubles the shadow distance for nVidia. Since it also works correctly on my iPhone without any change, I consider it an AMD driver bug.

I’ve never tried to work with shaders before, so I’m not sure that I understand fully what is going on here. I think it stores the distance to the occluder in a 1d texture. It gets this from the depth buffer bit. I doubled this distance before it is written to the shadow map, and that fixes it for AMD and breaks nVidia.

I’m hoping that gives someone that is interested some idea of how to work around this with AMD and still work with nVidia. I feel like it’s something with how AMD calculates the depth bit vs nVidia, but this is uneducated speculation on my part. Maybe there is some assumption in the shader program that works for nVidia but not the AMD drivers. Something with precision or clamping.

I would not even have noticed this if I wasn’t using my AMD card at the time… I feel it would be useful to fix this if it does affect all AMD cards and current drivers. Here is the change I made in a fork of this repo:

3 Likes

Hey i am curious how were you able to sample the background inside of the cone, while having the rest of the screen around it Darker? I have been having a hard time trying to get RT of the light cone also include the texture thats behind it.

It’s been a while since I’ve dabbled in game dev and Defold. You can download the project at the link below and open it in the latest version of Defold. It was a game jam game I wasn’t able to finish, so it’s pretty rough. Things are not organized intuitively, and the code is a bit messy.

I think the answer to your question can be found in the infinite_map.collection. In that collection, check the game object named “objects”, then “player” under that. You’ll see the player.script there. In player.script, there are two go.property at the top for light arc and distance. I was going to change these values during gameplay, but I don’t think I got that part finished.

You’ll also see under “player” a game object for “light” with the “lights/lightsource.script” attached. That is from the 2D Lights and Shadows library (which I had manually added to the project since I had made the change described above to fix the AMD bug). Note that the “light” game object is positioned at the end of the flashlight of the player sprite. That is where the light cone will originate from, and it is parented to the player so it inherits its position and rotation.

Basically, the arc and distance properties are given in that lightsource.script. I just set those to match the similarly named properties under my player.script using the go.set function. You can see that in the reset_player function at the top of player.script. It’s those properties under lightsource.script that define the cone (as well as color, falloff, etc.).

You can also see in player.script, on_message where I check the ghost angle to the light source to determine “collision” with the ghosts.

The game still builds and runs in the latest version of Defold, so give it a try. Let me know if you have any other questions.

5 Likes

Congratulations on your first released game @paulbaucom! :partying_face:

It’s a nice one game, great for a start, so wishing you more and more successes! :wink:

3 Likes



Dim lights overwrite bright lights if dim lights are lower on hierarchy. That causes weird effects. Since light can be dim on R channel and bright on G channel and vice versa (for example) it can be problematic (you will not be able to easily sort light rendering order).

It would be super cool to implement this technic in Defold. It’s slightly different, but allows to make soft shadows and is claimed to be super fast :slight_smile:

I am not able to understand atm are there any limitations in Defold to make it possible:

https://slembcke.github.io/js/lighting-2d/soft-shadows.js