Last active
October 1, 2020 02:14
-
-
Save 1bardesign/a72192668a2b08d213088f3b9e66bf34 to your computer and use it in GitHub Desktop.
texture utilities including various downsamples, dilation, and blurring
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--texture utilities | |
--todo: clean up and include in https://github.com/1bardesign/batteries | |
--[[ | |
Copyright (c) 2020 Max Cahill | |
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. | |
]] | |
local downsample_template = [[ | |
uniform float scale_factor; | |
uniform vec2 big_res; | |
uniform vec2 small_res; | |
#ifdef PIXEL | |
vec4 effect(vec4 c, Image t, vec2 uv, vec2 px) { | |
vec2 big_uv_origin_px = (floor(uv * small_res) / small_res) * big_res + vec2(0.5); | |
vec4 accum = vec4(<init>); | |
for (float oy = 0; oy < scale_factor; oy++) { | |
for (float ox = 0; ox < scale_factor; ox++) { | |
vec2 ouv = (big_uv_origin_px + vec2(ox, oy)) / big_res; | |
vec4 px = Texel(t, ouv); | |
<op> | |
} | |
} | |
return <res>; | |
} | |
#endif | |
]] | |
local function create_downsample_shader(init, op, res) | |
local src = downsample_template | |
src = src:gsub("<init>", init) | |
src = src:gsub("<op>", op) | |
src = src:gsub("<res>", res) | |
return lg.newShader(src) | |
end | |
local downsample_shaders = { | |
average = create_downsample_shader( | |
"0.0", | |
"accum += px;", | |
"accum / (scale_factor * scale_factor)" | |
), | |
min = create_downsample_shader( | |
"100.0", | |
"accum = min(accum, px);", | |
"accum" | |
), | |
max = create_downsample_shader( | |
"-100.0", | |
"accum = max(accum, px);", | |
"accum" | |
), | |
} | |
function downsample_canvas(big, small, op) | |
local shader = downsample_shaders[op] | |
if not shader then | |
error("unknown op for downsample_canvas, " .. tostring(op)) | |
end | |
local scale_factor = math.ceil(big:getWidth() / small:getWidth()) | |
love.graphics.push("all") | |
love.graphics.setCanvas(small) | |
if scale_factor == 1 then | |
--just copy | |
love.graphics.setBlendMode("replace", "premultiplied") | |
love.graphics.draw(big) | |
love.graphics.pop() | |
return | |
end | |
local downscale_factor = 1 / scale_factor | |
shader:send("scale_factor", scale_factor) | |
shader:send("big_res", {big:getDimensions()}) | |
shader:send("small_res", {small:getDimensions()}) | |
love.graphics.setShader(shader) | |
love.graphics.draw(big, 0, 0, 0, downscale_factor, downscale_factor) | |
love.graphics.pop() | |
end | |
local dilate_shader = lg.newShader([[ | |
uniform vec2 res; | |
uniform float distance; | |
uniform bool do_min; | |
#ifdef PIXEL | |
vec4 effect(vec4 c, Image t, vec2 uv, vec2 px) { | |
vec4 colour = Texel(t, uv); | |
for (float oy = -distance; oy <= distance; oy++) { | |
for (float ox = -distance; ox <= distance; ox++) { | |
float a = 1.0; | |
vec2 o = vec2(ox, oy); | |
if (length(o) <= distance) { | |
o /= res; | |
if (do_min) { | |
colour = min(colour, Texel(t, uv + o)); | |
} else { | |
colour = max(colour, Texel(t, uv + o)); | |
} | |
} | |
} | |
} | |
return colour; | |
} | |
#endif | |
]]) | |
function dilate_canvas(from, to, distance, do_min) | |
dilate_shader:send("distance", distance) | |
dilate_shader:send("res", {from:getDimensions()}) | |
dilate_shader:send("do_min", do_min) | |
lg.push("all") | |
lg.setCanvas(to) | |
lg.setShader(dilate_shader) | |
lg.draw(from) | |
lg.pop() | |
return to | |
end | |
local blur_shader = lg.newShader([[ | |
uniform vec2 res; | |
uniform float steps; | |
uniform vec2 dimension; | |
uniform bool semi_gaussian; | |
#ifdef PIXEL | |
vec4 effect(vec4 c, Image t, vec2 uv, vec2 px) { | |
vec4 colour = vec4(0.0); | |
float total = 0.0; | |
for (float oi = -steps; oi <= steps; oi++) { | |
float a = 1.0; | |
vec2 o = dimension * oi; | |
if (semi_gaussian) { | |
a = mix(1.0, 0.0, length(o) / (steps + 1.0)); | |
} | |
if (a > 0) { | |
o /= res; | |
colour += Texel(t, uv + o) * a; | |
total += a; | |
} | |
} | |
return colour / total; | |
} | |
#endif | |
]]) | |
function blur_canvas(from, partial, to, steps, semi_gaussian) | |
blur_shader:send("steps", steps) | |
blur_shader:send("res", {from:getDimensions()}) | |
blur_shader:send("semi_gaussian", semi_gaussian) | |
lg.push("all") | |
lg.setShader(blur_shader) | |
blur_shader:send("dimension", {1, 0}) | |
lg.setCanvas(partial) | |
lg.draw(from) | |
blur_shader:send("dimension", {0, 1}) | |
lg.setCanvas(to) | |
lg.draw(partial) | |
lg.pop() | |
return to | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment