Hello everyone,
Just wanted to know about how do we implement 2d lighting in Defold.
So, basically I want to create dynamic lightened scenes, with a sort of night like environment and just torches to light the way.
This could be done easily if we had lights, but as we don’t, how can we implement this in Defold.?
Thanks in advance.
Are we talking about dynamic 2d light where the sprites have both a diffuse texture and normal map and shadows are applied on the sprite based on the direction of the light (like SpriteLamp) or do you mean simple “fake” lights where areas outside the light radius is dark/black while within the light it gets rendered using it’s normal texture, optionally gradually darker the further from the lightsource?
For the latter I think something like this would work:
- The torches should have a sprite with an image in a circular shape with increasing transparency the further from the center you go
- The sprite should have another material and predicate so that it doesn’t get rendered with normal sprites
- Render the game to a render target first
- Next render the lights to a separate render target
- Finally combine the two render targets into a single image with the end result
For the former we need to wait a little while longer since we have an almost finished solution that is awaiting some additional work before we can release it.
@britzl think the latter should do the job.
Thanks for clearing my doubt
. BTW where can I learn using materials, render targets and render scripts?
Also there is something here, why is the reply button in Defold forums working weirdly?
I mean that whenever I reply to someone, it shows
Youre not allowed to view the requested resource.
Take a look at this thread:
Like this you mean? I’ve been seeing it too every time I post… logging out / clearing cache didn’t help.
We’re investigating the issue now.
+1 I have the some problem
@ross.grams the thread exactly elaborates upon what I need. But still this is the first time I am delving in render scripts, render targets, and therefore, I don’t know what they are… And how to use them( I was evading this part for long, but bad luck )
So, would you be kind to setup a small tutorial for me on how can I do this.
(Sorry if I have asked for something which I shouldn’t have)
BTW @britzl making changes to your orthographic camera render script won’t disturb the core functionality, isn’t it?
I think I’ve only encountered the issue when using Firefox on a work computer. Posting this from Chrome so I should see…
Edit: Dang, happens in Chrome too. Disregard this.
No, it won’t disturb anything. You can quite easily integrate the camera with any render script.
Hello again @britzl and @ross.grams
This is what I did today with your ideas
- Created a new material, namely lights.
- Assigned a new predicate to the material
- Assigned the lights material to the light sprite.
- Edited the render script to house the new predicate.
But I am still stuck with somethings.
- Do I need to create a new shader for my lights sprite?
- How do I do this?
- How do I color everything else black.?
Plz guide me with this.
Thanks in advance
BTW I watched the render scripts video and it was so informative that I can’t put it in a line
No, you don’t necessarily need a new shader for your lights. Just set the blend mode on the sprites to “Add” mode.
To draw things to a render target, first you need to create one with render.render_target()
(you can copy-paste most of the parameters from the API ref or somewhere). Then, in update(), use render.enable_render_target()
to enable it, and then render.clear()
to clear it. After that everything you draw will be drawn to that render target, so just use render.draw()
as usual. After that, call render.disable_render_target()
to disable it.
That just draws stuff to the render target texture, instead of drawing things directly to the screen. So you won’t see anything yet. The next step is to add a simple quad (flat square plane) model to your scene with a material on it that mixes together both of your render target textures. You’ll want a separate predicate just for this quad so you can draw it separately. Also set the view and projection matrices so this quad fills the whole screen. In your render script you’ll need to render.enable_texture()
both textures, then draw the quad, then disable both textures. For the material you need to give it a texture sampler for each render target. Then a custom fragment shader that combines them. The most basic, easy way to do it is just multiply them together. That way if the lighting texture is zero (black) you’ll just get black, and if it’s 1 (white) you’ll see the base render clearly.
To make everything black by default, just use a (0, 0, 0, 0) color to clear the lighting render target with. You can change this color to have some “ambient” lighting.
Ages ago I put up a sample project. It’s a bit old and a little bit more complicated, but maybe it will help: Link Here
Yeah!! I cracked the code, and finally:::
Here is the Sunday’s surprise .
I am just too much happy with the stuff I have created,
But still configuring it to work makes everything weird. The whole positioning, scaling, and stuff is messed up, and the orthographic camera script just refuses to work. How can I rectify it???
More errors incoming. The base tilemaps show weird scale, and now cover half of the screen rather than the usual full screen. Factory creates objects at random positions, and so on. This thing is kinda confusing, especially when using the render scripts for the first time.
Plz help me clear my doubts .
Post render script as is, probably something off!
If everything is showing at half size or offset by half the screen then probably your projection matrix doesn’t match up with the size and position of the quad you are rendering with.
Here’s the entire render script.
local camera = require "orthographic.camera"
-- Check if 'shared_state' setting is on
-- From https://github.com/rgrams/rendercam/blob/master/rendercam/rendercam.lua#L4-L7
if sys.get_config("script.shared_state") ~= "1" then
error("ERROR - camera - 'shared_state' setting in game.project must be enabled for camera to work.")
end
local high_dpi = sys.get_config("display.high_dpi", "0") == "1"
local WIDTH, HEIGHT = camera.get_window_size()
local CLEAR_COLOR = hash("clear_color")
local SET_VIEW_PROJECTION = hash("set_view_projection")
local SET_CAMERA_OFFSET = hash("set_camera_offset")
local IDENTITY_MATRIX = vmath.matrix4()
local DISPLAY_WIDTH = tonumber(sys.get_config("display.width"))
local DISPLAY_HEIGHT = tonumber(sys.get_config("display.height"))
local function create_render_targets(self)
local color_params = {
format = render.FORMAT_RGBA,
width = WIDTH,
height = (HEIGHT/1.3),
min_filter = render.FILTER_LINEAR,
mag_filter = render.FILTER_LINEAR,
u_wrap = render.WRAP_CLAMP_TO_EDGE,
v_wrap = render.WRAP_CLAMP_TO_EDGE
}
local depth_params = {
format = render.FORMAT_DEPTH,
width = WIDTH*2,
height = (HEIGHT/1.3),
u_wrap = render.WRAP_CLAMP_TO_EDGE,
v_wrap = render.WRAP_CLAMP_TO_EDGE
}
self.base_target = render.render_target("base_target", {[render.BUFFER_COLOR_BIT] = color_params, [render.BUFFER_DEPTH_BIT] = depth_params })
self.lights_target = render.render_target("lights_target", {[render.BUFFER_COLOR_BIT] = color_params, [render.BUFFER_DEPTH_BIT] = depth_params })
end
function init(self)
self.tile_pred = render.predicate({"tile"})
self.gui_pred = render.predicate({"gui"})
self.text_pred = render.predicate({"text"})
self.particle_pred = render.predicate({"particle"})
self.lights_pred = render.predicate({"lights"})
self.render_target_pred = render.predicate({"render target"})
self.clear_pred = render.predicate({"clear"})
self.ambient_light = vmath.vector4(0, 0, 0, 1)
self.clear_color = vmath.vector4(1, 1, 1, 1)
self.clear_color.x = sys.get_config("render.clear_color_red", 0)
self.clear_color.y = sys.get_config("render.clear_color_green", 0)
self.clear_color.z = sys.get_config("render.clear_color_blue", 0)
self.clear_color.w = sys.get_config("render.clear_color_alpha", 0)
self.world_view = vmath.matrix4()
self.world_projection = vmath.matrix4()
self.screen_view = vmath.matrix4()
self.window_width = nil
self.window_height = nil
create_render_targets(self)
end
function update(self)
local window_width = render.get_window_width()
local window_height = render.get_window_height()
if self.window_width ~= window_width or self.window_height ~= window_height then
self.window_width = window_width
self.window_height = window_height
-- update window width/height for camera (used by the projections)
if high_dpi then
camera.set_window_size(window_width / 2, window_height /2)
else
camera.set_window_size(window_width, window_height)
end
end
-- clear color
render.set_depth_mask(true)
render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})
-- setup the viewport
render.set_viewport(0, 0, window_width, window_height)
-- draw world space using projection received from the camera in on_message
render.set_view(self.world_view)
render.set_projection(self.world_projection)
render.set_depth_mask(false)
render.disable_state(render.STATE_DEPTH_TEST)
render.disable_state(render.STATE_STENCIL_TEST)
render.enable_state(render.STATE_BLEND)
render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
render.disable_state(render.STATE_CULL_FACE)
render.enable_render_target(self.base_target)
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_debug3d()
render.disable_render_target(self.base_target)
--LIGHTING STUFF
render.enable_render_target(self.lights_target)
-- Clear with ambient light color
render.clear({[render.BUFFER_COLOR_BIT] = self.ambient_light, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})
render.draw(self.lights_pred)
render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
render.disable_render_target(self.lights_target)
--PROJECTION
render.set_view(IDENTITY_MATRIX) -- set view and projection so quad fills the viewport
render.set_projection(IDENTITY_MATRIX)
render.enable_texture(0, self.lights_target, render.BUFFER_COLOR_BIT)
render.enable_texture(1, self.base_target, render.BUFFER_COLOR_BIT)
render.draw(self.render_target_pred)
render.disable_texture(0, self.lights_target)
render.disable_texture(1, self.base_target)
render.set_viewport(0, 0, window_width, window_height)
-- draw world space using projection received from the camera in on_message
render.set_view(self.world_view)
render.set_projection(self.world_projection)
-- draw gui in screen space using an orthographic projection
render.set_view(self.screen_view)
if self.camera_offset then
local m4 = vmath.matrix4_orthographic(
self.camera_offset.x, window_width + self.camera_offset.x,
self.camera_offset.y, window_height + self.camera_offset.y,
-1, 1)
render.set_projection(m4)
else
local m4 = vmath.matrix4_orthographic(0, window_width, 0, window_height, -1, 1 )
render.set_projection(m4)
end
render.enable_state(render.STATE_STENCIL_TEST)
render.draw(self.gui_pred)
render.draw(self.text_pred)
render.draw(self.clear_pred)
render.disable_state(render.STATE_STENCIL_TEST)
render.set_depth_mask(false)
render.draw_debug2d()
end
function on_message(self, message_id, message)
if message_id == CLEAR_COLOR then
self.clear_color = message.color
elseif message_id == SET_VIEW_PROJECTION then
self.world_view = message.view
self.world_projection = message.projection
elseif message_id == SET_CAMERA_OFFSET then
self.camera_offset = message.offset
end
end
this is basically just @britzl 's orhographic cam render script, in which I added the render lighting part. I just stretched the height part to fit the entire thing on the whole screen.
Yes, I think that is the case, but how can I properly set the matrix. Plz help…