Howdy, Stranger!

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

Blur Shader Needed.

edited May 2015 in Shaders Posts: 216

Hi, I'm looking for a good blur shader, something similar to the blur you get in the iOS Notification Center or OS X Yosemite. The blur shader included doesn't really do what I want. Also, preferably something non GPU intensive, as I have a lot of other stuff going on in the background (Even if it is GPU intensive, id still love to see it and test it) (also, preferably something with an open licence, as this will be used in a game that will most likely be on the App Store in the next couple of months). The background will constantly be changing, as this will be a background for a store which is "on top" or the menu screen which has several effects in it.

Comments

  • Posts: 2,020

    Most of the examples I've seen online require 2 passes. It also seems that you'd have to render the underlying scene to an image, and then pass that image as a texture to the shader. I don't know what the performance of that would be like if you were doing it every frame. This one looks interesting:

    http://xissburg.com/faster-gaussian-blur-in-glsl/

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Mr_Ninja

    the fastest method may be to create an overlay image which can be sprited over the drawn screen, creating the impression of a blur. This is much faster than creating a blur in real time (and does the difference really matter to users?).

    The code below demonstrates. Touch the screen to toggle the "blur" on and off. You can adjust the light drop off towards the edges with this line in the shader: f = fff; (the more you mutiply f by itself, the faster the light drops off, and vice versa)

    function setup()
        --create "blur" image using shader
        blurImg=image(WIDTH,HEIGHT)
        setContext(blurImg)
        local m=mesh()
        m:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
        m.shader=shader(BlurShader.vertexShader,BlurShader.fragmentShader)
        m.shader.centre=vec2(WIDTH/2,HEIGHT/2)
        m:draw()
        setContext()
       --image completed
        showBlur=false --blur toggle
    end
    
    function draw()
        background(50) 
       --draw something on the screen
        sprite("SpaceCute:Rocketship",WIDTH-200,HEIGHT-200)
        sprite("Small World:Store Extra Large",200,200,300)
        sprite("SpaceCute:Beetle Ship",WIDTH/2,HEIGHT/2)
        if showBlur then --draw blur if required
            sprite(img,WIDTH/2,HEIGHT/2) 
            sprite("Cargo Bot:Codea Icon",WIDTH/2,HEIGHT/2)
        end
    end
    
    --touching toggles blur on and off
    function touched(t)
        if t.state==ENDED then showBlur=not showBlur end
    end
    
    --blur shader
    BlurShader = {
    vertexShader = [[
    uniform mat4 modelViewProjection;
    attribute vec4 position;
    attribute vec4 color;
    varying highp vec4 vPosition;
    
    void main()
    {
        gl_Position = modelViewProjection * position;
        vPosition =  position;
    }
    ]],
    fragmentShader = [[
    precision highp float;
    uniform vec2 centre;
    varying highp vec4 vPosition;
    
    float L = sqrt(centre.x*centre.x+centre.y*centre.y)*1.4;
    
    void main()
    {
        float f=1.0 - distance(vPosition.xy,centre)/L;
        f=f*f*f;
        gl_FragColor = vec4(f,f,f,1.0-f);
    }
    ]]}
    
  • Posts: 2,020

    Here's a Gaussian blur shader. It looks great, but on an iPad Air, the FPS drops to 35 with two passes. I haven't tried optimising it, but I'm sure the bottleneck is sending two screen-sized images as textures to the shader, rather than the calculations in the shader itself. i.e. I'm not sure that reducing the number of samples in the shader from 14 would necessarily help. Could be worth trying?


    --# Main --Gaussian blur --adapted by Yojimbo2000 from http://xissburg.com/faster-gaussian-blur-in-glsl/ function setup() blur = {} --images blurred = {} --meshes for i=1,2 do --2 passes, one for horizontal, one vertical blur[i]=image(WIDTH,HEIGHT) blurred[i]=mesh() blurred[i].texture=blur[i] blurred[i]:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) blurred[i].shader=shader(Gaussian.vs[i], Gaussian.fs) end unblurred=mesh() --mesh w/o the blur shader unblurred.texture=blur[1] unblurred:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) showBlur=false --blur toggle pos={x1=200, y2=200, x3=800} tween(2, pos, {x1=800, y2=800, x3=200}, {easing=tween.easing.cubicInOut, loop=tween.loop.pingpong}) movement = true profiler.init() end function draw() if movement then setContext(blur[1]) background(50) sprite("SpaceCute:Rocketship",pos.x1,HEIGHT-200) sprite("Small World:Store Extra Large",pos.x3,200,300) sprite("SpaceCute:Beetle Ship",WIDTH/2,pos.y2) end if showBlur then --draw blur if required setContext(blur[2]) blurred[1]:draw() --pass one, offscreen setContext() blurred[2]:draw() --pass two else setContext() unblurred:draw() end profiler.draw() end --touching toggles blur on and off function touched(t) if t.state==ENDED then showBlur=not showBlur end end profiler={} function profiler.init(quiet) profiler.del=0 profiler.c=0 profiler.fps=0 profiler.mem=0 if not quiet then parameter.watch("profiler.fps") parameter.watch("profiler.mem") end end function profiler.draw() profiler.del = profiler.del + DeltaTime profiler.c = profiler.c + 1 if profiler.c==10 then profiler.fps=profiler.c/profiler.del profiler.del=0 profiler.c=0 profiler.mem=collectgarbage("count", 2) end end --# Gaussian Gaussian = { vs = {[[ uniform mat4 modelViewProjection; 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, 0.0); v_blurTexCoords[ 1] = vTexCoord + vec2(-0.024, 0.0); v_blurTexCoords[ 2] = vTexCoord + vec2(-0.020, 0.0); v_blurTexCoords[ 3] = vTexCoord + vec2(-0.016, 0.0); v_blurTexCoords[ 4] = vTexCoord + vec2(-0.012, 0.0); v_blurTexCoords[ 5] = vTexCoord + vec2(-0.008, 0.0); v_blurTexCoords[ 6] = vTexCoord + vec2(-0.004, 0.0); v_blurTexCoords[ 7] = vTexCoord + vec2( 0.004, 0.0); v_blurTexCoords[ 8] = vTexCoord + vec2( 0.008, 0.0); v_blurTexCoords[ 9] = vTexCoord + vec2( 0.012, 0.0); v_blurTexCoords[10] = vTexCoord + vec2( 0.016, 0.0); v_blurTexCoords[11] = vTexCoord + vec2( 0.020, 0.0); v_blurTexCoords[12] = vTexCoord + vec2( 0.024, 0.0); v_blurTexCoords[13] = vTexCoord + vec2( 0.028, 0.0); }]], [[ uniform mat4 modelViewProjection; 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); v_blurTexCoords[ 1] = vTexCoord + vec2(0.0, -0.024); v_blurTexCoords[ 2] = vTexCoord + vec2(0.0, -0.020); v_blurTexCoords[ 3] = vTexCoord + vec2(0.0, -0.016); v_blurTexCoords[ 4] = vTexCoord + vec2(0.0, -0.012); v_blurTexCoords[ 5] = vTexCoord + vec2(0.0, -0.008); v_blurTexCoords[ 6] = vTexCoord + vec2(0.0, -0.004); v_blurTexCoords[ 7] = vTexCoord + vec2(0.0, 0.004); v_blurTexCoords[ 8] = vTexCoord + vec2(0.0, 0.008); v_blurTexCoords[ 9] = vTexCoord + vec2(0.0, 0.012); v_blurTexCoords[10] = vTexCoord + vec2(0.0, 0.016); v_blurTexCoords[11] = vTexCoord + vec2(0.0, 0.020); v_blurTexCoords[12] = vTexCoord + vec2(0.0, 0.024); v_blurTexCoords[13] = vTexCoord + vec2(0.0, 0.028); }]]}, fs = [[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; }]] }
  • IgnatzIgnatz Mod
    Posts: 5,396

    Personally, I wouldn't go to a lot of effort just to blur the background that nobody will notice anyway :)

  • Posts: 2,020

    Well, it's what the OP asked for :-)

    I was wrong about optimisation: if you comment out half the calculations in the fragment shader, it runs at about 58 fps, and still looks acceptably blurry. I'll have to recalculate what the brightness values should be. I can post an optimised version later

  • Posts: 2,020

    Plus, with all of these things, it's often the things you discover along the way. I'm interested in this because I'm currently exploring the possibility of creating an underwater ripple effect for a platform game I'm working on, which would be the same principle of sending the entire screen to an effect shader.

    One really fun thing I just discovered: if you comment out every other line in the fragment shader above, you get a really cool after-image trail as the sprites move. With further adaptions so that the blur only goes in one direction, it would be a great effect

  • Posts: 2,020

    Ok, here's an optimised version. Tap to cycle through 3 states, no blur, sideways trails, 2-pass Gaussian. This runs at 59/60 fps on the Air:


    --# Main --Gaussian blur --adapted by Yojimbo2000 from http://xissburg.com/faster-gaussian-blur-in-glsl/ function setup() --2 pass Gaussian blur: blur = {} --images blurred = {} --meshes for i=1,2 do --2 passes, one for horizontal, one vertical blur[i]=image(WIDTH,HEIGHT) blurred[i]=mesh() blurred[i].texture=blur[i] blurred[i]:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) blurred[i].shader=shader(Gaussian.vs[i], Gaussian.fsO) end --mesh w/o the blur shader: unblurred=mesh() unblurred.texture=blur[1] unblurred:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) --mesh with sideways after images: sideTrails=mesh() sideTrails.texture=blur[1] sideTrails.shader=shader(Gaussian.vs[1], Gaussian.fsTrails) --try Gaussian.fsTesselated for a different effect sideTrails:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) showBlur=1 --blur state --some animations: pos={x1=200, y2=200, x3=800} tween(2, pos, {x1=800, y2=800, x3=200}, {easing=tween.easing.cubicInOut, loop=tween.loop.pingpong}) movement = true profiler.init() print ("tap screen to cycle through blur states") end function draw() if movement then setContext(blur[1]) background(50) sprite("SpaceCute:Rocketship",pos.x1,HEIGHT-200) sprite("Small World:Store Extra Large",pos.x3,200,300) sprite("SpaceCute:Beetle Ship",WIDTH/2,pos.y2) end if showBlur==1 then --draw blur if required setContext() background(50) unblurred:draw() elseif showBlur==2 then setContext() -- background(50) sideTrails:draw() elseif showBlur==3 then setContext(blur[2]) -- background(50) --hard to tell whether these background calls make a difference blurred[1]:draw() --pass one, offscreen setContext() -- background(50) blurred[2]:draw() --pass two end profiler.draw() end --touching toggles blur on and off local blurStates = {"no blur", "1 pass, horizontal trails", "2 pass Gaussian blur, optimized"} function touched(t) if t.state==ENDED then showBlur = showBlur + 1 if showBlur==4 then showBlur=1 end output.clear() print(blurStates[showBlur]) end end profiler={} function profiler.init(quiet) profiler.del=0 profiler.c=0 profiler.fps=0 profiler.mem=0 if not quiet then parameter.watch("profiler.fps") parameter.watch("profiler.mem") end end function profiler.draw() profiler.del = profiler.del + DeltaTime profiler.c = profiler.c + 1 if profiler.c==10 then profiler.fps=profiler.c/profiler.del profiler.del=0 profiler.c=0 profiler.mem=collectgarbage("count", 2) end end --# Gaussian Gaussian = { vs = { -- horizontal pass vertex shader [[ uniform mat4 modelViewProjection; 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, 0.0); v_blurTexCoords[ 1] = vTexCoord + vec2(-0.024, 0.0); v_blurTexCoords[ 2] = vTexCoord + vec2(-0.020, 0.0); v_blurTexCoords[ 3] = vTexCoord + vec2(-0.016, 0.0); v_blurTexCoords[ 4] = vTexCoord + vec2(-0.012, 0.0); v_blurTexCoords[ 5] = vTexCoord + vec2(-0.008, 0.0); v_blurTexCoords[ 6] = vTexCoord + vec2(-0.004, 0.0); v_blurTexCoords[ 7] = vTexCoord + vec2( 0.004, 0.0); v_blurTexCoords[ 8] = vTexCoord + vec2( 0.008, 0.0); v_blurTexCoords[ 9] = vTexCoord + vec2( 0.012, 0.0); v_blurTexCoords[10] = vTexCoord + vec2( 0.016, 0.0); v_blurTexCoords[11] = vTexCoord + vec2( 0.020, 0.0); v_blurTexCoords[12] = vTexCoord + vec2( 0.024, 0.0); v_blurTexCoords[13] = vTexCoord + vec2( 0.028, 0.0); }]], -- vertical pass vertex shader [[ uniform mat4 modelViewProjection; 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); v_blurTexCoords[ 1] = vTexCoord + vec2(0.0, -0.024); v_blurTexCoords[ 2] = vTexCoord + vec2(0.0, -0.020); v_blurTexCoords[ 3] = vTexCoord + vec2(0.0, -0.016); v_blurTexCoords[ 4] = vTexCoord + vec2(0.0, -0.012); v_blurTexCoords[ 5] = vTexCoord + vec2(0.0, -0.008); v_blurTexCoords[ 6] = vTexCoord + vec2(0.0, -0.004); v_blurTexCoords[ 7] = vTexCoord + vec2(0.0, 0.004); v_blurTexCoords[ 8] = vTexCoord + vec2(0.0, 0.008); v_blurTexCoords[ 9] = vTexCoord + vec2(0.0, 0.012); v_blurTexCoords[10] = vTexCoord + vec2(0.0, 0.016); v_blurTexCoords[11] = vTexCoord + vec2(0.0, 0.020); v_blurTexCoords[12] = vTexCoord + vec2(0.0, 0.024); v_blurTexCoords[13] = vTexCoord + vec2(0.0, 0.028); }]]}, --optimised fragment shader fsO = [[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[ 5])* 0.08; //0.169179866813; //0.115876621105; gl_FragColor += texture2D(texture, v_blurTexCoords[ 6])* 0.26; //0.215069761937; //0.147308056121; gl_FragColor += texture2D(texture, vTexCoord )* 0.32; //0.232982291755; //0.159576912161; gl_FragColor += texture2D(texture, v_blurTexCoords[ 7])* 0.26; //0.215069761937; //0.147308056121; gl_FragColor += texture2D(texture, v_blurTexCoords[ 8])* 0.08; //0.169179866813; //0.115876621105; }]], --fragment shader --awesome after-image trails! fsTrails = [[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; }]], --fragment shader. tesselated frosted glass effect (also try commenting out 3-10) fsTesselated = [[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; }]] }
  • Posts: 2,020

    On the above code,the weightings aren't right (I made them up). This blog uses the same technique as mine (ie 5 readings, fragment interpolation). I might see if I can copy their weightings. You could then emulate the iOS 7 panel effect, fullscreen, 60 fps.

    http://www.sunsetlakesoftware.com/2013/10/21/optimizing-gaussian-blurs-mobile-gpu

  • IgnatzIgnatz Mod
    Posts: 5,396

    You must have a fast iPad!

    My iPad 3 runs your code with one pass at 40 FPS, two passes at 30 FPS.

  • Posts: 2,020

    @Ignatz (or anyone else with an iPad 3), try this one. It downsamples by half, meaning there are a quarter as many calculations in the fragment shader. Because OpenGL up scaling adds a blurring effect anyway (as long as you don't invoke noSmooth()), the visual difference between this and full-pixel calculation is unnoticeable, but it should be a lot faster:


    --# Main --Gaussian blur --adapted by Yojimbo2000 from http://xissburg.com/faster-gaussian-blur-in-glsl/ function setup() --2 pass Gaussian blur: local downSample = 0.5 local dimensions = { vec2(WIDTH, HEIGHT), --full size vec2(WIDTH, HEIGHT) * downSample, --down sampled vec2(WIDTH, HEIGHT) * 0.5, --centre of fullsize (for positioning rect) vec2(WIDTH, HEIGHT) * downSample * 0.5, --centre of downsampled } blur = {} --images blurred = {} --meshes for i=1,2 do --2 passes, one for horizontal, one vertical blur[i]=image(dimensions[i].x, dimensions[i].y) --image 1 is full sized, image 2 is downsampled blurred[i]=mesh() blurred[i].texture=blur[i] local j=3-i --invert i so that... blurred[i]:addRect(dimensions[j+2].x, dimensions[j+2].y, dimensions[j].x, dimensions[j].y) --mesh 1 rect is down-sampled, mesh 2 rect is full-sized blurred[i].shader=shader(Gaussian.vs[i], Gaussian.fsO) end --mesh w/o the blur shader: unblurred=mesh() unblurred.texture=blur[1] unblurred:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) --mesh with sideways after images: sideTrails=mesh() sideTrails.texture=blur[1] sideTrails.shader=shader(Gaussian.vs[1], Gaussian.fsTrails) --try Gaussian.fsTesselated for a different effect sideTrails:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) showBlur=1 --blur state --some animations: pos={x1=200, y2=200, x3=800} tween(2, pos, {x1=800, y2=800, x3=200}, {easing=tween.easing.cubicInOut, loop=tween.loop.pingpong}) movement = true profiler.init() print ("tap screen to cycle through blur states") end function draw() if movement then setContext(blur[1]) background(50) sprite("SpaceCute:Rocketship",pos.x1,HEIGHT-200) sprite("Small World:Store Extra Large",pos.x3,200,300) sprite("SpaceCute:Beetle Ship",WIDTH/2,pos.y2) end if showBlur==1 then --draw blur if required setContext() background(50) unblurred:draw() elseif showBlur==2 then setContext() -- background(50) sideTrails:draw() elseif showBlur==3 then setContext(blur[2]) -- background(50) --hard to tell whether these background calls make a difference blurred[1]:draw() --pass one, offscreen setContext() -- background(50) blurred[2]:draw() --pass two end profiler.draw() end --touching toggles blur on and off local blurStates = {"no blur", "1 pass, horizontal trails", "2 pass Gaussian blur, optimized"} function touched(t) if t.state==ENDED then showBlur = showBlur + 1 if showBlur==4 then showBlur=1 end output.clear() print(blurStates[showBlur]) end end profiler={} function profiler.init(quiet) profiler.del=0 profiler.c=0 profiler.fps=0 profiler.mem=0 if not quiet then parameter.watch("profiler.fps") parameter.watch("profiler.mem") end end function profiler.draw() profiler.del = profiler.del + DeltaTime profiler.c = profiler.c + 1 if profiler.c==10 then profiler.fps=profiler.c/profiler.del profiler.del=0 profiler.c=0 profiler.mem=collectgarbage("count", 2) end end --# Gaussian Gaussian = { vs = { -- horizontal pass vertex shader [[ uniform mat4 modelViewProjection; 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, 0.0); v_blurTexCoords[ 1] = vTexCoord + vec2(-0.024, 0.0); v_blurTexCoords[ 2] = vTexCoord + vec2(-0.020, 0.0); v_blurTexCoords[ 3] = vTexCoord + vec2(-0.016, 0.0); v_blurTexCoords[ 4] = vTexCoord + vec2(-0.012, 0.0); v_blurTexCoords[ 5] = vTexCoord + vec2(-0.008, 0.0); v_blurTexCoords[ 6] = vTexCoord + vec2(-0.004, 0.0); v_blurTexCoords[ 7] = vTexCoord + vec2( 0.004, 0.0); v_blurTexCoords[ 8] = vTexCoord + vec2( 0.008, 0.0); v_blurTexCoords[ 9] = vTexCoord + vec2( 0.012, 0.0); v_blurTexCoords[10] = vTexCoord + vec2( 0.016, 0.0); v_blurTexCoords[11] = vTexCoord + vec2( 0.020, 0.0); v_blurTexCoords[12] = vTexCoord + vec2( 0.024, 0.0); v_blurTexCoords[13] = vTexCoord + vec2( 0.028, 0.0); }]], -- vertical pass vertex shader [[ uniform mat4 modelViewProjection; 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); v_blurTexCoords[ 1] = vTexCoord + vec2(0.0, -0.024); v_blurTexCoords[ 2] = vTexCoord + vec2(0.0, -0.020); v_blurTexCoords[ 3] = vTexCoord + vec2(0.0, -0.016); v_blurTexCoords[ 4] = vTexCoord + vec2(0.0, -0.012); v_blurTexCoords[ 5] = vTexCoord + vec2(0.0, -0.008); v_blurTexCoords[ 6] = vTexCoord + vec2(0.0, -0.004); v_blurTexCoords[ 7] = vTexCoord + vec2(0.0, 0.004); v_blurTexCoords[ 8] = vTexCoord + vec2(0.0, 0.008); v_blurTexCoords[ 9] = vTexCoord + vec2(0.0, 0.012); v_blurTexCoords[10] = vTexCoord + vec2(0.0, 0.016); v_blurTexCoords[11] = vTexCoord + vec2(0.0, 0.020); v_blurTexCoords[12] = vTexCoord + vec2(0.0, 0.024); v_blurTexCoords[13] = vTexCoord + vec2(0.0, 0.028); }]]}, --optimised fragment shader fsO = [[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[ 5])* 0.08; //0.169179866813; //0.115876621105; gl_FragColor += texture2D(texture, v_blurTexCoords[ 6])* 0.26; //0.215069761937; //0.147308056121; gl_FragColor += texture2D(texture, vTexCoord )* 0.32; //0.232982291755; //0.159576912161; gl_FragColor += texture2D(texture, v_blurTexCoords[ 7])* 0.26; //0.215069761937; //0.147308056121; gl_FragColor += texture2D(texture, v_blurTexCoords[ 8])* 0.08; //0.169179866813; //0.115876621105; }]], --fragment shader --awesome after-image trails! fsTrails = [[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; }]], --fragment shader. tesselated frosted glass effect (also try commenting out 3-10) fsTesselated = [[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; }]] }
  • Posts: 2,020

    Ok, last one I promise. This one lets you set varying amounts of horizontal and vertical blur, and has better weighting of the pixels. I'm not sure, but it could be that the downsampling only quarters the number of calls to the fragment shader on the first pass, but it depends on how OpenGL handles upscaling (ie does it call the fragment shader for the number of pixels in the source, or in the destination, when upscaling?). So it's possible that we're getting five-eighths the number of calculations, rather than a quarter. I can't really check this because it all runs at 60fps for me.


    --# Main --Gaussian blur --adapted by 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() --2 pass Gaussian blur: local downSample = 0.5 -- going down to 0.25 actually looks pretty good! local dimensions = { vec2(WIDTH, HEIGHT), --full size vec2(WIDTH, HEIGHT) * downSample --down sampled } local blurRadii = { vec2(0.002,0), --horizontal pass, increase x value for more horizontal blur vec2(0,0.002) --vertical pass, increase y value for more vertical blur } blur = {} --images blurred = {} --meshes for i=1,2 do --2 passes, one for horizontal, one vertical blur[i]=image(dimensions[i].x, dimensions[i].y) --image 1 is full sized, image 2 is downsampled blurred[i]=mesh() blurred[i].texture=blur[i] local j=3-i --invert i so that... blurred[i]:addRect(dimensions[j].x/2, dimensions[j].y/2, dimensions[j].x, dimensions[j].y) --mesh 1 rect is down-sampled, mesh 2 rect is full-sized blurred[i].shader=shader(Gaussian.vs, Gaussian.fs) blurred[i].shader.blurRadius=blurRadii[i] end --mesh w/o the blur shader: unblurred=mesh() unblurred.texture=blur[1] unblurred:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) showBlur=false --blur state --some animations: pos={x1=200, y2=200, x3=800} tween(2, pos, {x1=800, y2=800, x3=200}, {easing=tween.easing.cubicInOut, loop=tween.loop.pingpong}) movement = true profiler.init() print ("tap screen to toggle blur") end function draw() if movement then setContext(blur[1]) background(50) sprite("SpaceCute:Rocketship",pos.x1,HEIGHT-200) sprite("Small World:Store Extra Large",pos.x3,200,300) sprite("SpaceCute:Beetle Ship",WIDTH/2,pos.y2) end if showBlur then --draw blur if required setContext(blur[2]) -- background(50) --nice after-image effect if you dont clear the intermediary layer blurred[1]:draw() --pass one, offscreen setContext() blurred[2]:draw() --pass two, onscreen else setContext() -- background(50) --doesn't seem to be necesary to clear screen, for some reason unblurred:draw() end profiler.draw() end --touching toggles blur on and off function touched(t) if t.state==ENDED then showBlur = not showBlur end end profiler={} function profiler.init(quiet) profiler.del=0 profiler.c=0 profiler.fps=0 profiler.mem=0 if not quiet then parameter.watch("profiler.fps") parameter.watch("profiler.mem") end end function profiler.draw() profiler.del = profiler.del + DeltaTime profiler.c = profiler.c + 1 if profiler.c==10 then profiler.fps=profiler.c/profiler.del profiler.del=0 profiler.c=0 profiler.mem=collectgarbage("count", 2) end end --# Gaussian Gaussian = {vs = [[ uniform mat4 modelViewProjection; uniform vec2 blurRadius; attribute vec4 position; attribute vec2 texCoord; varying vec2 vBlurCoords[5]; void main() { gl_Position = modelViewProjection * position; vBlurCoords[0] = texCoord; vBlurCoords[1] = texCoord + blurRadius * 1.407333; vBlurCoords[2] = texCoord - blurRadius * 1.407333; vBlurCoords[3] = texCoord + blurRadius * 3.294215; vBlurCoords[4] = texCoord - blurRadius * 3.294215; } ]], --optimised fragment shader fs = [[ precision mediump float; uniform lowp sampler2D texture; varying vec2 vBlurCoords[5]; void main() { // gl_FragColor = vec4(0.0); gl_FragColor = texture2D(texture, vBlurCoords[ 0]) * 0.304005; gl_FragColor += texture2D(texture, vBlurCoords[ 1])* 0.204164; gl_FragColor += texture2D(texture, vBlurCoords[ 2])* 0.204164; gl_FragColor += texture2D(texture, vBlurCoords[ 3])* 0.093913; gl_FragColor += texture2D(texture, vBlurCoords[ 4])* 0.093913; }]]}
  • edited May 2015 Posts: 2,020

    Ok, I lied in my previous post, this really is the last one! I added a tween to the x and y radii of the blur to create a fun, "drunken insect eye" effect. Have a play, and let me know your fps (Added it to Codea Community) :


    --# Main --Gaussian blur --adapted by 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() --2 pass Gaussian blur: local downSample = 0.5 -- going down to 0.25 actually looks pretty good! local dimensions = { vec2(WIDTH, HEIGHT), --full size vec2(WIDTH, HEIGHT) * downSample --down sampled } blurRadii = { vec2(0.002,0), --horizontal pass, increase x value for more horizontal blur vec2(0,0.002) --vertical pass, increase y value for more vertical blur } blur = {} --images blurred = {} --meshes for i=1,2 do --2 passes, one for horizontal, one vertical blur[i]=image(dimensions[i].x, dimensions[i].y) --image 1 is full sized, image 2 is downsampled blurred[i]=mesh() blurred[i].texture=blur[i] local j=3-i --invert i so that... blurred[i]:addRect(dimensions[j].x/2, dimensions[j].y/2, dimensions[j].x, dimensions[j].y) --mesh 1 rect is down-sampled, mesh 2 rect is full-sized (ie, opposite of their images) blurred[i].shader=shader(Gaussian.vs, Gaussian.fs) blurred[i].shader.blurRadius=blurRadii[i] end --mesh w/o the blur shader: unblurred=mesh() unblurred.texture=blur[1] unblurred:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) showBlur=1 --blur state --some animations: pos={x1=200, y2=200, x3=800} radii={r1 = vec2(0.03,0.01), r2=vec2(-0.01,-0.03)} tween(2, pos, {x1=800, y2=800, x3=200}, {easing=tween.easing.cubicInOut, loop=tween.loop.pingpong}) tween(3, radii, {r1 = vec2(-0.03,-0.01), r2=vec2(0.01,0.03)}, {easing=tween.easing.sineInOut, loop=tween.loop.pingpong}) movement = true profiler.init() print ("tap screen to cycle blur effects") end function draw() if movement then setContext(blur[1]) background(50) sprite("SpaceCute:Rocketship",pos.x1,HEIGHT-200) sprite("Small World:Store Extra Large",pos.x3,200,300) sprite("SpaceCute:Beetle Ship",WIDTH/2,pos.y2) end if showBlur<3 then --draw blur if required if showBlur==2 then blurred[1].shader.blurRadius = radii.r1 blurred[2].shader.blurRadius = radii.r2 else blurred[1].shader.blurRadius = blurRadii[1] blurred[2].shader.blurRadius = blurRadii[2] end setContext(blur[2]) -- background(50) --nice after-image effect if you dont clear the intermediary layer blurred[1]:draw() --pass one, offscreen setContext() blurred[2]:draw() --pass two, onscreen elseif showBlur==3 then setContext() -- background(50) --doesn't seem to be necesary to clear screen, for some reason unblurred:draw() end profiler.draw() end --touching toggles blur on and off function touched(t) if t.state==ENDED then showBlur = showBlur + 1 if showBlur==4 then showBlur=1 end end end profiler={} function profiler.init(quiet) profiler.del=0 profiler.c=0 profiler.fps=0 profiler.mem=0 if not quiet then parameter.watch("profiler.fps") parameter.watch("profiler.mem") end end function profiler.draw() profiler.del = profiler.del + DeltaTime profiler.c = profiler.c + 1 if profiler.c==10 then profiler.fps=profiler.c/profiler.del profiler.del=0 profiler.c=0 profiler.mem=collectgarbage("count", 2) end end --# Gaussian Gaussian = {vs = [[ uniform mat4 modelViewProjection; uniform vec2 blurRadius; attribute vec4 position; attribute vec2 texCoord; varying vec2 vBlurCoords[5]; void main() { gl_Position = modelViewProjection * position; vBlurCoords[0] = texCoord; vBlurCoords[1] = texCoord + blurRadius * 1.407333; vBlurCoords[2] = texCoord - blurRadius * 1.407333; vBlurCoords[3] = texCoord + blurRadius * 3.294215; vBlurCoords[4] = texCoord - blurRadius * 3.294215; } ]], --optimised fragment shader fs = [[ precision mediump float; uniform lowp sampler2D texture; varying vec2 vBlurCoords[5]; void main() { // gl_FragColor = vec4(0.0); gl_FragColor = texture2D(texture, vBlurCoords[ 0]) * 0.304005; gl_FragColor += texture2D(texture, vBlurCoords[ 1])* 0.204164; gl_FragColor += texture2D(texture, vBlurCoords[ 2])* 0.204164; gl_FragColor += texture2D(texture, vBlurCoords[ 3])* 0.093913; gl_FragColor += texture2D(texture, vBlurCoords[ 4])* 0.093913; }]]}
  • Posts: 1,976

    I think that what Notification Center does (it wasn't made in Codea, but if it was) is draw the screen into a small image, then scale that up and display the blurred result. I know it might create some odd effects when things are moving on the blurred screen, but the same happens in the real Notification Center. Try scrolling up this post, and while it's moving, drag down Notification Center.

  • Posts: 2,020

    Yes, I think so. That's what my code does. Change the downsamples variable to affect how small the intermediary image is.

  • Posts: 216

    Wow, thanks for all this! I just came to check, and saw all the code! Not sure which method will work best for me, but I'll definantly look into all of them. Again, thanks a ton!

  • Posts: 2,020

    With my ones, I think you can just look at the last one.

  • Posts: 216

    Ok, will do

  • IgnatzIgnatz Mod
    Posts: 5,396

    @yojimbo2000 - thats better, about 55 now

  • Posts: 2,020

    @Ignatz cool, glad to hear that. I put the downsampling code (from my last entry) into my first entry at the top of this thread, the one that samples the texture 15 times per fragment, to see if I could get that up to speed on the iPad Air. At full-pixel it runs at around 35 fps, downsampling by 0.5, at 50 fps. Interestingly though, if you go down to 0.25, it goes at about 40 fps or so. So presumably there's a point at which too big a downscale/ upscale operation cancels out the benefit of quartering the number of calls to the fragment shader. In this particular case, downsampling by 0.5 seems to be optimal. You could also investigate scaling all of the draw operations too (of the 3 sprites I mean), see if that's quicker than drawing a 0.5 image of the whole scene.

  • Posts: 257

    Very good work and thanks for sharing.
    I like v5 with kaleiodoscopic effect
    I would like to create a face recognition shader ( with camera ) for my ia project
    but it's very difficult for me

  • Posts: 1,976

    @hpsoft I don't think you could do that with a shader. Your best bet would to just use image.get.

  • Posts: 216

    hmmm, after a bunch of testing, the only shader that worked for me (although only at 20 fps) was the first one by @yojimbo2000. It seems to work good for my needs, and he blur looks good. The other ones when I tried them just worked similarly to the blur shader that comes with Codea; a version of the image appeared a little bit off to all four corners of the image. In the demo program though it worked good, so I'm wondering how to implement it like that (with the 3 different demos, the code confused me). Any help is appreciated, and thanks again for all the shaders!

  • Posts: 2,020

    This one adds the builtin in shader for comparison. It runs quickly on the Air, by doing 10 samples per pixel in a single pass. My last one also does 10 samples, but because it does 2 passes of 5, that results in 25 samples. I'm biased of course, but I think my one looks way better than the builtin one ;-)


    --# Main --Gaussian blur --adapted by 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() --2 pass Gaussian blur: local downSample = 0.5 -- going down to 0.25 actually looks pretty good, but, weirdly, slower than 0.5 local dimensions = { vec2(WIDTH, HEIGHT), --full size vec2(WIDTH, HEIGHT) * downSample --down sampled } blurRadii = { vec2(0.002,0), --horizontal pass, increase x value for more horizontal blur vec2(0,0.002) --vertical pass, increase y value for more vertical blur } blur = {} --images blurred = {} --meshes for i=1,2 do --2 passes, one for horizontal, one vertical blur[i]=image(dimensions[i].x, dimensions[i].y) --image 1 is full sized, image 2 is downsampled blurred[i]=mesh() blurred[i].texture=blur[i] local j=3-i --invert i so that... blurred[i]:addRect(dimensions[j].x/2, dimensions[j].y/2, dimensions[j].x, dimensions[j].y) --mesh 1 rect is down-sampled, mesh 2 rect is full-sized (ie, opposite of their images) blurred[i].shader=shader(Gaussian.vs, Gaussian.fs) blurred[i].shader.blurRadius=blurRadii[i] end --mesh w/o the blur shader: unblurred=mesh() unblurred.texture=blur[1] unblurred:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) --builtin shader builtin=mesh() builtin.texture=blur[1] builtin:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) builtin.shader=shader("Filters:Blur") local blurriness = 3 builtin.shader.conPixel=vec2(blurriness/WIDTH,blurriness/HEIGHT) builtin.shader.conWeight=1/9 showBlur=1 --blur state --some animations: pos={x1=200, y2=200, x3=800} radii={r1 = vec2(0.03,0.01), r2=vec2(-0.01,-0.03)} tween(2, pos, {x1=800, y2=800, x3=200}, {easing=tween.easing.cubicInOut, loop=tween.loop.pingpong}) tween(3, radii, {r1 = vec2(-0.03,-0.01), r2=vec2(0.01,0.03)}, {easing=tween.easing.sineInOut, loop=tween.loop.pingpong}) movement = true profiler.init() print ("tap screen to cycle blur effects") end function draw() if movement then setContext(blur[1]) background(50) -- background(0,0) sprite("Platformer Art:Guy Jump",pos.x1,HEIGHT-200) sprite("Space Art:Red Ship",pos.x3,200,300) sprite("Platformer Art:Icon",WIDTH/2,pos.y2) end if showBlur<3 then --draw blur if required if showBlur==2 then blurred[1].shader.blurRadius = radii.r1 blurred[2].shader.blurRadius = radii.r2 else blurred[1].shader.blurRadius = blurRadii[1] blurred[2].shader.blurRadius = blurRadii[2] end setContext(blur[2]) -- background(50) --nice after-image effect if you dont clear the intermediary layer blurred[1]:draw() --pass one, offscreen setContext() blurred[2]:draw() --pass two, onscreen elseif showBlur==3 then setContext() -- background(50) --doesn't seem to be necesary to clear screen, for some reason unblurred:draw() elseif showBlur==4 then setContext() -- background(50) --doesn't seem to be necesary to clear screen, for some reason builtin:draw() end profiler.draw() end --touching toggles blur on and off local states={"2-pass blur, 5+5=25 samples", "2 pass insect eye", "off", "built-in blur, 1 pass, 10 samples"} function touched(t) if t.state==ENDED then showBlur = showBlur + 1 if showBlur==5 then showBlur=1 end output.clear() print(states[showBlur]) end end profiler={} function profiler.init(quiet) profiler.del=0 profiler.c=0 profiler.fps=0 profiler.mem=0 if not quiet then parameter.watch("profiler.fps") parameter.watch("profiler.mem") end end function profiler.draw() profiler.del = profiler.del + DeltaTime profiler.c = profiler.c + 1 if profiler.c==10 then profiler.fps=profiler.c/profiler.del profiler.del=0 profiler.c=0 profiler.mem=collectgarbage("count", 2) end end --# Gaussian Gaussian = {vs = [[ uniform mat4 modelViewProjection; uniform vec2 blurRadius; attribute vec4 position; attribute vec2 texCoord; varying vec2 vBlurCoords[5]; void main() { gl_Position = modelViewProjection * position; vBlurCoords[0] = texCoord; vBlurCoords[1] = texCoord + blurRadius * 1.407333; vBlurCoords[2] = texCoord - blurRadius * 1.407333; vBlurCoords[3] = texCoord + blurRadius * 3.294215; vBlurCoords[4] = texCoord - blurRadius * 3.294215; } ]], --optimised fragment shader fs = [[ precision mediump float; uniform lowp sampler2D texture; varying vec2 vBlurCoords[5]; void main() { // gl_FragColor = vec4(0.0); gl_FragColor = texture2D(texture, vBlurCoords[ 0]) * 0.304005; gl_FragColor += texture2D(texture, vBlurCoords[ 1])* 0.204164; gl_FragColor += texture2D(texture, vBlurCoords[ 2])* 0.204164; gl_FragColor += texture2D(texture, vBlurCoords[ 3])* 0.093913; gl_FragColor += texture2D(texture, vBlurCoords[ 4])* 0.093913; }]]}
  • Posts: 2,020

    Here's a Gaussian kernel calculator, if you want to experiment with weightings:

    http://dev.theomader.com/gaussian-kernel-calculator/

  • edited May 2015 Posts: 216

    @yojimbo2000 I agree with you, your blur looks better. For doing the 2 passes, would you draw the blur on a single axis, write it to an image, then blur that image on the other axis?

  • Posts: 2,020

    yes, that's what the code is doing.
    So, first the scene you want blurred is drawn to the blur[1] image. That is then drawn, at a quarter the size, with the horizontal blurring on the shader, to the blur[2] image (the blurRadii table determines whether the blurring is horizontal or vertical). blur[2] is then drawn back to the screen, at full size, with the vertical blurring added. I suspect you could optimize it further by drawing blur[2] to a third buffer, also a quarter the size of the screen, and then drawing that 3rd buffer back to the screen, full-size. I think at the moment, the processing saving from going down to a quarter the size is only gained on the first pass, but not the second.

Sign In or Register to comment.