#### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

# Function to blur to any image

Posts: 1,827

@yojimbo2000 made a super-awesome Gaussian blur for his Soda tools.

I wanted it!

So I turned it into a stand-alone function that takes any image as a parameter and returns a blurred version of that image.

In case such a thing is of use to anyone else, here's the code:

``````--# Main
--blurFunction
--adapted by UberGoober from Yojimbo2000 from http://xissburg.com/faster-gaussian-blur-in-glsl/ and http://www.sunsetlakesoftware.com/2013/10/21/optimizing-gaussian-blurs-mobile-gpu

function setup()
preBlur = imageWithGaussian2PassBlur(preBlur)
end

function imageResizedToScreen(imageToResize)
local screenSizedImage = image(WIDTH,HEIGHT)
setContext(screenSizedImage)
sprite(imageToResize,WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
setContext()
return screenSizedImage
end

function draw()
sprite(preBlur, WIDTH/2,HEIGHT/2, WIDTH,HEIGHT)
end
--# imageWithGaussian2PassBlur
function imageWithGaussian2PassBlur(imageToBlur)
--aspect ratio for blurring with:
local largestSide = math.max(imageToBlur.width,imageToBlur.height)
local aspect = vec2(largestSide/imageToBlur.width, largestSide/imageToBlur.height) --should be inverse ratio?
--dimensions for fullSized and downsampled images
local downsampleAmount = 0.5 -- going down to 0.25 actually looks pretty good, but, weirdly, slower than 0.5
local fullDimensions = vec2(imageToBlur.width,imageToBlur.height)
local downsampleDimensions = vec2(imageToBlur.width*downsampleAmount,imageToBlur.height*downsampleAmount)
--images
local blurImages = {}
blurImages.fullSized = imageToBlur
blurImages.downsampled = image(downsampleDimensions.x,downsampleDimensions.y)
setContext(blurImages.downsampled)
sprite(imageToBlur,downsampleDimensions.x/2,downsampleDimensions.y/2,downsampleDimensions.x,downsampleDimensions.y)
setContext()
--meshes
local blurMeshes = {}
blurMeshes.horizontal = mesh()
blurMeshes.vertical = mesh()
--horizontal mesh settings
blurMeshes.horizontal.texture = blurImages.fullSized
downsampleDimensions.x,downsampleDimensions.y) --fullSized image uses downsampled rect
--vertical mesh settings
blurMeshes.vertical.texture = blurImages.downsampled
fullDimensions.x,fullDimensions.y) --downsampled image uses fullSized rect
--draw the blurred horizontal mesh to the vertical mesh texture
setContext(blurMeshes.vertical.texture)
blurMeshes.horizontal:draw() --pass one
setContext()
--draw the double-blurred vertical mesh to a new image
local renderTarget = image(imageToBlur.width,imageToBlur.height)
setContext(renderTarget)
blurMeshes.vertical:draw() --pass two
setContext()
--send back the blurred image
return renderTarget
end

vert = { -- horizontal pass vertex shader
[[
uniform mat4 modelViewProjection;
uniform vec2 am; // ammount of blur, inverse aspect ratio (so that oblong shapes still produce round blur)
attribute vec4 position;
attribute vec2 texCoord;

varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];

void main()
{
gl_Position = modelViewProjection * position;
vTexCoord = texCoord;
v_blurTexCoords[ 0] = vTexCoord + vec2(-0.028 * am.x, 0.0);
v_blurTexCoords[ 1] = vTexCoord + vec2(-0.024 * am.x, 0.0);
v_blurTexCoords[ 2] = vTexCoord + vec2(-0.020 * am.x, 0.0);
v_blurTexCoords[ 3] = vTexCoord + vec2(-0.016 * am.x, 0.0);
v_blurTexCoords[ 4] = vTexCoord + vec2(-0.012 * am.x, 0.0);
v_blurTexCoords[ 5] = vTexCoord + vec2(-0.008 * am.x, 0.0);
v_blurTexCoords[ 6] = vTexCoord + vec2(-0.004 * am.x, 0.0);
v_blurTexCoords[ 7] = vTexCoord + vec2( 0.004 * am.x, 0.0);
v_blurTexCoords[ 8] = vTexCoord + vec2( 0.008 * am.x, 0.0);
v_blurTexCoords[ 9] = vTexCoord + vec2( 0.012 * am.x, 0.0);
v_blurTexCoords[10] = vTexCoord + vec2( 0.016 * am.x, 0.0);
v_blurTexCoords[11] = vTexCoord + vec2( 0.020 * am.x, 0.0);
v_blurTexCoords[12] = vTexCoord + vec2( 0.024 * am.x, 0.0);
v_blurTexCoords[13] = vTexCoord + vec2( 0.028 * am.x, 0.0);
}]],
[[
uniform mat4 modelViewProjection;
uniform vec2 am; // ammount of blur
attribute vec4 position;
attribute vec2 texCoord;

varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];

void main()
{
gl_Position = modelViewProjection * position;
vTexCoord = texCoord;
v_blurTexCoords[ 0] = vTexCoord + vec2(0.0, -0.028 * am.y);
v_blurTexCoords[ 1] = vTexCoord + vec2(0.0, -0.024 * am.y);
v_blurTexCoords[ 2] = vTexCoord + vec2(0.0, -0.020 * am.y);
v_blurTexCoords[ 3] = vTexCoord + vec2(0.0, -0.016 * am.y);
v_blurTexCoords[ 4] = vTexCoord + vec2(0.0, -0.012 * am.y);
v_blurTexCoords[ 5] = vTexCoord + vec2(0.0, -0.008 * am.y);
v_blurTexCoords[ 6] = vTexCoord + vec2(0.0, -0.004 * am.y);
v_blurTexCoords[ 7] = vTexCoord + vec2(0.0,  0.004 * am.y);
v_blurTexCoords[ 8] = vTexCoord + vec2(0.0,  0.008 * am.y);
v_blurTexCoords[ 9] = vTexCoord + vec2(0.0,  0.012 * am.y);
v_blurTexCoords[10] = vTexCoord + vec2(0.0,  0.016 * am.y);
v_blurTexCoords[11] = vTexCoord + vec2(0.0,  0.020 * am.y);
v_blurTexCoords[12] = vTexCoord + vec2(0.0,  0.024 * am.y);
v_blurTexCoords[13] = vTexCoord + vec2(0.0,  0.028 * am.y);
}]]},
frag = [[precision mediump float;

uniform lowp sampler2D texture;

varying vec2 vTexCoord;
varying vec2 v_blurTexCoords[14];

void main()
{
gl_FragColor = vec4(0.0);
gl_FragColor += texture2D(texture, v_blurTexCoords[ 0])*0.0044299121055113265;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 1])*0.00895781211794;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 2])*0.0215963866053;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 3])*0.0443683338718;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 4])*0.0776744219933;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 5])*0.115876621105;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 6])*0.147308056121;
gl_FragColor += texture2D(texture, vTexCoord         )*0.159576912161;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 7])*0.147308056121;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 8])*0.115876621105;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 9])*0.0776744219933;
gl_FragColor += texture2D(texture, v_blurTexCoords[10])*0.0443683338718;
gl_FragColor += texture2D(texture, v_blurTexCoords[11])*0.0215963866053;
gl_FragColor += texture2D(texture, v_blurTexCoords[12])*0.00895781211794;
gl_FragColor += texture2D(texture, v_blurTexCoords[13])*0.0044299121055113265;
}]]
}

``````
Tagged:

• Mod
Posts: 894

Nice background!

• Posts: 257

Thanks @UberGoober ,

you can see the difference with sprite without blur

``````function draw()
sprite(preBlur, WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
sprite("Cargo Bot:Game Area",0,HEIGHT/2,WIDTH/1.1,HEIGHT)
end

``````
• Posts: 1,827
@hpsoft nice!
• Mod
Posts: 2,020

Good work! It's really nice to see code being recycled and re-worked.

One thing I noticed (this is probably an issue in my original), when removing the width and height from the Sprite command in draw, to see it at its regular resolution, is that the top of the image is sometimes clipped (I tried it with the rocket from planet cute). If you're just using it for full screen output, you wouldn't notice it. I guess it's because the blur actually makes the image larger. Maybe making the output image a bit larger than the input would fix this.

• Posts: 1,827
@yojimbo2000 the choices seem to be a) return an image of different dimensions than the one passed in, or b) return an image with the same dimensions but with the content shrunk to fit them.

If the drawn size is set dynamically, it comes to the same thing. If not, my question is how (and should) the user get notified that their image has been altered? Using this function without being aware of that could lead to all sorts of hair-pulling during debugging, so I think they definitely *should* be made aware of it somehow. Is a comment in the code enough? I'd prefer a notification that's unavoidable, so that the user is made aware of it just by using the code. Maybe in the function name itself?