Applying blur shader to a circle (New problem implementing code)

How would you apple a blur shader to an ellipse? Doing it to a rectangle is easy enough. Write screen to image, Apple shader, draw, but for a circle you’d have to write part of the screen to a circle, but not have it distorted, then apply a shader. Not sure how I would go about doing this.

I would write a mask shader that takes two textures, 1 the image to be drawn, as usual (ie the blurred image), and the second texture a mask to draw it with, eg in this case a white circle on a transparent background

Voila!

-- Mask Shader
-- by Yojimbo2000

function setup()
    centre = vec2(WIDTH, HEIGHT)/2
    
    --make blur image
    local imgBlur = readImage("Cargo Bot:Starry Background")
    w,h = imgBlur.width, imgBlur.height
    local img1 = image(w ,h)
    local mBlur = mesh()
    mBlur.shader = shader("Filters:Blur")
    mBlur.shader.conPixel = vec2(1,1)/200
    mBlur.shader.conWeight = 1/9
    mBlur.texture = imgBlur
    mBlur:addRect(w*0.5,h*0.5,w,h)
    setContext(img1)
    mBlur:draw()
    
    --make mask image
    local img2 = image(w, h)
    local falloff = 50 --how thickly to feather the edges of the mask
    setContext(img2)
    noStroke()
    local diameter = math.min(w,h)
    local alpha = 255/ falloff
    for i = 1, falloff do
        fill(255,i*alpha) --grey value = brightness, alpha = opacity
        ellipse(w*0.5,h*0.5,diameter - i) --or whatever drawing you want your mask to be
    end
    setContext()
    
    --build mesh
    m = mesh()
    m.shader = shader(maskShader.vert, maskShader.frag)
    m.texture = img1
    m.shader.texture2 = img2 --nb note texture2 has to be set differently
    m:addRect(centre.x, centre.y, w, h)
    
    pos = vec2(50, centre.y) --some animation
    tween(5, pos, {x=WIDTH-50}, {easing = tween.easing.sineInOut, loop = tween.loop.pingpong})
end

function draw()
    background(40, 40, 50)
    sprite("Cargo Bot:Game Area", centre.x, centre.y)
    m:setRect(1,pos.x,pos.y,w,h)
    m:draw()   
end

maskShader={
vert=[[
uniform mat4 modelViewProjection;

attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;

// varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    // vColor = color;
    vTexCoord = texCoord;
    
    gl_Position = modelViewProjection * position;
}
]],
frag=[[
precision highp float;

uniform lowp sampler2D texture;
uniform lowp sampler2D texture2;

//varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    lowp vec4 col = texture2D( texture, vTexCoord ) * texture2D( texture2, vTexCoord ); 

    gl_FragColor = col;
}
]]
}

Thanks!

Hmmm, seem to be having more problems implementing it. For some reason, the mask never masks. Here’s a little demo I made showing the problem. I create two images, the background and the mask shape, as well as a mesh with the shader applied, set the image variables to the mesh and draw it, but the shape doesn’t draw correctly. Here’s the code. Thanks!


--# Main
function setup()
--Creates background image (codea icon) and Circle image for mask
image1=image(WIDTH,HEIGHT)
circle=image(WIDTH,HEIGHT)
--Makes mesh for mask, applies shader and adds a rectangle to it
c=mesh()
c:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
c.shader=shader(MS.vertexShader,MS.fragmentShader)
--loads codea logo to image1
setContext(image1)
sprite("Cargo Bot:Codea Icon",WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
setContext()
--creates a circle for the mask image
setContext(circle)
fill(255, 255, 255, 255)
ellipse(WIDTH/2,HEIGHT/2, math.min(WIDTH,HEIGHT)/1.5) 
setContext()
--sets mesh textures 
c.texture=image1
c.texture2=circle
end

function draw()
--draws image
background(0, 0, 0, 255)
c:draw()
end

--# Shader
--shader roughly based from ignatz's on coolcodea and yojimbo2000s

MS={
vertexShader=[[
//
// A basic vertex shader
//

//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;

//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;

//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    //Pass the mesh color to the fragment shader
    vColor = color;
    vTexCoord = texCoord;
    
    //Multiply the vertex position by our combined transform
    gl_Position = modelViewProjection * position;
}


]],

fragmentShader=[[
//
// A basic fragment shader
//

//Default precision qualifier
precision highp float;

//This represents the current texture on the mesh
uniform lowp sampler2D texture;
uniform lowp sampler2D texture2;
//The interpolated vertex color for this fragment
varying lowp vec4 vColor;

//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;

void main()
{
    //Sample the texture at the interpolated coordinate
    lowp vec4 col = texture2D( texture, vTexCoord );
    lowp vec4 col2 = texture2D(texture2, vTexCoord) ;
    //Set the output color to the texture color
    if (col2.a>0.) gl_FragColor = col; else discard;
}

]]
}

Texture 2 must be defined as c.shader.texture2 (whereas texture1 is c.texture) as there is only a built-in slot for 1 texture.

Thank you! I would’ve never gotten that on my own.