Here I posted a video of 2d lighting effect. I was using a simple ray casting algorithm. But it gives incorrect result depending on amount of rays.
So, I thought that is would be a good idea to use GPU
So I created a simple shader that can calculate the shadow mask for a given object (using ray marching algorithm).
Pros:
- Pretty accurate result
- Good performance (? not sure)
Cons:
- It is ONLY visual effect
- 2 render targets (1 for final light mask, another for a shadow).
- This version works only with AABB's
Code is here:
https://github.com/MultiPain/Gideros_examples/tree/master/2d_lightSmooth version:
http://forum.giderosmobile.com/discussion/comment/62655/#Comment_62655The algorithm is very simple:
Code:
function Light:update()
local x,y = self:getPosition()
-- self.world - Bump world
local list, len = self.world:queryRect(x,y,self.r*2,self.r*2)
if len > 0 then
LightShader:setConstant("fResolution", Shader.CFLOAT2, 1, {self.r*2,self.r*2})
-- draw light mask
self.canvas:clear(0,0)
self.canvas:draw(self.lightSourceImg)
for i,rect in ipairs(list) do
local bx,by = rect:getPosition()
local bw,bh = rect:getSize()
local lx,ly = self:globalToLocal(bx,by)
-- let shader calculate shadow
LightShader:setConstant("rectPos", Shader.CFLOAT2, 1, {lx,ly})
LightShader:setConstant("rectSize", Shader.CFLOAT2, 1, {bw,bh})
-- draw this shadow to light mask
self.canvas:draw(self.shadow)
end
end
end |
In order to check if object is visible by light you can simply use a ray casting method
See this video from 00:18 to 01:02. Its actualy not that hard.
Ray vs AABB function:
--rx1,ry1,rx2,ry2 ray start position and end position
-- bx,by - position of top left corner
-- bw,bh - size
-- return intersection point or nil if there is no intersection
local function rayVSaabb(rx1,ry1,rx2,ry2, bx,by,bw,bh)
local nx = rx2-rx1
local ny = ry2-ry1
local rix = 1 / nx
local riy = 1 / ny
local tx1 = (bx - rx1) * rix
local tx2 = (bx + bw - rx1) * rix
local tmin = mmin(tx1, tx2)
local tmax = mmax(tx1, tx2)
local ty1 = (by - ry1) * riy
local ty2 = (by + bh - ry1) * riy
tmin = mmax(tmin, mmin(ty1, ty2))
tmax = mmin(tmax, mmax(ty1, ty2))
if (tmin > tmax) then return nil end
if (tmin < 0) then
local px = rx1 + nx * tmax
local py = ry1 + ny * tmax
return px, py
end
local px = rx1 + nx * tmin
local py = ry1 + ny * tmin
return px, py
end |
Comments
Fragmenter - animated loop machine and IKONOMIKON - the memory game
Fragmenter - animated loop machine and IKONOMIKON - the memory game
Code is here: https://github.com/MultiPain/Gideros_examples/tree/master/2D_SmoothLight
Video:
Likes: keszegh, hgy29, MoKaLux, SinisterSoft
https://deluxepixel.com
Fragmenter - animated loop machine and IKONOMIKON - the memory game
btw if i go near an object then it looks strange, see attached image.
Fragmenter - animated loop machine and IKONOMIKON - the memory game
There is a problems btw Shader works correctly only with POT (Power Of Two) radius. Maybe @hgy29 can tell me why?))
The radius defines the size of your RT which is used as a source texture for a Bitmap. Gideros internally makes sure that all textures are POT (because some devices still don't handle NPOT textures correctly, even now!), but the shader is unaware of that and expects the texcoords to go from 0 to 1 on each axis while they no longer do!
The solution is to correct the texcoords in the vertex shader. Gideros can supply the actual texture extent in a uniform, so:
a) In the uniforms definiton in lua, add:
Likes: rrraptor, SinisterSoft
Using the same approach but not only AABB, i can handle any type of object, even polygons, and rotating polygons, all coordinates tooks from level file made in Tiled
But Im using for each shadow a new Sprite, with edge smoothing using shader.
Likes: SinisterSoft