Wiki

Clone wiki

Core / SwirlShader

Back to Built-in Shader Packs


Swirl Shader

Introduction

'Swirl' is an example shader provided in Codea's 'Effects' Shaders Pack.

Vertex shader

The vertex shader simply passes the 'attribute' variables color and texCoord on to the accompanying fragment shader as 'varying' variables vColor and vTexCoord:

void main()
{
    ...
    vColor = color;
    vTexCoord = texCoord;
    ...
    gl_Position = modelViewProjection * position;
}

gl_Position is a variable that is intended for outputting the vertex position in homogenous co-ordinates (that is, as a vec4 value). All vertex shaders must write a value into that variable. (See Section 7.1 'Vertex Shader Special Variables' of the GLSL ES specification.) Here, the modelViewProjection 4x4 matrix is applied to the vertex's position. modelViewProjection is a 'uniform' mat4 variable supplied automatically by Codea when the shader is used with a mesh. It is the current model matrix * view matrix * projection matrix. position is a vec4 'attribute' variable, also supplied automatically by Codea from the mesh.

Fragment shader

The fragment shader sets the gl_FragColor based on the result of the swirl() function, tinted by multiplying it, component-by-component, by the 'varying' variable vColor. The parameters passed to swirl() are a texture (a sampler2D type) and vTexCoord:

void main()
{
    ...
    vec2 uv = vTexCoord.st;
    ...
    gl_FragColor = swirl(texture, uv) * vColor;
}

As vTexCoord is itself of type vec2, main() could be rewritten more succinctly as:

void main()
{
    gl_FragColor = swirl(texture, vTexCoord) * vColor;
}

swirl() uses 'uniform' vec2 variable texSize to scale the texture co-ordinate (to the dimensions of the texture) and 'uniform' float variable radius to specify the radius of the circular region that will be 'swirled'.

vec4 swirl(sampler2D tex, vec2 uv)
{
    vec2 center = texSize * 0.5;   
    vec2 tc = (uv * texSize) - center;
    float dist = length(tc);
    if( dist < radius )
    {
        float percent = (radius - dist) / radius;
        float theta = percent * percent * angle * 8.0;
        float s = sin(theta);
        float c = cos(theta);       
        tc = vec2(dot(tc, vec2(c, -s)), dot(tc, vec2(s, c)));
    }    
    tc += center;    
    return texture2D(tex, tc / texSize);
}

The function rotates the point uv through angle theta about the centre of the texture at texSize * 0.5. theta depends on the square of the distance of the point from the centre (dist).

The rotation vec2(dot(tc, vec2(c, -s)), dot(tc, vec2(s, c))) could be rewritten more succinctly by making use of the mat2() constructor and the vector multiplied by matrix operator:

tc *= mat2(c, -s, s, c);

Example of use

The code below is a simple example of the use of the shader, with a rectangular texture:

function setup()
    local img = readImage("Cargo Bot:Codea Logo")
    local w, h = img.width, img.height
    local aspect = h / w
    local imgDim = math.min(w, h)
    local viewerDim = math.min(WIDTH, HEIGHT / aspect)
    myMesh = mesh()
    myMesh:addRect(WIDTH / 2, HEIGHT / 2, viewerDim, viewerDim * aspect)
    myMesh.shader = shader("Effects:Swirl")
    myMesh.texture = img
    myMesh.shader.texSize = vec2(img.width, img.height)
    myMesh.shader.radius = imgDim / 2
end

function draw()
    background(255, 255, 0)
    myMesh.shader.angle = math.sin(ElapsedTime) * 2
    myMesh:draw()
end

Updated