From 691da848de798a791a283cffdc059fee91d05c51 Mon Sep 17 00:00:00 2001 From: Tim Anema Date: Sat, 3 Jan 2015 22:18:48 -0500 Subject: [PATCH] added image stencils for objects so shadows are properly stenciled, and made it so that object that dont cast shadows have shadows cast over them. --- examples/complex.lua | 147 ------------------------------------ lib/body.lua | 8 +- lib/init.lua | 23 ++++-- lib/shaders/image_mask.glsl | 6 ++ lib/shaders/shadow.glsl | 42 ++++------- 5 files changed, 44 insertions(+), 182 deletions(-) create mode 100644 lib/shaders/image_mask.glsl diff --git a/examples/complex.lua b/examples/complex.lua index d1479c1..f41a56c 100644 --- a/examples/complex.lua +++ b/examples/complex.lua @@ -27,9 +27,6 @@ function initScene() phyCnt = 0 phyLight = {} - phyBody = {} - phyShape = {} - phyFixture = {} end function love.load() @@ -141,34 +138,6 @@ function love.update(dt) lightDirection = lightDirection + dt colorAberration = math.max(0.0, colorAberration - dt * 10.0) - if love.keyboard.isDown("w") then - for i = 1, phyCnt do - if phyBody[i] then - phyBody[i]:applyForce(0, -2000) - end - end - elseif love.keyboard.isDown("s") then - for i = 1, phyCnt do - if phyBody[i] then - phyBody[i]:applyForce(0, 2000) - end - end - end - - if love.keyboard.isDown("a") then - for i = 1, phyCnt do - if phyBody[i] then - phyBody[i]:applyForce(-2000, 0) - end - end - elseif love.keyboard.isDown("d") then - for i = 1, phyCnt do - if phyBody[i] then - phyBody[i]:applyForce(2000, 0) - end - end - end - if love.keyboard.isDown("up") then offsetY = offsetY + dt * 200 elseif love.keyboard.isDown("down") then @@ -194,29 +163,6 @@ function love.update(dt) tileX = tileX + dt * 32.0 tileY = tileY + dt * 8.0 - for i = 1, phyCnt do - if phyBody[i] and (phyBody[i]:isAwake() or offsetChanged) then - if phyLight[i]:getType() == "polygon" then - phyLight[i]:setPoints(phyBody[i]:getWorldPoints(phyShape[i]:getPoints())) - elseif phyLight[i]:getType() == "circle" then - phyLight[i]:setPosition(phyBody[i]:getPosition()) - elseif phyLight[i]:getType() == "image" then - phyLight[i]:setPosition(phyBody[i]:getPosition()) - elseif phyLight[i]:getType() == "refraction" then - --phyLight[i]:setPosition(phyBody[i]:getPosition()) - end - end - if phyLight[i]:getType() == "refraction" then - --if math.mod(i, 2) == 0 then - phyLight[i]:setNormalTileOffset(tileX, tileY) - --end - end - end - - if physicOn then - physicWorld:update(dt) - end - -- draw shader if colorAberration > 0.0 then -- vert / horz blur @@ -305,27 +251,6 @@ function love.draw() love.graphics.rectangle("fill", love.graphics.getWidth() - 244, love.graphics.getHeight() - 84, 244, 84) love.graphics.setColor(0, 255, 0) love.graphics.print("F1: Help (on)", 4 + 152 * 0, 4) - if physicOn then - love.graphics.setColor(0, 255, 0) - love.graphics.print("F2: Physic (on)", 4 + 152 * 1, 4) - else - love.graphics.setColor(255, 0, 0) - love.graphics.print("F2: Physic (off)", 4 + 152 * 1, 4) - end - if lightOn then - love.graphics.setColor(0, 255, 0) - love.graphics.print("F3: Light (on)", 4 + 152 * 2, 4) - else - love.graphics.setColor(255, 0, 0) - love.graphics.print("F3: Light (off)", 4 + 152 * 2, 4) - end - if gravityOn == 1.0 then - love.graphics.setColor(0, 255, 0) - love.graphics.print("F4: Gravity (on)", 4 + 152 * 3, 4) - else - love.graphics.setColor(255, 0, 0) - love.graphics.print("F4: Gravity (off)", 4 + 152 * 3, 4) - end if shadowBlur >= 1.0 then love.graphics.setColor(0, 255, 0) love.graphics.print("F5: Shadowblur (" .. shadowBlur .. ")", 4 + 152 * 4, 4) @@ -413,20 +338,12 @@ function love.mousepressed(x, y, c) x+w, y+h, x, y+h ) - phyBody[phyCnt] = love.physics.newBody(physicWorld, x, y, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, w, h) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif c == "r" then -- add circle math.randomseed(love.timer.getTime()) cRadius = math.random(8, 32) phyCnt = phyCnt + 1 phyLight[phyCnt] = lightWorld:newCircle(x, y, cRadius) - phyBody[phyCnt] = love.physics.newBody(physicWorld, x, y, "dynamic") - phyShape[phyCnt] = love.physics.newCircleShape(0, 0, cRadius) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif c == "wu" then if lightSmooth < 4.0 then lightSmooth = lightSmooth * 1.1 @@ -444,13 +361,6 @@ function love.keypressed(k, u) -- debug options if k == "f1" then helpOn = not helpOn - elseif k == "f2" then - physicOn = not physicOn - elseif k == "f3" then - lightOn = not lightOn - elseif k == "f4" then - gravityOn = 1 - gravityOn - physicWorld:setGravity(0, gravityOn * 9.81 * 64) elseif k == "f5" then shadowBlur = math.max(1, shadowBlur * 2.0) if shadowBlur > 8.0 then @@ -518,10 +428,6 @@ function love.keypressed(k, u) phyLight[phyCnt] = lightWorld:newImage(circle, mx, my) phyLight[phyCnt]:setNormalMap(circle_normal) phyLight[phyCnt]:setShadowType("circle", 16) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 32, 32) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif k == "2" then local r = lightWorld:getBodyCount() % 2 if r == 0 then @@ -530,19 +436,11 @@ function love.keypressed(k, u) phyLight[phyCnt] = lightWorld:newImage(cone, mx, my, 24, 12, 12, 16) phyLight[phyCnt]:setNormalMap(cone_normal) phyLight[phyCnt]:setShadowType("circle", 12) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 24, 32) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif r == 1 then -- add image phyCnt = phyCnt + 1 phyLight[phyCnt] = lightWorld:newImage(chest, mx, my, 32, 24, 16, 0) phyLight[phyCnt]:setNormalMap(chest_normal) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 32, 24) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) end elseif k == "3" then -- add image @@ -554,10 +452,6 @@ function love.keypressed(k, u) phyLight[phyCnt]:setGlowMap(ape_glow) end phyLight[phyCnt]:setMaterial(material[r + 1]) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 32, 24) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) phyLight[phyCnt]:setShadowType("image", 0, -16, 0.0) elseif k == "4" then -- add glow image @@ -567,46 +461,26 @@ function love.keypressed(k, u) phyLight[phyCnt] = lightWorld:newImage(machine, mx, my, 32, 24, 16, 0) phyLight[phyCnt]:setNormalMap(machine_normal) phyLight[phyCnt]:setGlowMap(machine_glow) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 32, 24) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif r == 1 then phyCnt = phyCnt + 1 phyLight[phyCnt] = lightWorld:newImage(machine2, mx, my, 24, 12, 12, -4) phyLight[phyCnt]:setNormalMap(machine2_normal) phyLight[phyCnt]:setGlowMap(machine2_glow) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 24, 32) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif r == 2 then phyCnt = phyCnt + 1 phyLight[phyCnt] = lightWorld:newImage(led, mx, my, 32, 6, 16, -8) phyLight[phyCnt]:setNormalMap(led_normal) phyLight[phyCnt]:setGlowMap(led_glow) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 32, 6) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif r == 3 then phyCnt = phyCnt + 1 phyLight[phyCnt] = lightWorld:newImage(led2, mx, my, 32, 6, 16, -8) phyLight[phyCnt]:setNormalMap(led_normal) phyLight[phyCnt]:setGlowMap(led_glow2) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 32, 6) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif r == 4 then phyCnt = phyCnt + 1 phyLight[phyCnt] = lightWorld:newImage(led3, mx, my, 32, 6, 16, -8) phyLight[phyCnt]:setNormalMap(led_normal) phyLight[phyCnt]:setGlowMap(led_glow3) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 32, 6) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) end elseif k == "5" then -- add image @@ -614,20 +488,12 @@ function love.keypressed(k, u) phyLight[phyCnt] = lightWorld:newImage(cone_large, mx, my, 24, 128, 12, 64) phyLight[phyCnt]:setNormalMap(cone_large_normal) phyLight[phyCnt]:setShadowType("image", 0, -6, 0.0) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 24, 32) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif k == "6" then -- add image phyCnt = phyCnt + 1 phyLight[phyCnt] = lightWorld:newImage(blopp, mx, my, 42, 16, 21, 0) phyLight[phyCnt]:generateNormalMapGradient("gradient", "gradient") phyLight[phyCnt]:setAlpha(255 * 0.5) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 42, 29) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif k == "7" then -- add image phyCnt = phyCnt + 1 @@ -636,10 +502,6 @@ function love.keypressed(k, u) phyLight[phyCnt]:setGlowMap(tile_glow) phyLight[phyCnt]:setShadow(false) phyLight[phyCnt].reflective = false - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 64, 64) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif k == "8" then -- add rectangle phyCnt = phyCnt + 1 @@ -652,11 +514,6 @@ function love.keypressed(k, u) ) phyLight[phyCnt]:setAlpha(255 * 0.5) phyLight[phyCnt]:setGlowStrength(1.0) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - math.randomseed(love.timer.getTime()) - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, w, h) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) math.randomseed(phyCnt) phyLight[phyCnt]:setGlowColor(math.random(0, 255), math.random(0, 255), math.random(0, 255)) math.randomseed(phyCnt) @@ -673,10 +530,6 @@ function love.keypressed(k, u) phyLight[phyCnt]:setGlowColor(math.random(0, 255), math.random(0, 255), math.random(0, 255)) math.randomseed(phyCnt) phyLight[phyCnt]:setColor(math.random(0, 255), math.random(0, 255), math.random(0, 255)) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newCircleShape(0, 0, cRadius) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) elseif k == "0" then phyCnt = phyCnt + 1 phyLight[phyCnt] = lightWorld:newRefraction(refraction_normal, mx, my) diff --git a/lib/body.lua b/lib/body.lua index 206cb18..c705f71 100644 --- a/lib/body.lua +++ b/lib/body.lua @@ -615,11 +615,17 @@ function body:drawMaterial() end end +function body:drawStencil() + if not self.refraction and not self.reflection and not self.castsNoShadow then + love.graphics.draw(self.img, self.x - self.ix, self.y - self.iy) + end +end + function body:drawShadow(light) if self.castsNoShadow or (self.zheight - light.z) > 0 then return end - + love.graphics.setColor(self.red, self.green, self.blue, self.alpha) if self.shadowType == "rectangle" or self.shadowType == "polygon" then self:drawPolyShadow(light) diff --git a/lib/init.lua b/lib/init.lua index 7325732..c9a5610 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -31,9 +31,10 @@ local PostShader = require(_PACKAGE..'postshader') local light_world = {} light_world.__index = light_world -light_world.shadowShader = love.graphics.newShader(_PACKAGE.."/shaders/shadow.glsl") -light_world.refractionShader = love.graphics.newShader(_PACKAGE.."shaders/refraction.glsl") -light_world.reflectionShader = love.graphics.newShader(_PACKAGE.."shaders/reflection.glsl") +light_world.image_mask = love.graphics.newShader(_PACKAGE.."/shaders/image_mask.glsl") +light_world.shadowShader = love.graphics.newShader(_PACKAGE.."/shaders/shadow.glsl") +light_world.refractionShader = love.graphics.newShader(_PACKAGE.."shaders/refraction.glsl") +light_world.reflectionShader = love.graphics.newShader(_PACKAGE.."shaders/reflection.glsl") local function new(options) local obj = {} @@ -122,17 +123,27 @@ function light_world:drawShadows(l,t,w,h,s) self.shadow_buffer:clear() for i = 1, #self.lights do local light = self.lights[i] - if self.lights[i]:isVisible() then + if light:isVisible() then -- create shadow map for this light self.shadowMap:clear() util.drawto(self.shadowMap, l, t, s, function() + --I dont know if it uses both or just calls both love.graphics.setStencil(function() local angle = light.direction - (light.angle / 2.0) love.graphics.arc("fill", light.x, light.y, light.range, angle, angle + light.angle) end) + love.graphics.setInvertedStencil(function() + love.graphics.setShader(self.image_mask) + for k = 1, #self.bodies do + if self.bodies[k]:inLightRange(light) and self.bodies[k]:isVisible() then + self.bodies[k]:drawStencil() + end + end + love.graphics.setShader() + end) for k = 1, #self.bodies do - if self.bodies[k]:inLightRange(self.lights[i]) and self.bodies[k]:isVisible() then - self.bodies[k]:drawShadow(self.lights[i]) + if self.bodies[k]:inLightRange(light) and self.bodies[k]:isVisible() then + self.bodies[k]:drawShadow(light) end end end) diff --git a/lib/shaders/image_mask.glsl b/lib/shaders/image_mask.glsl new file mode 100644 index 0000000..196f00d --- /dev/null +++ b/lib/shaders/image_mask.glsl @@ -0,0 +1,6 @@ +//https://love2d.org/wiki/love.graphics.setStencil image mask +vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) { + if (Texel(texture, texture_coords).rgb == vec3(0.0)) + discard; + return vec4(1.0); +} diff --git a/lib/shaders/shadow.glsl b/lib/shaders/shadow.glsl index a4351f0..5bae828 100644 --- a/lib/shaders/shadow.glsl +++ b/lib/shaders/shadow.glsl @@ -13,52 +13,38 @@ extern vec2 lightGlow; //how brightly the light bulb part glows extern bool invert_normal; //if the light should invert normals vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) { - vec4 normalColor = Texel(normalMap, texture_coords); - float dist = distance(lightPosition, vec3(pixel_coords, 1.0)); - //if the pixel is within this lights range - if(dist > lightRange) { - //not in range draw in shadows + if(dist > lightRange) { //not in range draw in shadows return vec4(0.0, 0.0, 0.0, 1.0); }else{ - vec3 normal; - if(normalColor.a > 0.0) { - //if on the normal map ie there is normal map data - //so get the normal data - if(invert_normal) { - normal = normalize(vec3(normalColor.r, 1 - normalColor.g, normalColor.b) * 2.0 - 1.0); - } else { - normal = normalize(normalColor.rgb * 2.0 - 1.0); - } - } else { - // not on the normal map so it is the floor with a normal point strait up - normal = vec3(0.0, 0.0, 1.0); - } - //calculater attenuation of light based on the distance + vec4 shadowColor = Texel(texture, texture_coords); + vec4 normalColor = Texel(normalMap, texture_coords); + vec4 pixel; + //calculate attenuation of light based on the distance float att = clamp((1.0 - dist / lightRange) / lightSmooth, 0.0, 1.0); // if not on the normal map draw attenuated shadows - if(normalColor.a == 0.0) { + if(normalColor.a <= 0.0) { //start with a dark color and add in the light color and shadow color - vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0); + pixel = vec4(0.0, 0.0, 0.0, 1.0); if (lightGlow.x < 1.0 && lightGlow.y > 0.0) { pixel.rgb = clamp(lightColor * pow(att, lightSmooth) + pow(smoothstep(lightGlow.x, 1.0, att), lightSmooth) * lightGlow.y, 0.0, 1.0); } else { pixel.rgb = lightColor * pow(att, lightSmooth); } - //If on the shadow map add the shadow color - vec4 shadowColor = Texel(texture, texture_coords); - if(shadowColor.a > 0.0) { - pixel.rgb = pixel.rgb * shadowColor.rgb; - } - return pixel; } else { + vec3 normal = normalize(vec3(normalColor.r,invert_normal ? 1 - normalColor.g : normalColor.g, normalColor.b) * 2.0 - 1.0); //on the normal map, draw normal shadows vec3 dir = vec3((lightPosition.xy - pixel_coords.xy) / love_ScreenSize.xy, lightPosition.z); dir.x *= love_ScreenSize.x / love_ScreenSize.y; vec3 diff = lightColor * max(dot(normalize(normal), normalize(dir)), 0.0); //return the light that is effected by the normal and attenuation - return vec4(diff * att, 1.0); + pixel = vec4(diff * att, 1.0); } + + if(shadowColor.a > 0.0) { + pixel.rgb = pixel.rgb * shadowColor.rgb; + } + return pixel; } }