Add material system.
BIN
gfx/ape.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
gfx/ape_glow.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
gfx/ape_normal.png
Normal file
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 981 B After Width: | Height: | Size: 981 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
gfx/sphere/cooper.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
gfx/sphere/glass.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
gfx/sphere/glow.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
gfx/sphere/lava.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
gfx/sphere/neon.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
gfx/sphere/reflect.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
gfx/sphere/toon.png
Normal file
After Width: | Height: | Size: 51 KiB |
119
light.lua
@ -39,6 +39,7 @@ function love.light.newWorld()
|
||||
o.refractionMap2 = love.graphics.newCanvas()
|
||||
o.reflectionMap = love.graphics.newCanvas()
|
||||
o.reflectionMap2 = love.graphics.newCanvas()
|
||||
o.normalInvert = false
|
||||
o.glowBlur = 1.0
|
||||
o.isGlowBlur = false
|
||||
o.glowTimer = 0.0
|
||||
@ -49,6 +50,8 @@ function love.light.newWorld()
|
||||
o.shader = love.graphics.newShader("shader/poly_shadow.glsl")
|
||||
o.glowShader = love.graphics.newShader("shader/glow.glsl")
|
||||
o.normalShader = love.graphics.newShader("shader/normal.glsl")
|
||||
o.normalInvertShader = love.graphics.newShader("shader/normal_invert.glsl")
|
||||
o.materialShader = love.graphics.newShader("shader/material.glsl")
|
||||
o.refractionShader = love.graphics.newShader("shader/refraction.glsl")
|
||||
o.refractionShader:send("screen", {love.window.getWidth(), love.window.getHeight()})
|
||||
o.reflectionShader = love.graphics.newShader("shader/reflection.glsl")
|
||||
@ -219,14 +222,25 @@ function love.light.newWorld()
|
||||
|
||||
for i = 1, #o.lights do
|
||||
if o.lights[i].visible then
|
||||
o.normalShader:send('screenResolution', {love.graphics.getWidth(), love.graphics.getHeight()})
|
||||
o.normalShader:send('lightColor', {o.lights[i].red / 255.0, o.lights[i].green / 255.0, o.lights[i].blue / 255.0})
|
||||
o.normalShader:send('lightPosition',{o.lights[i].x, love.graphics.getHeight() - o.lights[i].y, o.lights[i].z / 255.0})
|
||||
o.normalShader:send('lightRange',{o.lights[i].range})
|
||||
o.normalShader:send("lightSmooth", o.lights[i].smooth)
|
||||
o.normalShader:send("lightAngle", math.pi - o.lights[i].angle / 2.0)
|
||||
o.normalShader:send("lightDirection", o.lights[i].direction)
|
||||
love.graphics.setShader(o.normalShader)
|
||||
if normalInvert then
|
||||
o.normalInvertShader:send('screenResolution', {love.graphics.getWidth(), love.graphics.getHeight()})
|
||||
o.normalInvertShader:send('lightColor', {o.lights[i].red / 255.0, o.lights[i].green / 255.0, o.lights[i].blue / 255.0})
|
||||
o.normalInvertShader:send('lightPosition',{o.lights[i].x, love.graphics.getHeight() - o.lights[i].y, o.lights[i].z / 255.0})
|
||||
o.normalInvertShader:send('lightRange',{o.lights[i].range})
|
||||
o.normalInvertShader:send("lightSmooth", o.lights[i].smooth)
|
||||
o.normalInvertShader:send("lightAngle", math.pi - o.lights[i].angle / 2.0)
|
||||
o.normalInvertShader:send("lightDirection", o.lights[i].direction)
|
||||
love.graphics.setShader(o.normalInvertShader)
|
||||
else
|
||||
o.normalShader:send('screenResolution', {love.graphics.getWidth(), love.graphics.getHeight()})
|
||||
o.normalShader:send('lightColor', {o.lights[i].red / 255.0, o.lights[i].green / 255.0, o.lights[i].blue / 255.0})
|
||||
o.normalShader:send('lightPosition',{o.lights[i].x, love.graphics.getHeight() - o.lights[i].y, o.lights[i].z / 255.0})
|
||||
o.normalShader:send('lightRange',{o.lights[i].range})
|
||||
o.normalShader:send("lightSmooth", o.lights[i].smooth)
|
||||
o.normalShader:send("lightAngle", math.pi - o.lights[i].angle / 2.0)
|
||||
o.normalShader:send("lightDirection", o.lights[i].direction)
|
||||
love.graphics.setShader(o.normalShader)
|
||||
end
|
||||
love.graphics.draw(o.normalMap, LOVE_LIGHT_TRANSLATE_X, LOVE_LIGHT_TRANSLATE_Y)
|
||||
end
|
||||
end
|
||||
@ -408,6 +422,18 @@ function love.light.newWorld()
|
||||
love.graphics.draw(o.pixelShadow, LOVE_LIGHT_TRANSLATE_X, LOVE_LIGHT_TRANSLATE_Y)
|
||||
love.graphics.setBlendMode("alpha")
|
||||
end
|
||||
-- draw material
|
||||
o.drawMaterial = function()
|
||||
love.graphics.setShader(o.materialShader)
|
||||
for i = 1, #o.body do
|
||||
if o.body[i].material and o.body[i].normal then
|
||||
love.graphics.setColor(255, 255, 255)
|
||||
o.materialShader:send("material", o.body[i].material)
|
||||
love.graphics.draw(o.body[i].normal, o.body[i].x - o.body[i].nx + LOVE_LIGHT_TRANSLATE_X, o.body[i].y - o.body[i].ny + LOVE_LIGHT_TRANSLATE_Y)
|
||||
end
|
||||
end
|
||||
love.graphics.setShader()
|
||||
end
|
||||
-- draw glow
|
||||
o.drawGlow = function()
|
||||
love.graphics.setColor(255, 255, 255)
|
||||
@ -507,6 +533,10 @@ function love.light.newWorld()
|
||||
o.setAmbientBlue = function(blue)
|
||||
o.ambient[3] = blue
|
||||
end
|
||||
-- set normal invert
|
||||
o.setNormalInvert = function(invert)
|
||||
o.normalInvert = invert
|
||||
end
|
||||
-- set blur
|
||||
o.setBlur = function(blur)
|
||||
o.blur = blur
|
||||
@ -558,6 +588,14 @@ function love.light.newWorld()
|
||||
o.newRefractionHeightMap = function(heightMap, x, y, strength)
|
||||
return love.light.newRefractionHeightMap(o, heightMap, x, y, strength)
|
||||
end
|
||||
-- new reflection
|
||||
o.newReflection = function(normal, x, y)
|
||||
return love.light.newReflection(o, normal, x, y)
|
||||
end
|
||||
-- new reflection from height map
|
||||
o.newReflectionHeightMap = function(heightMap, x, y, strength)
|
||||
return love.light.newReflectionHeightMap(o, heightMap, x, y, strength)
|
||||
end
|
||||
-- new body
|
||||
o.newBody = function(type, ...)
|
||||
return love.light.newBody(o, type, ...)
|
||||
@ -738,6 +776,7 @@ function love.light.newBody(p, type, ...)
|
||||
o.id = #p.body
|
||||
o.type = type
|
||||
o.normal = nil
|
||||
o.material = nil
|
||||
o.glow = nil
|
||||
if o.type == "circle" then
|
||||
o.x = args[1] or 0
|
||||
@ -834,12 +873,41 @@ function love.light.newBody(p, type, ...)
|
||||
o.width = args[4] or 64
|
||||
o.height = args[5] or 64
|
||||
end
|
||||
o.ox = o.width / 2.0
|
||||
o.oy = o.height / 2.0
|
||||
o.ox = o.width * 0.5
|
||||
o.oy = o.height * 0.5
|
||||
o.reflection = false
|
||||
o.reflective = false
|
||||
o.refraction = true
|
||||
o.refractive = false
|
||||
elseif o.type == "reflection" then
|
||||
o.normal = args[1]
|
||||
o.x = args[2] or 0
|
||||
o.y = args[3] or 0
|
||||
if o.normal then
|
||||
o.normalWidth = o.normal:getWidth()
|
||||
o.normalHeight = o.normal:getHeight()
|
||||
o.width = args[4] or o.normalWidth
|
||||
o.height = args[5] or o.normalHeight
|
||||
o.nx = o.normalWidth * 0.5
|
||||
o.ny = o.normalHeight * 0.5
|
||||
o.normal:setWrap("repeat", "repeat")
|
||||
o.normalVert = {
|
||||
{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.normalMesh = love.graphics.newMesh(o.normalVert, o.normal, "fan")
|
||||
else
|
||||
o.width = args[4] or 64
|
||||
o.height = args[5] or 64
|
||||
end
|
||||
o.ox = o.width * 0.5
|
||||
o.oy = o.height * 0.5
|
||||
o.reflection = true
|
||||
o.reflective = false
|
||||
o.refraction = false
|
||||
o.refractive = false
|
||||
end
|
||||
o.shine = true
|
||||
o.red = 0
|
||||
@ -1062,13 +1130,13 @@ function love.light.newBody(p, type, ...)
|
||||
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 == "back" then
|
||||
color = {127, 255, 127}
|
||||
elseif mode == "left" then
|
||||
color = {31, 255, 223}
|
||||
color = {31, 0, 223}
|
||||
elseif mode == "right" then
|
||||
color = {223, 223, 127}
|
||||
color = {223, 0, 127}
|
||||
end
|
||||
|
||||
for i = 0, o.imgHeight - 1 do
|
||||
@ -1109,10 +1177,10 @@ function love.light.newBody(p, type, ...)
|
||||
end
|
||||
|
||||
if verticalGradient == "gradient" then
|
||||
ny = 127 + k * dy * 0.5
|
||||
ny = 127 - k * dy * 0.5
|
||||
nz = 255 - k * dy * 0.5
|
||||
elseif verticalGradient == "inverse" then
|
||||
ny = 127 - k * dy * 0.5
|
||||
ny = 127 + k * dy * 0.5
|
||||
nz = 127 - k * dy * 0.25
|
||||
else
|
||||
ny = 255
|
||||
@ -1138,6 +1206,12 @@ function love.light.newBody(p, type, ...)
|
||||
o.nx = o.normalWidth * 0.5
|
||||
o.ny = o.normalHeight * 0.5
|
||||
end
|
||||
-- set material
|
||||
o.setMaterial = function(material)
|
||||
if material then
|
||||
o.material = material
|
||||
end
|
||||
end
|
||||
-- set normal
|
||||
o.setGlowMap = function(glow)
|
||||
o.glow = glow
|
||||
@ -1242,6 +1316,17 @@ function love.light.newRefractionHeightMap(p, heightMap, x, y, strength)
|
||||
return love.light.newRefraction(p, normal, x, y)
|
||||
end
|
||||
|
||||
-- reflection object
|
||||
function love.light.newReflection(p, normal, x, y, width, height)
|
||||
return p.newBody("reflection", normal, x, y, width, height)
|
||||
end
|
||||
|
||||
-- reflection object (height map)
|
||||
function love.light.newReflectionHeightMap(p, heightMap, x, y, strength)
|
||||
local normal = HeightMapToNormalMap(heightMap, strength)
|
||||
return love.light.newReflection(p, normal, x, y)
|
||||
end
|
||||
|
||||
-- vector functions
|
||||
function normalize(v)
|
||||
local len = math.sqrt(math.pow(v[1], 2) + math.pow(v[2], 2))
|
||||
@ -1423,7 +1508,7 @@ function HeightMapToNormalMap(heightMap, strength)
|
||||
end
|
||||
|
||||
red = (255 + ((matrix[1][2] - matrix[2][2]) + (matrix[2][2] - matrix[3][2])) * strength) / 2.0
|
||||
green = (255 - ((matrix[2][2] - matrix[1][1]) + (matrix[2][3] - matrix[2][2])) * strength) / 2.0
|
||||
green = (255 + ((matrix[2][2] - matrix[1][1]) + (matrix[2][3] - matrix[2][2])) * strength) / 2.0
|
||||
blue = 192
|
||||
|
||||
imgData2:setPixel(k, i, red, green, blue)
|
||||
|
60
main.lua
@ -72,6 +72,17 @@ function love.load()
|
||||
led_glow = love.graphics.newImage("gfx/led_glow.png")
|
||||
led_glow2 = love.graphics.newImage("gfx/led_glow2.png")
|
||||
led_glow3 = love.graphics.newImage("gfx/led_glow3.png")
|
||||
ape = love.graphics.newImage("gfx/ape.png")
|
||||
ape_normal = love.graphics.newImage("gfx/ape_normal.png")
|
||||
ape_glow = love.graphics.newImage("gfx/ape_glow.png")
|
||||
|
||||
-- materials
|
||||
material = {}
|
||||
|
||||
local files = love.filesystem.getDirectoryItems("gfx/sphere")
|
||||
for i, file in ipairs(files) do
|
||||
material[i] = love.graphics.newImage("gfx/sphere/" .. file)
|
||||
end
|
||||
|
||||
-- light world
|
||||
lightRange = 400
|
||||
@ -80,9 +91,10 @@ function love.load()
|
||||
lightWorld.setAmbientColor(15, 15, 31)
|
||||
lightWorld.setRefractionStrength(16.0)
|
||||
lightWorld.setReflectionVisibility(0.75)
|
||||
mouseLight = lightWorld.newLight(0, 0, 255, 127, 63, lightRange)
|
||||
mouseLight = lightWorld.newLight(0, 0, 255, 191, 127, lightRange)
|
||||
mouseLight.setGlowStrength(0.3)
|
||||
mouseLight.setSmooth(lightSmooth)
|
||||
mouseLight.z = 63
|
||||
lightDirection = 0.0
|
||||
colorAberration = 0.0
|
||||
|
||||
@ -270,7 +282,7 @@ function love.draw()
|
||||
|
||||
love.graphics.setBlendMode("alpha")
|
||||
for i = 1, phyCnt do
|
||||
if phyLight[i].getType() == "image" then
|
||||
if phyLight[i].getType() == "image" and not phyLight[i].material then
|
||||
if not normalOn then
|
||||
math.randomseed(i)
|
||||
love.graphics.setColor(math.random(127, 255), math.random(127, 255), math.random(127, 255))
|
||||
@ -282,6 +294,8 @@ function love.draw()
|
||||
end
|
||||
end
|
||||
|
||||
lightWorld.drawMaterial()
|
||||
|
||||
-- draw pixel shadow
|
||||
if lightOn and not normalOn then
|
||||
lightWorld.drawPixelShadow()
|
||||
@ -479,7 +493,7 @@ function love.keypressed(k, u)
|
||||
initScene()
|
||||
elseif k == "f12" then
|
||||
lightWorld.clearLights()
|
||||
mouseLight = lightWorld.newLight(0, 0, 255, 127, 63, lightRange)
|
||||
mouseLight = lightWorld.newLight(0, 0, 255, 191, 127, lightRange)
|
||||
mouseLight.setGlowStrength(0.3)
|
||||
mouseLight.setSmooth(lightSmooth)
|
||||
elseif k == "1" then
|
||||
@ -493,24 +507,42 @@ function love.keypressed(k, u)
|
||||
phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt])
|
||||
phyFixture[phyCnt]:setRestitution(0.5)
|
||||
elseif k == "2" then
|
||||
-- add image
|
||||
phyCnt = phyCnt + 1
|
||||
phyLight[phyCnt] = lightWorld.newImage(cone, mx, my, 24, 12, 12, 16)
|
||||
phyLight[phyCnt].setNormalMap(cone_normal)
|
||||
phyLight[phyCnt].setShadowType("circle", 12, 0, -8)
|
||||
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)
|
||||
local r = lightWorld.getBodyCount() % 2
|
||||
if r == 0 then
|
||||
-- add image
|
||||
phyCnt = phyCnt + 1
|
||||
phyLight[phyCnt] = lightWorld.newImage(cone, mx, my, 24, 12, 12, 16)
|
||||
phyLight[phyCnt].setNormalMap(cone_normal)
|
||||
phyLight[phyCnt].setShadowType("circle", 12, 0, -8)
|
||||
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
|
||||
local r = lightWorld.getBodyCount() % #material
|
||||
phyCnt = phyCnt + 1
|
||||
phyLight[phyCnt] = lightWorld.newImage(chest, mx, my, 32, 24, 16, 0)
|
||||
phyLight[phyCnt].setNormalMap(chest_normal)
|
||||
phyLight[phyCnt] = lightWorld.newImage(ape, mx, my, 160, 128, 80, 64)
|
||||
phyLight[phyCnt].setNormalMap(ape_normal)
|
||||
if r == 3 then
|
||||
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
|
||||
local r = lightWorld.getBodyCount() % 5
|
||||
|
10
shader/material.glsl
Normal file
@ -0,0 +1,10 @@
|
||||
extern Image material;
|
||||
|
||||
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||
vec4 normal = Texel(texture, texture_coords);
|
||||
if(normal.a == 1.0) {
|
||||
return Texel(material, vec2(normal.x, normal.y));
|
||||
} else {
|
||||
return vec4(0.0);
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||
}
|
||||
}
|
||||
|
||||
vec3 normal = vec3(pixelColor.r, 1 - pixelColor.g, pixelColor.b);
|
||||
vec3 normal = pixelColor.rgb;
|
||||
float dist = distance(lightPosition, vec3(pixel_coords, normal.b));
|
||||
|
||||
if(dist < lightRange) {
|
||||
|
50
shader/normal_invert.glsl
Normal file
@ -0,0 +1,50 @@
|
||||
#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);
|
||||
}
|
||||
}
|