From 62ff5245c26c305e35a2903cc64a60cb20718e96 Mon Sep 17 00:00:00 2001 From: Indrajith K L Date: Sun, 27 Feb 2022 01:15:31 +0530 Subject: Initial Commit * ECS - In-Progress * GameStates - Skeleton Implemented * Library Integrations - Completed * Levels - In-Progress --- libs/moonshine/fastgaussianblur.lua | 139 ++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 libs/moonshine/fastgaussianblur.lua (limited to 'libs/moonshine/fastgaussianblur.lua') diff --git a/libs/moonshine/fastgaussianblur.lua b/libs/moonshine/fastgaussianblur.lua new file mode 100644 index 0000000..364b766 --- /dev/null +++ b/libs/moonshine/fastgaussianblur.lua @@ -0,0 +1,139 @@ +--[[ +The MIT License (MIT) + +Copyright (c) 2017 Tim Moore +Adapted for new moonshine API by Matthias Richter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]]-- + +-- Bilinear Gaussian blur filter as detailed here: http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ +-- Produces near identical results to a standard Gaussian blur by using sub-pixel sampling, +-- this allows us to do ~1/2 the number of pixel lookups. + +-- unroll convolution loop +local function build_shader(taps, offset, offset_type, sigma) + taps = math.floor(taps) + sigma = sigma >= 1 and sigma or (taps - 1) * offset / 6 + sigma = math.max(sigma, 1) + + local steps = (taps + 1) / 2 + + -- Calculate gaussian function. + local g_offsets = {} + local g_weights = {} + for i = 1, steps, 1 do + g_offsets[i] = offset * (i - 1) + + -- We don't need to include the constant part of the gaussian function as we normalize later. + -- 1 / math.sqrt(2 * sigma ^ math.pi) * math.exp(-0.5 * ((offset - 0) / sigma) ^ 2 ) + g_weights[i] = math.exp(-0.5 * (g_offsets[i] - 0) ^ 2 * 1 / sigma ^ 2 ) + end + + -- Calculate offsets and weights for sub-pixel samples. + local offsets = {} + local weights = {} + for i = #g_weights, 2, -2 do + local oA, oB = g_offsets[i], g_offsets[i - 1] + local wA, wB = g_weights[i], g_weights[i - 1] + wB = oB == 0 and wB / 2 or wB -- On center tap the middle is getting sampled twice so half weight. + local weight = wA + wB + offsets[#offsets + 1] = offset_type == 'center' and (oA + oB) / 2 or (oA * wA + oB * wB) / weight + weights[#weights + 1] = weight + end + + local code = {[[ + extern vec2 direction; + vec4 effect(vec4 color, Image tex, vec2 tc, vec2 sc) {]]} + + local norm = 0 + if #g_weights % 2 == 0 then + code[#code+1] = 'vec4 c = vec4( 0.0 );' + else + local weight = g_weights[1] + norm = norm + weight + code[#code+1] = ('vec4 c = %f * texture2D(tex, tc);'):format(weight) + end + + local tmpl = 'c += %f * ( texture2D(tex, tc + %f * direction)+ texture2D(tex, tc - %f * direction));\n' + for i = 1, #offsets, 1 do + local offset = offsets[i] + local weight = weights[i] + norm = norm + weight * 2 + code[#code+1] = tmpl:format(weight, offset, offset) + end + code[#code+1] = ('return c * vec4(%f) * color; }'):format(1 / norm) + + local shader = table.concat(code) + return love.graphics.newShader(shader) +end + +return function(moonshine) + local taps, offset, offset_type, sigma = 7, 1, 'weighted', -1 + local shader = build_shader(taps, offset, offset_type, sigma) + + local function draw(buffer) + shader:send('direction', {1 / love.graphics.getWidth(), 0}) + moonshine.draw_shader(buffer, shader) + + shader:send('direction', {0, 1 / love.graphics.getHeight()}) + moonshine.draw_shader(buffer, shader) + end + + local setters = {} + + -- Number of effective samples to take per pass. e.g. 3-tap is the current pixel and the neighbors each side. + -- More taps = larger blur, but slower. + setters.taps = function(v) + assert(tonumber(v) >= 3, "Invalid value for `taps': Must be >= 3") + assert(tonumber(v)%2 == 1, "Invalid value for `taps': Must be odd") + taps = tonumber(v) + shader = build_shader(taps, offset, offset_type, sigma) + end + + -- Offset of each tap. + -- For highest quality this should be <=1 but if the image has low entropy we + -- can approximate the blur with a number > 1 and less taps, for better performance. + setters.offset = function(v) + offset = tonumber(v) or 0 + shader = build_shader(taps, offset, offset_type, sigma) + end + + -- Offset type, either 'weighted' or 'center'. + -- 'weighted' gives a more accurate gaussian decay but can introduce modulation + -- for high frequency details. + setters.offset_type = function(v) + assert(v == 'weighted' or v == 'center', "Invalid value for 'offset_type': Must be 'weighted' or 'center'.") + offset_type = v + shader = build_shader(taps, offset, offset_type, sigma) + end + + -- Sigma value for gaussian distribution. You don't normally need to set this. + setters.sigma = function(v) + sigma = tonumber(v) or -1 + shader = build_shader(taps, offset, offset_type, sigma) + end + + return moonshine.Effect{ + name = "fastgaussianblur", + draw = draw, + setters = setters, + -- no defaults here, as we dont want the shader to be built 3 times on startup + } +end -- cgit v1.2.3