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

@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 = imageResizedToScreen(readImage("Cargo Bot:Game Area")) 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 blurMeshes.horizontal:addRect(downsampleDimensions.x/2,downsampleDimensions.y/2, downsampleDimensions.x,downsampleDimensions.y) --fullSized image uses downsampled rect blurMeshes.horizontal.shader = shader(gaussianShader.vert[1],gaussianShader.frag) blurMeshes.horizontal.shader.am = aspect --vertical mesh settings blurMeshes.vertical.texture = blurImages.downsampled blurMeshes.vertical:addRect(fullDimensions.x/2,fullDimensions.y/2, fullDimensions.x,fullDimensions.y) --downsampled image uses fullSized rect blurMeshes.vertical.shader = shader(gaussianShader.vert[2],gaussianShader.frag) blurMeshes.vertical.shader.am = aspect --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 gaussianShader = { 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); }]], -- vertical pass vertex shader [[ 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); }]]}, --fragment shader 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:

Comments

  • AnatolyAnatoly Mod
    Posts: 877

    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
  • @hpsoft nice!
  • 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.

  • @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?
Sign In or Register to comment.