Hey, hope this helps (most likely not the only way):
1. draw normally
2. to off-sceen canvas: draw mask so we know where the silhouette could should be drawn (on objects, not on player, if you have layers - e.g. floor, you might skip drawing it
3. to another off-screen canvas: draw all silhouettes for the player (and other things)
4. combine the two and draw to screen (or wherever)
Code: Select all
local box = function(self, w, h)
love.graphics.rectangle("fill", self.x - w/2, self.y - h, w, h)
end
local things = {
-- character
{
isSilhouetteable = true,
x = 200,
y = 300,
draw = function(self)
love.graphics.setColor(1, 0, 0)
love.graphics.circle("fill", self.x, self.y - 50, 50)
love.graphics.setColor(1, 1, 1)
end
},
-- obstacles
{ x = 250, y = 250, draw = function(self) box(self, 100 ,100) end },
{ x = 150, y = 380, draw = function(self) box(self, 100 ,100) end },
}
local player = things[1]
local maskC = love.graphics.newCanvas(400, 400)
local silhouetteC = love.graphics.newCanvas(400, 400)
local maskSh = love.graphics.newShader([[
extern bool on = false;
vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords)
{
return on ? vec4(0,0,0,1) : vec4(1,1,1,1);
}
]])
local silhouetteSh = love.graphics.newShader([[
extern vec4 tint = vec4(0, 1, 0, 0.5);
vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords)
{
return tint;
}
]])
local combineSh = love.graphics.newShader([[
extern Image mask;
extern vec4 tint = vec4(0, 1, 0, 0.5);
vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords)
{
vec4 m = Texel(mask, texture_coords);
vec4 s = Texel(tex, texture_coords);
s.a *= m.r;
return s;
}
]])
function love.update()
if love.keyboard.isDown("w", "up") then player.y = player.y - 1 end
if love.keyboard.isDown("s", "down") then player.y = player.y + 1 end
if love.keyboard.isDown("a", "left") then player.x = player.x - 1 end
if love.keyboard.isDown("d", "right") then player.x = player.x + 1 end
end
function love.draw()
love.graphics.clear()
-- sort by Y
table.sort(things, function(a, b) return a.y < b.y end)
-- draw normal
for i, v in ipairs(things) do v:draw() end
-- draw mask (where one can put shiluette)
love.graphics.setCanvas(maskC)
love.graphics.clear(0,0,0,1)
love.graphics.setShader(maskSh)
for i, v in ipairs(things) do
maskSh:send("on", not not v.isSilhouetteable)
v:draw()
end
love.graphics.setColor(1, 1, 1)
-- draw silhouettes
love.graphics.setCanvas(silhouetteC)
love.graphics.setShader(silhouetteSh)
love.graphics.clear(1,1,1,0)
for i, v in ipairs(things) do
if v.isSilhouetteable then
v:draw()
end
end
-- combine both
love.graphics.setCanvas()
love.graphics.setShader(combineSh)
combineSh:send("mask", maskC)
love.graphics.draw(silhouetteC)
-- draw to screen
love.graphics.setShader()
love.graphics.setCanvas()
love.graphics.draw(maskC,500, 0, 0, 0.5)
love.graphics.draw(silhouetteC,500, 300, 0, 0.5)
end
I guess you could be smarter with blendModes or something, but at least it works