diff --git a/lib/init.lua b/lib/init.lua index 2f05015..4f70e25 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -33,7 +33,8 @@ local light_world = class() light_world.blurv = love.graphics.newShader(_PACKAGE.."shaders/blurv.glsl") light_world.blurh = love.graphics.newShader(_PACKAGE.."shaders/blurh.glsl") -light_world.shadowShader = love.graphics.newShader(_PACKAGE.."/shaders/shadow.glsl") +light_world.normalShader = love.graphics.newShader(_PACKAGE.."shaders/normal.glsl") +light_world.shineShader = 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") @@ -53,8 +54,10 @@ function light_world:init(options) self.glowDown = false self.normalInvert = false - self.disableGlow = false - self.disableMaterial = false + self.disableMaterial = true + self.disableNormals = true + self.disableShadows = false + self.disableGlow = true self.disableReflection = true self.disableRefraction = true @@ -69,15 +72,11 @@ function light_world:refreshScreenSize(w, h) self.w, self.h = w, h self.render_buffer = love.graphics.newCanvas(w, h) - self.normal = love.graphics.newCanvas(w, h) - self.normal2 = love.graphics.newCanvas(w, h) self.normalMap = love.graphics.newCanvas(w, h) self.shadowMap = love.graphics.newCanvas(w, h) self.glowMap = love.graphics.newCanvas(w, h) - self.glowMap2 = love.graphics.newCanvas(w, h) self.refractionMap = love.graphics.newCanvas(w, h) self.reflectionMap = love.graphics.newCanvas(w, h) - self.reflectionMap2 = love.graphics.newCanvas(w, h) self.post_shader:refreshScreenSize(w, h) end @@ -98,7 +97,8 @@ function light_world:draw(cb) util.drawto(self.render_buffer, self.l, self.t, self.s, function() cb( self.l,self.t,self.w,self.h,self.s) _ = self.disableMaterial or self:drawMaterial( self.l,self.t,self.w,self.h,self.s) - self:drawShadows( self.l,self.t,self.w,self.h,self.s) + _ = self.disableNormals or self:drawNormalShading( self.l,self.t,self.w,self.h,self.s) + _ = self.disableShadows or self:drawShadows( self.l,self.t,self.w,self.h,self.s) _ = self.disableGlow or self:drawGlow( self.l,self.t,self.w,self.h,self.s) _ = self.disableRefraction or self:drawRefraction( self.l,self.t,self.w,self.h,self.s) _ = self.disableReflection or self:drawReflection( self.l,self.t,self.w,self.h,self.s) @@ -106,19 +106,15 @@ function light_world:draw(cb) self.post_shader:drawWith(self.render_buffer, self.l, self.t, self.s) end -function light_world:drawBlur(blendmode, blur, canvas, canvas2, l, t, w, h, s) - if blur <= 0 then - return - end - canvas2:clear() +function light_world:drawBlur(blendmode, blur, canvas, l, t, w, h, s) + if blur <= 0 then return end self.blurv:send("steps", blur) self.blurh:send("steps", blur) - util.drawCanvasToCanvas(canvas, canvas2, {shader = self.blurv, blendmode = blendmode}) - util.drawCanvasToCanvas(canvas2, canvas, {shader = self.blurh, blendmode = blendmode}) + util.process(canvas, {shader = self.blurv, blendmode = blendmode}) + util.process(canvas, {shader = self.blurh, blendmode = blendmode}) end --- draw normal shading -function light_world:drawShadows(l,t,w,h,s) +function light_world:drawNormalShading(l,t,w,h,s) -- create normal map self.normalMap:clear() util.drawto(self.normalMap, l, t, s, function() @@ -128,48 +124,58 @@ function light_world:drawShadows(l,t,w,h,s) end end end) - - self.normal2:clear() + self.normalShader:send('NormalMap', self.normalMap) + self.normalShader:send('AmbientColor',{self.ambient[1] / 255, + self.ambient[2] / 255, + self.ambient[3] / 255, + 0.2}) for i = 1, #self.lights do local light = self.lights[i] if light:isVisible() then - -- create shadow map for this light - self.shadowMap:clear() - util.drawto(self.shadowMap, l, t, s, function() - for k = 1, #self.bodies do - if self.bodies[k]:isInLightRange(light) and self.bodies[k]:isVisible() then - self.bodies[k]:drawShadow(light) - end - end - local angle = math.pi - light.angle / 2.0 - love.graphics.setColor(0, 0, 0) - love.graphics.arc("fill", light.x, light.y, light.range, light.direction - angle, light.direction + angle) - end) - -- draw scene for this light using normals and shadowmap - self.shadowShader:send('shadowMap', self.shadowMap) - self.shadowShader:send('lightColor', {light.red / 255.0, light.green / 255.0, light.blue / 255.0}) - self.shadowShader:send("lightPosition", {(light.x + l/s) * s, (h/s - (light.y + t/s)) * s, (light.z * 10) / 255.0}) - self.shadowShader:send('lightRange', light.range * s) - self.shadowShader:send("lightSmooth", light.smooth) - self.shadowShader:send("lightGlow", {1.0 - light.glowSize, light.glowStrength}) - self.shadowShader:send("invert_normal", self.normalInvert) - util.drawCanvasToCanvas(self.normalMap, self.normal2, { + self.normalShader:send('LightColor', {light.red / 255.0, light.green / 255.0, light.blue / 255.0, 1}) + self.normalShader:send("LightPos", {(light.x + l/s) * s, (h/s - (light.y + t/s)) * s, (light.z * 10) / 255.0}) + self.normalShader:send('Falloff', {light.glowSize, light.smooth, light.range}) + util.process(self.render_buffer, { blendmode = 'additive', - shader = self.shadowShader + shader = self.normalShader + }) + end + end +end + +-- draw normal shading +function light_world:drawShadows(l,t,w,h,s) + self.shadowMap:clear() + for i = 1, #self.lights do + local light = self.lights[i] + if light:isVisible() then + self.shineShader:send('lightColor', {light.red / 255.0, light.green / 255.0, light.blue / 255.0}) + self.shineShader:send("lightPosition", {(light.x + l/s) * s, (h/s - (light.y + t/s)) * s, (light.z * 10) / 255.0}) + self.shineShader:send('lightRange', light.range * s) + self.shineShader:send("lightSmooth", light.smooth) + self.shineShader:send("lightGlow", {1.0 - light.glowSize, light.glowStrength}) + self.shineShader:send('AmbientColor',{self.ambient[1] / 255, self.ambient[2] / 255, self.ambient[3] / 255, 0.2}) + util.process(self.shadowMap, { + blendmode = 'additive', + shader = self.shineShader, + istencil = function() + love.graphics.translate(l, t) + love.graphics.scale(s) + for k = 1, #self.bodies do + if self.bodies[k]:isInLightRange(light) and self.bodies[k]:isVisible() then + self.bodies[k]:drawShadow(light) + end + end + local angle = math.pi - light.angle / 2.0 + love.graphics.setColor(0, 0, 0) + love.graphics.arc("fill", light.x, light.y, light.range, light.direction - angle, light.direction + angle) + end }) end end - -- add in ambient color - util.drawCanvasToCanvas(self.normal2, self.normal) - util.drawto(self.normal, 0, 0, 1, function() - love.graphics.setBlendMode("additive") - love.graphics.setColor({self.ambient[1], self.ambient[2], self.ambient[3]}) - love.graphics.rectangle("fill", 0, 0, w,h) - end) - - light_world:drawBlur("alpha", self.shadowBlur, self.normal, self.normal2, l, t, w, h, s) - util.drawCanvasToCanvas(self.normal, self.render_buffer, {blendmode = "multiplicative"}) + light_world:drawBlur("alpha", self.shadowBlur, self.shadowMap, l, t, w, h, s) + util.drawCanvasToCanvas(self.shadowMap, self.render_buffer, {blendmode = "multiplicative"}) end -- draw material @@ -206,7 +212,7 @@ function light_world:drawGlow(l,t,w,h,s) end) if has_glow then - light_world:drawBlur("alpha", self.glowBlur, self.glowMap, self.glowMap2, l, t, w, h, s) + light_world:drawBlur("alpha", self.glowBlur, self.glowMap, l, t, w, h, s) util.drawCanvasToCanvas(self.glowMap, self.render_buffer, {blendmode = "additive"}) end end diff --git a/lib/shaders/normal.glsl b/lib/shaders/normal.glsl new file mode 100644 index 0000000..4225cfd --- /dev/null +++ b/lib/shaders/normal.glsl @@ -0,0 +1,33 @@ +extern Image NormalMap; //normal map +extern vec3 LightPos; //light position, normalized, div x/res +extern vec4 LightColor; //light RGBA -- alpha is intensity +extern vec4 AmbientColor;//ambient RGBA -- alpha is intensity +extern vec3 Falloff; //attenuation coefficients + +vec4 effect(vec4 vColor, Image u_texture, vec2 vTexCoord, vec2 screen_coords) { + //RGBA of our diffuse color + vec4 DiffuseColor = Texel(u_texture, vTexCoord); + //RGB of our normal map + vec4 normal = Texel(NormalMap, vTexCoord); + if(normal.a == 0.0) { + return vec4(0.0); + } + //The delta position of light + vec3 LightDir = vec3((LightPos.xy - screen_coords.xy)/love_ScreenSize.xy, LightPos.z); + //Correct for aspect ratio + LightDir.x *= love_ScreenSize.x / love_ScreenSize.y; + //Determine distance (used for attenuation) BEFORE we normalize our LightDir + float D = length(LightDir); + //normalize our vectors + vec3 N = normalize(normal.rgb * 2.0 - 1.0); + vec3 L = normalize(LightDir); + //Pre-multiply light color with intensity + //Then perform "N dot L" to determine our diffuse term + vec3 Diffuse = (LightColor.rgb * LightColor.a) * max(dot(N, L), 0.0); + //pre-multiply ambient color with intensity + vec3 Ambient = AmbientColor.rgb * AmbientColor.a; + float Attenuation = 1.0 / ( Falloff.x + (Falloff.y*D) + (Falloff.z*D*D) ); + vec3 Intensity = Ambient + Diffuse * Attenuation; + return vec4(DiffuseColor.rgb * Intensity, DiffuseColor.a); +} + diff --git a/lib/shaders/shadow.glsl b/lib/shaders/shadow.glsl deleted file mode 100644 index c569bb8..0000000 --- a/lib/shaders/shadow.glsl +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright (c) 2014 Tim Anema - light shadow, shine and normal shader all in one -*/ -extern Image shadowMap; //a canvas containing shadow data only -extern vec3 lightPosition; //the light position on the screen(not global) -extern vec3 lightColor; //the rgb color of the light -extern float lightRange; //the range of the light -extern float lightSmooth; //smoothing of the lights attenuation -extern vec2 lightGlow = vec2(0.5, 0.5); //how brightly the light bulb part glows -extern bool invert_normal = false; //if the light should invert normals - -vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) { - vec4 pixelColor = Texel(texture, texture_coords); - - float dist = distance(lightPosition, vec3(pixel_coords, 1.0)); - //if the pixel is within this lights range - if(dist > lightRange) { - return vec4(0.0, 0.0, 0.0, 1.0); - }else{ - //calculater 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(pixelColor.a == 0.0) { - vec3 pixel = lightColor * pow(att, lightSmooth) + pow(smoothstep(lightGlow.x, 1.0, att), lightSmooth) * lightGlow.y; - //If on the shadow map add the shadow color - vec4 shadowColor = Texel(shadowMap, texture_coords); - if(shadowColor.a > 0.0) { - pixel.rgb = pixel.rgb * shadowColor.rgb; - } - return vec4(pixel, 1.0); - } else { - //on the normal map, draw normal shadows - vec3 lightDir = vec3((lightPosition.xy - pixel_coords.xy) / love_ScreenSize.xy, lightPosition.z); - lightDir.x *= love_ScreenSize.x / love_ScreenSize.y; - vec3 normal = normalize(vec3(pixelColor.r,(invert_normal ? 1 - pixelColor.g : pixelColor.g), pixelColor.b) * 2.0 - 1.0); - vec3 diffuse = lightColor * max(dot(normalize(normal), normalize(lightDir)), 0.0); - //return the light that is effected by the normal and attenuation - return vec4(diffuse * att, 1.0); - } - } -} - diff --git a/lib/shaders/shine.glsl b/lib/shaders/shine.glsl new file mode 100644 index 0000000..f070f19 --- /dev/null +++ b/lib/shaders/shine.glsl @@ -0,0 +1,21 @@ +/* + Copyright (c) 2014 Tim Anema +*/ +extern vec3 lightPosition; //the light position on the screen(not global) +extern vec3 lightColor; //the rgb color of the light +extern float lightRange; //the range of the light +extern float lightSmooth; //smoothing of the lights attenuation +extern vec2 lightGlow = vec2(0.5, 0.5); //how brightly the light bulb part glows +extern vec4 AmbientColor; //ambient RGBA -- alpha is intensity + +vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) { + float dist = distance(lightPosition, vec3(pixel_coords, 1.0)); + //if the pixel is within this lights range + //calculater 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 + vec3 Ambient = AmbientColor.rgb * AmbientColor.a; + vec3 pixel = lightColor * pow(att, lightSmooth) + pow(smoothstep(lightGlow.x, 1.0, att), lightSmooth) * lightGlow.y; + return vec4(Ambient + pixel, 1.0); +} + diff --git a/lib/util.lua b/lib/util.lua index 8374ecb..54560f4 100644 --- a/lib/util.lua +++ b/lib/util.lua @@ -1,5 +1,9 @@ local util = {} +function util.process(canvas, options) + util.drawCanvasToCanvas(canvas, canvas, options) +end + function util.drawCanvasToCanvas(canvas, other_canvas, options) options = options or {}