Simple 2D Lighting

I’m looking to implement some very simple 2d Lighting similar to Hammerwatch coliseum, I understand the logistics of how it works but was just looking for a solution to it through defold, any help?

some example photos

4 Likes

I have something fairly similar in a WIP project (nevermind the black pillars, they are just 3D meshes and have nothing to do with the lights).

Here is how I handled it:

  • Make two render targets
  • Render your normal scene to the first one
  • Render your lights to the second one
    • Clear it with black (or your ambient light color)
  • Make a material for your render target quad that uses both render target textures
  • In that material’s fragment shader, blend the two textures however you wish
    • Just multiplying them together is easiest

You can read through this thread and this thread for info on how to set up and use render targets. See here for a simplified step-by-step list of what you need to do in your render script update function to use a render target.

Note: This is basically just a mask, there will be no cast shadows or anything like that

17 Likes

Looks great!! You should share a technical demo, I would love to see it in action! :smiley:

5 Likes

The approach we used on Hammerwatch Coliseum is very similar to what you suggest. IIRC we have both an add pass and a multiply pass where lights are applied to a render texture that has been cleared with an ambient light color

5 Likes

Il try an implementation and get back to you on how it goes, thank you very much

1 Like

How do I go about rendering the lights to a separate target

  1. In your render script, make a separate render target for the lights.
  2. Also in your render script make a separate predicate for the lights.
  3. Give your light sprites a new material with a tag matching your predicate.
  4. In your render script update, for each render target, do:
    1. enable render target
    2. clear it
    3. draw stuff to it
    4. disable it

In the first thread I linked before, Sicher explains how you use multiple render targets in the material that they are rendered with.

4 Likes

im struggling to get the render target for the light into the shader i know im doing something wrong i just dont know what

could i maybe get a code example of the render script and the shader

Here is my material for the render target quad:

The shader in my project has a ton of extra stuff in it, but I tested this and it should work for you:

varying mediump vec4 var_position;
varying mediump vec3 var_normal;
varying mediump vec2 var_texcoord0;

uniform lowp sampler2D lighting;
uniform lowp sampler2D base;

void main()
{
	vec4 light = texture2D(lighting, var_texcoord0.xy);
	vec4 b = texture2D(base, var_texcoord0.xy);
	gl_FragColor = b * light;
}

And the relevant part of my render script:


-- In render script update()

	-- self.base_RT == Render target for base scene
	-- self.shadowmap_RT == Render target for lights

	--- BASE RENDER
	--		Draw normal stuff to render target
	render.enable_render_target(self.base_RT)
	render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})
	render.draw(self.tile_pred)
	render.draw(self.particle_pred)
	render.draw_debug2d()
	render.draw_debug3d()
	render.disable_render_target(self.base_RT)

	--- SHADOW MAP
	--		Draw lights and Line-of-Sight blockers to render target
	render.enable_render_target(self.shadowmap_RT)
	render.clear({[render.BUFFER_COLOR_BIT] = self.shadowmap_clear, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})
	render.draw(self.lighting_pred)
	render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
	render.draw(self.los_pred)
	render.disable_render_target(self.shadowmap_RT)

	--		Draw quad with render target texture on it
	render.set_view(zeroMatrix)
	render.set_projection(self.RT_proj)

	render.enable_texture(0, self.shadowmap_RT, render.BUFFER_COLOR_BIT)
	render.enable_texture(1, self.base_RT, render.BUFFER_COLOR_BIT)
	render.draw(self.shadowmap_pred)
	render.disable_texture(0, self.shadowmap_RT)
	render.disable_texture(1, self.base_RT)
5 Likes

Massive thanks to you, I have it working now, il be sure to give you a mention in the credits

10 Likes

Would be awesome, if someone of you would create a tutorial about lighting in 2d games :slight_smile:

9 Likes

I could maybe put something together for you

13 Likes

Hey there.

Try to implement 2d lightning. But I dont understand what to do exactly. Tried the code above in a clear project without success.

What about the tutorial or an example project in a clear enviroment? Would be very helpful :slight_smile:

1 Like

Have a look here

1 Like

It is not that complex although you do need to mess with the render script, a shader and some new materials. The basic idea is this:

STEP 1 - DRAW NORMAL COMPONENTS TO RENDER TARGET
Draw your sprites, tilemaps and any other components you have in your game to a render target (a texture in memory) instead of directly to screen.

You create a render target using render.render_target(). You enable it in your render script, draw using the normal render calls, then disable it again. Done!

STEP 2 - DRAW LIGHTS TO RENDER TARGET
Draw your lights to another render target. Clear the render target with a semitransparent black color for some ambient light.

In this example the lights are circular sprites of various sizes and with increasing transparency.

They are drawn with a Blend Mode set to Add instead of Alpha. 02

The sprites have a different material assigned than the normal sprite material. The material uses another tag/predicate so that it doesn’t get drawn with everything else in step 1.

STEP 3 - DRAW A MIX OF BOTH RENDER TARGETS TO A QUAD ON SCREEN
Draw a mix of the two render targets to a model component with a quad (a rectangle) and draw it so that it fills the entire screen.

The quad has a separate material which takes two textures as input parameters and it uses a new tag/predicate so that we can draw it in a separate pass.

Each pixels is a multiplication of the pixel value from the normal graphics and the light. Final result:

I’ve put it all together in an example here:

CODE: https://github.com/britzl/publicexamples/tree/master/examples/simple_lights
DEMO: http://britzl.github.io/publicexamples/simple_lights/index.html

28 Likes

I have some questions about simple_lights example:

  1. Why create render target with depth, if we not write to depth buffer? And why clear it, if we not write to it?
  2. quad.vp has varyings var_position and var_normal. They are calculated but not used anywere?
5 Likes

Good points. Parts of the example was copied from another projects and I didn’t consider some of the settings. Pushed some changes. Thanks!

6 Likes

Thanks for the example @britzl.

I dont understand the mechanics at all and the example looks a little bit to complicated to explain :smiley:

Maybe I have to dive much deeper in it. Never worked with the render and the materials in that way.

Your example works fine and I fooled around a bit to get into it :wink:

But do you understand the basic principle behind it? One image with everything fully lit. One image with a semi transparent black/grey (ambient light) with the areas around the lights transparent. Overlay the image with the lights on the normal one to compose the final image with everything dark except around the lights.

This effect is achieved through some renderscript and shader “magic” that might seem very hard to understand in the beginning. But playing around with different examples might help you understand.

6 Likes

Hi.

Sure, its kind of layering / masking.
I started a new project to reproduce it and dupilicated materials etc. but… you use a complete different render-script and an orthographic camera & Script (Helper).

Isnt it possible to “more simpify” the demo? Without any external scripts? Just to have the real bone basics for a better understanding? Maybe some comments or maybe with screenshots or whatever . including - or special - the render-script.

I think there arejust a few changes / additions to the standard-render-script.

Are really 3 materials needed? and a mesh (quad)?