diff options
Diffstat (limited to 'libs/moonshine/glow.lua')
-rw-r--r-- | libs/moonshine/glow.lua | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/libs/moonshine/glow.lua b/libs/moonshine/glow.lua new file mode 100644 index 0000000..cec010b --- /dev/null +++ b/libs/moonshine/glow.lua @@ -0,0 +1,104 @@ +--[[ +Public domain: + +Copyright (C) 2017 by Matthias Richter <vrld@vrld.org> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +]]-- + + +-- unroll convolution loop for gaussian blur shader +local function make_blur_shader(sigma) + local support = math.max(1, math.floor(3*sigma + .5)) + local one_by_sigma_sq = sigma > 0 and 1 / (sigma * sigma) or 1 + local norm = 0 + + local code = {[[ + extern vec2 direction; + vec4 effect(vec4 color, Image texture, vec2 tc, vec2 _) { + vec4 c = vec4(0.0); + ]]} + local blur_line = "c += vec4(%f) * Texel(texture, tc + vec2(%f) * direction);" + + for i = -support,support do + local coeff = math.exp(-.5 * i*i * one_by_sigma_sq) + norm = norm + coeff + code[#code+1] = blur_line:format(coeff, i) + end + + code[#code+1] = ("return c * vec4(%f) * color;}"):format(1 / norm) + + return love.graphics.newShader(table.concat(code)) +end + +return function(moonshine) + local blurshader -- set in setters.glow_strength + local threshold = love.graphics.newShader[[ + extern number min_luma; + vec4 effect(vec4 color, Image texture, vec2 tc, vec2 _) { + vec4 c = Texel(texture, tc); + number luma = dot(vec3(0.299, 0.587, 0.114), c.rgb); + return c * step(min_luma, luma) * color; + }]] + + local setters = {} + setters.strength = function(v) + blurshader = make_blur_shader(math.max(0,tonumber(v) or 1)) + end + setters.min_luma = function(v) + threshold:send("min_luma", math.max(0, math.min(1, tonumber(v) or 0.5))) + end + + local scene = love.graphics.newCanvas() + local draw = function(buffer) + local front, back = buffer() -- scene so far is in `back' + scene, back = back, scene -- save it for second draw below + + -- 1st pass: draw scene with brightness threshold + love.graphics.setCanvas(front) + love.graphics.clear() + love.graphics.setShader(threshold) + love.graphics.draw(scene) + + -- 2nd pass: apply blur shader in x + blurshader:send('direction', {1 / love.graphics.getWidth(), 0}) + love.graphics.setCanvas(back) + love.graphics.clear() + love.graphics.setShader(blurshader) + love.graphics.draw(front) + + -- 3nd pass: apply blur shader in y and draw original and blurred scene + love.graphics.setCanvas(front) + love.graphics.clear() + + -- original scene without blur shader + love.graphics.setShader() + love.graphics.setBlendMode("add", "premultiplied") + love.graphics.draw(scene) -- original scene + + -- second pass of light blurring + blurshader:send('direction', {0, 1 / love.graphics.getHeight()}) + love.graphics.setShader(blurshader) + love.graphics.draw(back) + + -- restore things as they were before entering draw() + love.graphics.setBlendMode("alpha", "premultiplied") + scene = back + end + + return moonshine.Effect{ + name = "glow", + draw = draw, + setters = setters, + defaults = {min_luma=.7, strength = 5} + } +end |