Add refraction.

This commit is contained in:
Marcus Ihde 2014-03-16 22:01:14 +01:00
parent 0eb6c5f127
commit c8c7ec418a
5 changed files with 303 additions and 7 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 29 KiB

249
light.lua
View File

@ -23,6 +23,7 @@ function love.light.newWorld()
o.circle = {} o.circle = {}
o.poly = {} o.poly = {}
o.img = {} o.img = {}
o.refraction = {}
o.shadow = love.graphics.newCanvas() o.shadow = love.graphics.newCanvas()
o.shadow2 = love.graphics.newCanvas() o.shadow2 = love.graphics.newCanvas()
o.shine = love.graphics.newCanvas() o.shine = love.graphics.newCanvas()
@ -30,12 +31,16 @@ function love.light.newWorld()
o.normalMap = love.graphics.newCanvas() o.normalMap = love.graphics.newCanvas()
o.glowMap = love.graphics.newCanvas() o.glowMap = love.graphics.newCanvas()
o.glowMap2 = love.graphics.newCanvas() o.glowMap2 = love.graphics.newCanvas()
o.refractionMap = love.graphics.newCanvas()
o.refractionMap2 = love.graphics.newCanvas()
o.glowBlur = 1.0 o.glowBlur = 1.0
o.isGlowBlur = false o.isGlowBlur = false
o.refractionStrength = 8.0
o.pixelShadow = love.graphics.newCanvas() o.pixelShadow = love.graphics.newCanvas()
o.pixelShadow2 = love.graphics.newCanvas() o.pixelShadow2 = love.graphics.newCanvas()
o.shader = love.graphics.newShader("shader/poly_shadow.glsl") o.shader = love.graphics.newShader("shader/poly_shadow.glsl")
o.normalShader = love.graphics.newShader("shader/normal.glsl") o.normalShader = love.graphics.newShader("shader/normal.glsl")
o.refractionShader = love.graphics.newShader("shader/refraction.glsl")
o.changed = true o.changed = true
o.blur = 2.0 o.blur = 2.0
-- update -- update
@ -247,15 +252,31 @@ function love.light.newWorld()
for i = 1, #o.img do for i = 1, #o.img do
if o.img[i].glow then if o.img[i].glow then
love.graphics.setColor(o.img[i].glowRed, o.img[i].glowGreen, o.img[i].glowBlue) love.graphics.setColor(o.img[i].glowRed, o.img[i].glowGreen, o.img[i].glowBlue)
love.graphics.draw(o.img[i].glow, o.img[i].x - o.img[i].ox2 + LOVE_LIGHT_TRANSLATE_X, o.img[i].y - o.img[i].oy2 + LOVE_LIGHT_TRANSLATE_X) love.graphics.draw(o.img[i].glow, o.img[i].x - o.img[i].ox2 + LOVE_LIGHT_TRANSLATE_X, o.img[i].y - o.img[i].oy2 + LOVE_LIGHT_TRANSLATE_Y)
else else
love.graphics.setColor(0, 0, 0) love.graphics.setColor(0, 0, 0)
love.graphics.draw(o.img[i].img, o.img[i].x - o.img[i].ox2 + LOVE_LIGHT_TRANSLATE_X, o.img[i].y - o.img[i].oy2 + LOVE_LIGHT_TRANSLATE_X) love.graphics.draw(o.img[i].img, o.img[i].x - o.img[i].ox2 + LOVE_LIGHT_TRANSLATE_X, o.img[i].y - o.img[i].oy2 + LOVE_LIGHT_TRANSLATE_Y)
end end
end end
o.isGlowBlur = false o.isGlowBlur = false
end end
-- create refraction map
if o.changed then
o.refractionMap:clear()
love.graphics.setCanvas(o.refractionMap)
for i = 1, #o.refraction do
if o.refraction[i].strength > 0.0 and o.refraction[i].normal then
love.graphics.setColor(255, 255, 255)
o.refraction[i].mesh:setVertices(o.refraction[i].vertices)
love.graphics.draw(o.refraction[i].mesh, o.refraction[i].x - o.refraction[i].ox + LOVE_LIGHT_TRANSLATE_X, o.refraction[i].y - o.refraction[i].oy + LOVE_LIGHT_TRANSLATE_Y)
else
love.graphics.setColor(0, 0, 0, 0)
love.graphics.rectangle("fill", o.refraction[i].x - o.refraction[i].ox + LOVE_LIGHT_TRANSLATE_X, o.refraction[i].y - o.refraction[i].oy, o.refraction[i].normalWidth, o.refraction[i].normalHeight + LOVE_LIGHT_TRANSLATE_Y)
end
end
end
love.graphics.setShader() love.graphics.setShader()
love.graphics.setBlendMode("alpha") love.graphics.setBlendMode("alpha")
love.graphics.setStencil() love.graphics.setStencil()
@ -352,6 +373,23 @@ function love.light.newWorld()
o.isGlowBlur = true o.isGlowBlur = true
end end
end end
-- draw refraction
o.drawRefraction = function()
LOVE_LIGHT_LAST_BUFFER = love.graphics.getCanvas()
if LOVE_LIGHT_LAST_BUFFER then
love.graphics.setColor(255, 255, 255)
love.graphics.setBlendMode("alpha")
love.graphics.setCanvas(o.refractionMap2)
love.graphics.draw(LOVE_LIGHT_LAST_BUFFER, LOVE_LIGHT_TRANSLATE_X, LOVE_LIGHT_TRANSLATE_Y)
love.graphics.setCanvas(LOVE_LIGHT_LAST_BUFFER)
o.refractionShader:send("backBuffer", o.refractionMap2)
o.refractionShader:send("refractionStrength", o.refractionStrength)
love.graphics.setShader(o.refractionShader)
love.graphics.draw(o.refractionMap, LOVE_LIGHT_TRANSLATE_X, LOVE_LIGHT_TRANSLATE_Y)
--love.graphics.rectangle("fill", LOVE_LIGHT_TRANSLATE_X, LOVE_LIGHT_TRANSLATE_Y, love.graphics.getWidth(), love.graphics.getHeight())
love.graphics.setShader()
end
end
-- new light -- new light
o.newLight = function(x, y, red, green, blue, range) o.newLight = function(x, y, red, green, blue, range)
o.lights[#o.lights + 1] = love.light.newLight(o, x, y, red, green, blue, range) o.lights[#o.lights + 1] = love.light.newLight(o, x, y, red, green, blue, range)
@ -368,6 +406,7 @@ function love.light.newWorld()
o.poly = {} o.poly = {}
o.circle = {} o.circle = {}
o.img = {} o.img = {}
o.refraction = {}
o.changed = true o.changed = true
end end
-- set offset -- set offset
@ -396,11 +435,20 @@ function love.light.newWorld()
o.blur = blur o.blur = blur
o.changed = true o.changed = true
end end
-- set blur
o.setShadowBlur = function(blur)
o.blur = blur
o.changed = true
end
-- set glow blur -- set glow blur
o.setGlowStrength = function(strength) o.setGlowStrength = function(strength)
o.glowBlur = strength o.glowBlur = strength
o.changed = true o.changed = true
end end
-- set refraction blur
o.setRefractionStrength = function(strength)
o.refractionStrength = strength
end
-- new rectangle -- new rectangle
o.newRectangle = function(x, y, w, h) o.newRectangle = function(x, y, w, h)
return love.light.newRectangle(o, x, y, w, h) return love.light.newRectangle(o, x, y, w, h)
@ -417,6 +465,14 @@ function love.light.newWorld()
o.newImage = function(img, x, y, width, height, ox, oy) o.newImage = function(img, x, y, width, height, ox, oy)
return love.light.newImage(o, img, x, y, width, height, ox, oy) return love.light.newImage(o, img, x, y, width, height, ox, oy)
end end
-- new refraction
o.newRefraction = function(normal, x, y)
return love.light.newRefraction(o, normal, x, y)
end
-- new refraction from height map
o.newRefractionHeightMap = function(heightMap, x, y, strength)
return love.light.newRefractionHeightMap(o, heightMap, x, y, strength)
end
-- set polygon data -- set polygon data
o.setPoints = function(n, ...) o.setPoints = function(n, ...)
o.poly[n].data = {...} o.poly[n].data = {...}
@ -929,6 +985,7 @@ function love.light.newImage(p, img, x, y, width, height, ox, oy)
o.glowGreen = 255 o.glowGreen = 255
o.glowBlue = 255 o.glowBlue = 255
o.glowStrength = 0.0 o.glowStrength = 0.0
o.refractionStrength = 1.0
o.type = "image" o.type = "image"
p.changed = true p.changed = true
o.data = { o.data = {
@ -977,6 +1034,14 @@ function love.light.newImage(p, img, x, y, width, height, ox, oy)
p.changed = true p.changed = true
end end
end end
-- get x position
o.getX = function()
return o.x
end
-- get y position
o.getY = function(y)
return o.y
end
-- get width -- get width
o.getWidth = function() o.getWidth = function()
return o.width return o.width
@ -1119,6 +1184,186 @@ function love.light.newImage(p, img, x, y, width, height, ox, oy)
return o return o
end end
-- refraction object (height map)
function love.light.newRefractionHeightMap(p, heightMap, x, y, strength)
local normal = HeightMapToNormalMap(heightMap, strength)
return love.light.newRefraction(p, normal, x, y)
end
-- refraction object
function love.light.newRefraction(p, normal, x, y)
local o = {}
p.refraction[#p.refraction + 1] = o
o.id = #p.refraction
o.normal = normal
o.normal:setWrap("repeat", "repeat")
o.x = x or 0
o.y = y or 0
o.width = width or normal:getWidth()
o.height = height or normal:getHeight()
o.ox = o.width / 2.0
o.oy = o.height / 2.0
o.tileX = 0
o.tileY = 0
o.vertices = {
{0.0, 0.0, 0.0, 0.0},
{o.width, 0.0, 1.0, 0.0},
{o.width, o.height, 1.0, 1.0},
{0.0, o.height, 0.0, 1.0}
}
o.mesh = love.graphics.newMesh(o.vertices, o.normal, "fan")
o.normalWidth = normal:getWidth()
o.normalHeight = normal:getHeight()
o.strength = strength or 1.0
o.type = "refraction"
p.changed = true
-- set position
o.setPosition = function(x, y)
if x ~= o.x or y ~= o.y then
o.x = x
o.y = y
p.changed = true
end
end
-- set x position
o.setX = function(x)
if x ~= o.x then
o.x = x
p.changed = true
end
end
-- set y position
o.setY = function(y)
if y ~= o.y then
o.y = y
p.changed = true
end
end
-- set tile offset
o.setTileOffset = function(tx, ty)
o.tileX = tx / o.width
o.tileY = ty / o.height
o.vertices = {
{0.0, 0.0, o.tileX, o.tileY},
{o.width, 0.0, o.tileX + 1.0, o.tileY},
{o.width, o.height, o.tileX + 1.0, o.tileY + 1.0},
{0.0, o.height, o.tileX, o.tileY + 1.0}
}
p.changed = true
end
-- get x position
o.getX = function()
return o.x
end
-- get y position
o.getY = function(y)
return o.y
end
-- get width
o.getWidth = function()
return o.width
end
-- get height
o.getHeight = function()
return o.height
end
-- get image width
o.getImageWidth = function()
return o.imgWidth
end
-- get image height
o.getImageHeight = function()
return o.imgHeight
end
-- set normal
o.setNormalMap = function(normal)
o.normal = normal
end
-- set height map
o.setHeightMap = function(heightMap, strength)
o.normal = HeightMapToNormalMap(heightMap, strength)
end
-- generate flat normal map
o.generateNormalMapFlat = function(mode)
local imgData = o.img:getData()
local imgNormalData = love.image.newImageData(o.imgWidth, o.imgHeight)
local color
if mode == "top" then
color = {127, 127, 255}
elseif mode == "front" then
color = {127, 255, 127}
elseif mode == "back" then
color = {127, 0, 127}
elseif mode == "left" then
color = {31, 255, 223}
elseif mode == "right" then
color = {223, 223, 127}
end
for i = 0, o.imgHeight - 1 do
for k = 0, o.imgWidth - 1 do
local r, g, b, a = imgData:getPixel(k, i)
if a > 0 then
imgNormalData:setPixel(k, i, color[1], color[2], color[3], 255)
end
end
end
o.normal = love.graphics.newImage(imgNormalData)
end
-- generate faded normal map
o.generateNormalMapGradient = function(horizontalGradient, verticalGradient)
local imgData = o.img:getData()
local imgNormalData = love.image.newImageData(o.imgWidth, o.imgHeight)
local dx = 255.0 / o.imgWidth
local dy = 255.0 / o.imgHeight
local nx
local ny
local nz
for i = 0, o.imgWidth - 1 do
for k = 0, o.imgHeight - 1 do
local r, g, b, a = imgData:getPixel(i, k)
if a > 0 then
if horizontalGradient == "gradient" then
nx = i * dx
elseif horizontalGradient == "inverse" then
nx = 255 - i * dx
else
nx = 127
end
if verticalGradient == "gradient" then
ny = 127 + k * dy * 0.5
nz = 255 - k * dy * 0.5
elseif verticalGradient == "inverse" then
ny = 127 - k * dy * 0.5
nz = 127 - k * dy * 0.25
else
ny = 255
nz = 127
end
imgNormalData:setPixel(i, k, nx, ny, nz, 255)
end
end
end
o.normal = love.graphics.newImage(imgNormalData)
end
-- generate normal map
o.generateNormalMap = function(strength)
o.normal = HeightMapToNormalMap(o.img, strength)
end
-- get type
o.getType = function()
return o.type
end
return o
end
-- vector functions -- vector functions
function normalize(v) function normalize(v)
local len = math.sqrt(math.pow(v[1], 2) + math.pow(v[2], 2)) local len = math.sqrt(math.pow(v[1], 2) + math.pow(v[2], 2))

View File

@ -61,12 +61,14 @@ function love.load()
tile = love.graphics.newImage("gfx/tile.png") tile = love.graphics.newImage("gfx/tile.png")
tile_normal = love.graphics.newImage("gfx/tile_normal.png") tile_normal = love.graphics.newImage("gfx/tile_normal.png")
tile_glow = love.graphics.newImage("gfx/tile_glow.png") tile_glow = love.graphics.newImage("gfx/tile_glow.png")
refraction_normal = love.graphics.newImage("gfx/refraction_normal.png")
-- light world -- light world
lightRange = 400 lightRange = 400
lightSmooth = 1.0 lightSmooth = 1.0
lightWorld = love.light.newWorld() lightWorld = love.light.newWorld()
lightWorld.setAmbientColor(15, 15, 31) lightWorld.setAmbientColor(15, 15, 31)
lightWorld.setRefractionStrength(16.0)
mouseLight = lightWorld.newLight(0, 0, 255, 127, 63, lightRange) mouseLight = lightWorld.newLight(0, 0, 255, 127, 63, lightRange)
mouseLight.setGlowStrength(0.3) mouseLight.setGlowStrength(0.3)
mouseLight.setSmooth(lightSmooth) mouseLight.setSmooth(lightSmooth)
@ -90,6 +92,9 @@ function love.load()
offsetOldX = 0.0 offsetOldX = 0.0
offsetOldY = 0.0 offsetOldY = 0.0
offsetChanged = false offsetChanged = false
tileX = 0
tileY = 0
end end
function love.update(dt) function love.update(dt)
@ -101,23 +106,31 @@ function love.update(dt)
if love.keyboard.isDown("w") then if love.keyboard.isDown("w") then
for i = 1, phyCnt do for i = 1, phyCnt do
if phyBody[i] then
phyBody[i]:applyForce(0, -2000) phyBody[i]:applyForce(0, -2000)
end end
end
elseif love.keyboard.isDown("s") then elseif love.keyboard.isDown("s") then
for i = 1, phyCnt do for i = 1, phyCnt do
if phyBody[i] then
phyBody[i]:applyForce(0, 2000) phyBody[i]:applyForce(0, 2000)
end end
end end
end
if love.keyboard.isDown("a") then if love.keyboard.isDown("a") then
for i = 1, phyCnt do for i = 1, phyCnt do
if phyBody[i] then
phyBody[i]:applyForce(-2000, 0) phyBody[i]:applyForce(-2000, 0)
end end
end
elseif love.keyboard.isDown("d") then elseif love.keyboard.isDown("d") then
for i = 1, phyCnt do for i = 1, phyCnt do
if phyBody[i] then
phyBody[i]:applyForce(2000, 0) phyBody[i]:applyForce(2000, 0)
end end
end end
end
if love.keyboard.isDown("up") then if love.keyboard.isDown("up") then
offsetY = offsetY + dt * 200 offsetY = offsetY + dt * 200
@ -144,8 +157,11 @@ function love.update(dt)
lightWorld.setLightDirection(i, lightDirection) lightWorld.setLightDirection(i, lightDirection)
end end
tileX = tileX + dt * 32.0
tileY = tileY + dt * 8.0
for i = 1, phyCnt do for i = 1, phyCnt do
if phyBody[i]:isAwake() or offsetChanged then if phyBody[i] and (phyBody[i]:isAwake() or offsetChanged) then
if offsetChanged then if offsetChanged then
phyBody[i]:setX(phyBody[i]:getX() + (offsetX - offsetOldX)) phyBody[i]:setX(phyBody[i]:getX() + (offsetX - offsetOldX))
phyBody[i]:setY(phyBody[i]:getY() + (offsetY - offsetOldY)) phyBody[i]:setY(phyBody[i]:getY() + (offsetY - offsetOldY))
@ -156,6 +172,14 @@ function love.update(dt)
phyLight[i].setPosition(phyBody[i]:getX(), phyBody[i]:getY()) phyLight[i].setPosition(phyBody[i]:getX(), phyBody[i]:getY())
elseif phyLight[i].getType() == "image" then elseif phyLight[i].getType() == "image" then
phyLight[i].setPosition(phyBody[i]:getX(), phyBody[i]:getY()) phyLight[i].setPosition(phyBody[i]:getX(), phyBody[i]:getY())
elseif phyLight[i].getType() == "refraction" then
--phyLight[i].setPosition(phyBody[i]:getX(), phyBody[i]:getY())
end
end
if phyLight[i].getType() == "refraction" then
phyLight[i].setTileOffset(tileX, tileY)
if offsetChanged then
phyLight[i].setPosition(phyLight[i].getX() + (offsetX - offsetOldX), phyLight[i].getY() + (offsetY - offsetOldY))
end end
end end
end end
@ -235,6 +259,9 @@ function love.draw()
lightWorld.drawGlow() lightWorld.drawGlow()
end end
-- draw refraction
lightWorld.drawRefraction()
-- draw help -- draw help
if helpOn then if helpOn then
love.graphics.setBlendMode("alpha") love.graphics.setBlendMode("alpha")
@ -482,6 +509,7 @@ function love.keypressed(k, u)
phyLight[phyCnt].setAlpha(0.5) phyLight[phyCnt].setAlpha(0.5)
phyLight[phyCnt].setGlowStrength(1.0) phyLight[phyCnt].setGlowStrength(1.0)
phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic")
math.randomseed(love.timer.getTime())
phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, math.random(32, 64), math.random(32, 64)) phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, math.random(32, 64), math.random(32, 64))
phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt])
phyFixture[phyCnt]:setRestitution(0.5) phyFixture[phyCnt]:setRestitution(0.5)
@ -491,6 +519,7 @@ function love.keypressed(k, u)
phyLight[phyCnt].setColor(math.random(0, 255), math.random(0, 255), math.random(0, 255)) phyLight[phyCnt].setColor(math.random(0, 255), math.random(0, 255), math.random(0, 255))
elseif k == "9" then elseif k == "9" then
-- add circle -- add circle
math.randomseed(love.timer.getTime())
cRadius = math.random(8, 32) cRadius = math.random(8, 32)
phyCnt = phyCnt + 1 phyCnt = phyCnt + 1
phyLight[phyCnt] = lightWorld.newCircle(mx, my, cRadius) phyLight[phyCnt] = lightWorld.newCircle(mx, my, cRadius)
@ -505,6 +534,13 @@ function love.keypressed(k, u)
phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt])
phyFixture[phyCnt]:setRestitution(0.5) phyFixture[phyCnt]:setRestitution(0.5)
elseif k == "0" then elseif k == "0" then
phyCnt = phyCnt + 1
phyLight[phyCnt] = lightWorld.newRefraction(refraction_normal, mx, my, 1.0)
--phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic")
--phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, phyLight[phyCnt].getWidth(), phyLight[phyCnt].getHeight())
--phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt])
--phyFixture[phyCnt]:setRestitution(0.5)
elseif k == "l" then
-- add light -- add light
local r = lightWorld.getLightCount() % 3 local r = lightWorld.getLightCount() % 3
local light local light

15
shader/refraction.glsl Normal file
View File

@ -0,0 +1,15 @@
extern Image backBuffer;
extern vec2 screen = vec2(800.0, 600.0);
extern float refractionStrength = 1.0;
extern vec3 refractionColor = vec3(1.0, 1.0, 1.0);
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
vec2 pSize = vec2(1.0 / screen.x, 1.0 / screen.y);
vec4 normal = Texel(texture, texture_coords);
if(normal.a > 0.0) {
return vec4(Texel(backBuffer, vec2(texture_coords.x + (normal.x - 0.5) * pSize.x * refractionStrength, texture_coords.y + (normal.y - 0.5) * pSize.y * refractionStrength)).rgb * refractionColor, 1.0);
} else {
return vec4(0.0);
}
}