made better shadow body calculations so that the z coordinate of the light effects the cast light

This commit is contained in:
Tim Anema 2014-11-28 10:20:00 -05:00
parent 64d8613549
commit 7db34a4a02
10 changed files with 166 additions and 166 deletions

View File

@ -134,7 +134,7 @@ function love.update(dt)
mx, my = (love.mouse.getX() - offsetX)/scale, (love.mouse.getY() - offsetY)/scale
mouseLight:setPosition(mx, my, 16.0 + (math.sin(lightDirection) + 1.0) * 64.0)
mouseLight:setPosition(mx, my, 1 + (math.sin(lightDirection) + 1.0) * 64.0)
lightDirection = lightDirection + dt
colorAberration = math.max(0.0, colorAberration - dt * 10.0)

View File

@ -5,6 +5,7 @@ function love.load()
testShader = 0
x = 0
y = 0
z = 1
scale = 1
colorAberration = 0.0
-- load images
@ -116,7 +117,15 @@ function love.update(dt)
lightWorld.post_shader:removeEffect("chromatic_aberration")
end
lightMouse:setPosition((love.mouse.getX() - x)/scale, (love.mouse.getY() - y)/scale)
lightMouse:setPosition((love.mouse.getX() - x)/scale, (love.mouse.getY() - y)/scale, z)
end
function love.mousepressed(x, y, c)
if c == "wu" then
z = z + 1
elseif c == "wd" then
z = z - 1
end
end
function love.draw()
@ -131,6 +140,7 @@ function love.draw()
love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), 24)
love.graphics.setColor(0, 255, 0)
love.graphics.print("To toggle postshaders, use 0-9 and q->y, to scale use - and =, and to translate use arrows")
love.graphics.print("light z: " .. lightMouse.z, 0, 50)
end
function drawBackground(l,t,w,h)

View File

@ -1,9 +1,8 @@
local _PACKAGE = (...):match("^(.+)[%./][^%./]+") or ""
local class = require(_PACKAGE.."/class")
local normal_map = require(_PACKAGE..'/normal_map')
local vector = require(_PACKAGE..'/vector')
local shadowLength = 100000
local vec2 = require(_PACKAGE..'/vec2')
local vec3 = require(_PACKAGE..'/vec3')
local body = class()
body.glowShader = love.graphics.newShader(_PACKAGE.."/shaders/glow.glsl")
@ -24,6 +23,7 @@ function body:init(id, type, ...)
self.glowStrength = 0.0
self.tileX = 0
self.tileY = 0
self.zheight = 1
if self.type == "circle" then
self.x = args[1] or 0
@ -88,14 +88,10 @@ end
function body:refresh()
if self.x and self.y and self.width and self.height and self.ox and self.oy then
self.data = {
self.x - self.ox,
self.y - self.oy,
self.x - self.ox + self.width,
self.y - self.oy,
self.x - self.ox + self.width,
self.y - self.oy + self.height,
self.x - self.ox,
self.y - self.oy + self.height
self.x - self.ox, self.y - self.oy,
self.x - self.ox + self.width, self.y - self.oy,
self.x - self.ox + self.width, self.y - self.oy + self.height,
self.x - self.ox, self.y - self.oy + self.height
}
end
end
@ -452,7 +448,7 @@ function body:drawShadow(light)
end
function body:drawPixelShadow()
if self.normalMesh then
if self.type == "image" and self.normalMesh then
love.graphics.setColor(255, 255, 255)
love.graphics.draw(self.normalMesh, self.x - self.nx, self.y - self.ny)
end
@ -546,56 +542,57 @@ function body:calculateShadow(light)
end
end
--using shadow point calculations from this article
--http://web.cs.wpi.edu/~matt/courses/cs563/talks/shadow/shadow.html
function body:calculatePolyShadow(light)
if self.castsNoShadow then
if self.castsNoShadow or (self.zheight - light.z) > 0 then
return nil
end
local curPolygon = self.data
local edgeFacingTo = {}
for k = 1, #curPolygon, 2 do
local indexOfNextVertex = (k + 2) % #curPolygon
local normal = {-curPolygon[indexOfNextVertex+1] + curPolygon[k + 1], curPolygon[indexOfNextVertex] - curPolygon[k]}
local lightToPoint = {curPolygon[k] - light.x, curPolygon[k + 1] - light.y}
for k = 1, #self.data, 2 do
local indexOfNextVertex = (k + 2) % #self.data
local normal = vec2(-self.data[indexOfNextVertex+1] + self.data[k + 1], self.data[indexOfNextVertex] - self.data[k]):normalize()
local lightToPoint = vec2(self.data[k] - light.x, self.data[k + 1] - light.y):normalize()
normal = vector.normalize(normal)
lightToPoint = vector.normalize(lightToPoint)
local dotProduct = vector.dot(normal, lightToPoint)
if dotProduct > 0 then table.insert(edgeFacingTo, true)
else table.insert(edgeFacingTo, false) end
local dotProduct = normal:dot(lightToPoint)
if dotProduct > 0 then
table.insert(edgeFacingTo, true)
else
table.insert(edgeFacingTo, false)
end
end
local curShadowGeometry = {}
local lxh = (light.x * self.zheight)
local lyh = (light.y * self.zheight)
local height_diff = (self.zheight - light.z)
if height_diff == 0 then -- prevent inf
height_diff = -0.001
end
for k = 1, #edgeFacingTo do
local nextIndex = (k + 1) % #edgeFacingTo
if nextIndex == 0 then nextIndex = #edgeFacingTo end
local x, y = self.data[nextIndex*2-1], self.data[nextIndex*2]
local xs, ys = (lxh - (x * light.z))/height_diff, (lyh - (y * light.z))/height_diff
if edgeFacingTo[k] and not edgeFacingTo[nextIndex] then
curShadowGeometry[1] = curPolygon[nextIndex*2-1]
curShadowGeometry[2] = curPolygon[nextIndex*2]
local lightVecFrontBack = vector.normalize({curPolygon[nextIndex*2-1] - light.x, curPolygon[nextIndex*2] - light.y})
curShadowGeometry[3] = curShadowGeometry[1] + lightVecFrontBack[1] * shadowLength
curShadowGeometry[4] = curShadowGeometry[2] + lightVecFrontBack[2] * shadowLength
curShadowGeometry[#curShadowGeometry+1] = x
curShadowGeometry[#curShadowGeometry+1] = y
curShadowGeometry[#curShadowGeometry+1] = xs
curShadowGeometry[#curShadowGeometry+1] = ys
elseif not edgeFacingTo[k] and not edgeFacingTo[nextIndex] then
curShadowGeometry[#curShadowGeometry+1] = xs
curShadowGeometry[#curShadowGeometry+1] = ys
elseif not edgeFacingTo[k] and edgeFacingTo[nextIndex] then
curShadowGeometry[7] = curPolygon[nextIndex*2-1]
curShadowGeometry[8] = curPolygon[nextIndex*2]
local lightVecBackFront = vector.normalize({curPolygon[nextIndex*2-1] - light.x, curPolygon[nextIndex*2] - light.y})
curShadowGeometry[5] = curShadowGeometry[7] + lightVecBackFront[1] * shadowLength
curShadowGeometry[6] = curShadowGeometry[8] + lightVecBackFront[2] * shadowLength
curShadowGeometry[#curShadowGeometry+1] = xs
curShadowGeometry[#curShadowGeometry+1] = ys
curShadowGeometry[#curShadowGeometry+1] = x
curShadowGeometry[#curShadowGeometry+1] = y
end
end
if curShadowGeometry[1]
and curShadowGeometry[2]
and curShadowGeometry[3]
and curShadowGeometry[4]
and curShadowGeometry[5]
and curShadowGeometry[6]
and curShadowGeometry[7]
and curShadowGeometry[8]
then
if #curShadowGeometry >= 6 then
curShadowGeometry.alpha = self.alpha
curShadowGeometry.red = self.red
curShadowGeometry.green = self.green
@ -606,38 +603,47 @@ function body:calculatePolyShadow(light)
end
end
--using shadow point calculations from this article
--http://web.cs.wpi.edu/~matt/courses/cs563/talks/shadow/shadow.html
function body:calculateCircleShadow(light)
if self.castsNoShadow then
if self.castsNoShadow or (self.zheight - light.z) > 0 then
return nil
end
local length = math.sqrt(math.pow(light.x - (self.x - self.ox), 2) + math.pow(light.y - (self.y - self.oy), 2))
if length >= self.radius and length <= light.range then
local curShadowGeometry = {}
local angle = math.atan2(light.x - (self.x - self.ox), (self.y - self.oy) - light.y) + math.pi / 2
local x2 = ((self.x - self.ox) + math.sin(angle) * self.radius)
local y2 = ((self.y - self.oy) - math.cos(angle) * self.radius)
local x3 = ((self.x - self.ox) - math.sin(angle) * self.radius)
local y3 = ((self.y - self.oy) + math.cos(angle) * self.radius)
curShadowGeometry[1] = x2
curShadowGeometry[2] = y2
curShadowGeometry[3] = x3
curShadowGeometry[4] = y3
local curShadowGeometry = {}
local angle = math.atan2(light.x - (self.x - self.ox), (self.y - self.oy) - light.y) + math.pi / 2
local x2 = ((self.x - self.ox) + math.sin(angle) * self.radius)
local y2 = ((self.y - self.oy) - math.cos(angle) * self.radius)
local x3 = ((self.x - self.ox) - math.sin(angle) * self.radius)
local y3 = ((self.y - self.oy) + math.cos(angle) * self.radius)
curShadowGeometry[5] = x3 - (light.x - x3) * shadowLength
curShadowGeometry[6] = y3 - (light.y - y3) * shadowLength
curShadowGeometry[7] = x2 - (light.x - x2) * shadowLength
curShadowGeometry[8] = y2 - (light.y - y2) * shadowLength
curShadowGeometry[1] = x2
curShadowGeometry[2] = y2
curShadowGeometry[3] = x3
curShadowGeometry[4] = y3
curShadowGeometry.red = self.red
curShadowGeometry.green = self.green
curShadowGeometry.blue = self.blue
curShadowGeometry.alpha = self.alpha
return curShadowGeometry
else
return nil
local lxh = (light.x * self.zheight)
local lyh = (light.y * self.zheight)
local height_diff = (self.zheight - light.z)
if height_diff == 0 then -- prevent inf
height_diff = -0.001
end
curShadowGeometry[5] = (lxh - (x3 * light.z))/height_diff
curShadowGeometry[6] = (lyh - (y3 * light.z))/height_diff
curShadowGeometry[7] = (lxh - (x2 * light.z))/height_diff
curShadowGeometry[8] = (lyh - (y2 * light.z))/height_diff
local radius = math.sqrt(math.pow(curShadowGeometry[7] - curShadowGeometry[5], 2) + math.pow(curShadowGeometry[8]-curShadowGeometry[6], 2)) / 2
local cx, cy = (curShadowGeometry[5] + curShadowGeometry[7])/2, (curShadowGeometry[6] + curShadowGeometry[8])/2
curShadowGeometry.circle = {cx, cy, radius}
curShadowGeometry.red = self.red
curShadowGeometry.green = self.green
curShadowGeometry.blue = self.blue
curShadowGeometry.alpha = self.alpha
return curShadowGeometry
end
return body

View File

@ -7,7 +7,6 @@ local light = class()
light.shader = love.graphics.newShader(_PACKAGE.."/shaders/poly_shadow.glsl")
light.normalShader = love.graphics.newShader(_PACKAGE.."/shaders/normal.glsl")
light.normalInvertShader = love.graphics.newShader(_PACKAGE.."/shaders/normal_invert.glsl")
function light:init(x, y, r, g, b, range)
self.direction = 0
@ -15,7 +14,7 @@ function light:init(x, y, r, g, b, range)
self.range = 0
self.x = x or 0
self.y = y or 0
self.z = 15
self.z = 1
self.red = r or 255
self.green = g or 255
self.blue = b or 255
@ -32,7 +31,6 @@ function light:refresh(w, h)
self.shadow = love.graphics.newCanvas(w, h)
self.shine = love.graphics.newCanvas(w, h)
self.normalInvertShader:send('screenResolution', {w, h})
self.normalShader:send('screenResolution', {w, h})
end
@ -199,23 +197,14 @@ end
function light:drawPixelShadow(l,t,w,h,s, normalMap, canvas)
if self.visible and self:inRange(l,t,w,h,s) then
if self.normalInvert then
self.normalInvertShader:send('lightColor', {self.red / 255.0, self.green / 255.0, self.blue / 255.0})
self.normalInvertShader:send("lightPosition", {(self.x + l/s) * s, (h/s - (self.y + t/s)) * s, self.z / 255.0})
self.normalInvertShader:send('lightRange',{self.range})
self.normalInvertShader:send("lightSmooth", self.smooth)
self.normalInvertShader:send("lightAngle", math.pi - self.angle / 2.0)
self.normalInvertShader:send("lightDirection", self.direction)
util.drawCanvasToCanvas(normalMap, canvas, {shader = self.normalInvertShader})
else
self.normalShader:send('lightColor', {self.red / 255.0, self.green / 255.0, self.blue / 255.0})
self.normalShader:send("lightPosition", {(self.x + l/s) * s, (h/s - (self.y + t/s)) * s, self.z / 255.0})
self.normalShader:send('lightRange',{self.range})
self.normalShader:send("lightSmooth", self.smooth)
self.normalShader:send("lightAngle", math.pi - self.angle / 2.0)
self.normalShader:send("lightDirection", self.direction)
util.drawCanvasToCanvas(normalMap, canvas, {shader = self.normalShader})
end
self.normalShader:send('lightColor', {self.red / 255.0, self.green / 255.0, self.blue / 255.0})
self.normalShader:send("lightPosition", {(self.x + l/s) * s, (h/s - (self.y + t/s)) * s, self.z / 255.0})
self.normalShader:send('lightRange',{self.range})
self.normalShader:send("lightSmooth", self.smooth)
self.normalShader:send("lightAngle", math.pi - self.angle / 2.0)
self.normalShader:send("lightDirection", self.direction)
self.normalShader:send("invert_normal", self.normalInvert == true)
util.drawCanvasToCanvas(normalMap, canvas, {shader = self.normalShader})
end
end

View File

@ -7,6 +7,7 @@ extern float lightRange;
extern float lightSmooth;
extern float lightDirection;
extern float lightAngle;
extern bool invert_normal;
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
vec4 pixelColor = Texel(texture, texture_coords);
@ -25,7 +26,12 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
}
}
vec3 normal = pixelColor.rgb;
vec3 normal;
if(invert_normal == true) {
normal = vec3(pixelColor.r, 1 - pixelColor.g, pixelColor.b);
} else {
normal = pixelColor.rgb;
}
float dist = distance(lightPosition, vec3(pixel_coords, normal.b));
if(dist < lightRange) {

View File

@ -1,50 +0,0 @@
#define PI 3.1415926535897932384626433832795
extern vec2 screenResolution;
extern vec3 lightPosition;
extern vec3 lightColor;
extern float lightRange;
extern float lightSmooth;
extern float lightDirection;
extern float lightAngle;
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
vec4 pixelColor = Texel(texture, texture_coords);
if(pixelColor.a > 0.0) {
if(lightAngle > 0.0) {
float angle2 = atan(lightPosition.x - pixel_coords.x, pixel_coords.y - lightPosition.y) + PI;
if(lightDirection - lightAngle > 0 && lightDirection + lightAngle < PI * 2) {
if(angle2 < mod(lightDirection + lightAngle, PI * 2) && angle2 > mod(lightDirection - lightAngle, PI * 2)) {
return vec4(0.0, 0.0, 0.0, 1.0);
}
} else {
if(angle2 < mod(lightDirection + lightAngle, PI * 2) || angle2 > mod(lightDirection - lightAngle, PI * 2)) {
return vec4(0.0, 0.0, 0.0, 1.0);
}
}
}
vec3 normal = vec3(pixelColor.r, 1 - pixelColor.g, pixelColor.b);
float dist = distance(lightPosition, vec3(pixel_coords, normal.b));
if(dist < lightRange) {
vec3 dir = vec3((lightPosition.xy - pixel_coords.xy) / screenResolution.xy, lightPosition.z);
dir.x *= screenResolution.x / screenResolution.y;
vec3 N = normalize(normal * 2.0 - 1.0);
vec3 L = normalize(dir);
vec3 diff = lightColor * max(dot(N, L), 0.0);
float att = clamp((1.0 - dist / lightRange) / lightSmooth, 0.0, 1.0);
return vec4(diff * att, 1.0);
} else {
return vec4(0.0, 0.0, 0.0, 1.0);
}
} else {
return vec4(0.0);
}
}

View File

@ -6,6 +6,9 @@ function stencils.shadow(geometry, bodies)
for i = 1,#geometry do
if geometry[i].alpha == 1.0 then
love.graphics.polygon("fill", unpack(geometry[i]))
if geometry[i].circle then
love.graphics.circle("fill", unpack(geometry[i].circle))
end
end
end
-- underneath shadows

27
lib/vec2.lua Normal file
View File

@ -0,0 +1,27 @@
local _PACKAGE = (...):match("^(.+)[%./][^%./]+") or ""
local class = require(_PACKAGE.."/class")
local vec2 = class()
function vec2:init(x, y)
self.x, self.y = x, y
end
function vec2:normalize()
local len = self:length()
return vec2(self.x / len, self.y / len)
end
function vec2:dot(v2)
return (self.x * v2.x) + (self.y * v2.y)
end
function vec2:cross(v2)
return ((self.x * v2.y) - (self.y * v2.x))
end
function vec2:length()
return math.sqrt(self:dot(self))
end
return vec2

30
lib/vec3.lua Normal file
View File

@ -0,0 +1,30 @@
local _PACKAGE = (...):match("^(.+)[%./][^%./]+") or ""
local class = require(_PACKAGE.."/class")
local vec3 = class()
function vec3:init(x, y, z)
self.x, self.y, self.z = x, y, z
end
function vec3:normalize()
local len = self:length()
return vec3((self.x / len), (self.y / len), (self.z / len))
end
function vec3:dot(v2)
return (self.x * v2.x) + (self.y * v2.y) + (self.z * v2.z)
end
function vec3:cross(v2)
return ((self.y * v2.z) - (self.z * v2.y)),
((self.z * v2.x) - (self.x * v2.z)),
((self.x * v2.y) - (self.y * v2.x))
end
function vec3:length()
return math.sqrt(self:dot(self))
end
return vec3

View File

@ -1,21 +0,0 @@
local vector = {}
-- vector functions
function vector.normalize(v)
local len = math.sqrt(math.pow(v[1], 2) + math.pow(v[2], 2))
local normalizedv = {v[1] / len, v[2] / len}
return normalizedv
end
function vector.dot(v1, v2)
return v1[1] * v2[1] + v1[2] * v2[2]
end
function vector.lengthSqr(v)
return v[1] * v[1] + v[2] * v[2]
end
function vector.length(v)
return math.sqrt(lengthSqr(v))
end
return vector