Simple 2D Lighting

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)?

what about the quad scripts (.vp and .fp)? which content?

4 posts were split to a new topic: Water effect

How can I get this result:
combination

front sprite should not be transparent
he should be black
How can I do it?

One solution could be to draw the background to one render target, the player to a second and the lava to a third render target. You render the player and the lava render targets onto the background and either draw as is from the player target or with rgb set to 0.0 depending on lava or not. And finally you apply the lights.

2 Likes

add some flicker to the light,
not all objects of light (selectively)
how to do it?

I don’t really understand your question. Could you please elaborate?

If you mean something like flickering light from e.g. fire, there is no problem - you have a game object with a sprite that has a light material, right? Then animate GO’s scale up and down relatively quick :wink:

I mean this piece of code:

function update(self)
...
    -- draw lights
	render_to_rt(self, self.light_rt, function(self)
		clear(AMBIENT_LIGHT, nil, nil)
		-- add some flicker to the light
		local constants = render.constant_buffer()
		constants.time = vmath.vector4(math.random(90, 100) / 100)
		render_to_world(self, { self.light_pred }, constants)
	end)
end