Wiki

Clone wiki

Core / CheckerShader

Back to Built-in Shader Packs


Checker Shader

Introduction

'Checker' is an example shader provided in Codea's 'Patterns' Shaders Pack. Based on the location of a fragment on the Viewer, it produces a black-and-white checker-board effect.

Vertex shader

The vertex shader does nothing other than set the gl_Position of the vertex by applying the modelViewProjection matrix to the position attribute:

void main()
{
    ...
    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 makes use of a 'uniform' variable size, which represents the length of the sides of the squares in the check, in pixels. (The other 'uniform' variable, resolution, is not used in the code and is redundant.)

The fragment shader sets gl_FragColor to a vec4 constructed from color (a vec3) and an alpha (opacity) value of 0.9:

gl_FragColor = vec4(color, 0.90);

color is constructed with all three components the same (col:

color = vec3(col, col , col);

A more succinct syntax with the same effect is (see Section 5.4.2 'Vector and Matrix Constructors' of the GLSL ES specification):

color = vec3(col);

color is declared and initialised as:

vec3 color = vec3(0.0);

The initialisation is redundant because another value is always assigned to the variable.

col is set to 0.0 or 1.0, depending on the components of gl_FragCoord. Those components are initially reduced in scale by size and rounded down to integer values:

vec2 position = (gl_FragCoord.xy);
...
float xpos = floor(position.x/size);
float ypos = floor(position.y/size);

As gl_FragCoord is itself a vec2 value this could be rewritten more succinctly as:

float xpos = floor(gl_FragCoord.x / size);
float ypos = floor(gl_FragCoord.y / size);

col is then calculated as:

float col = mod(xpos, 2.);
if (mod(ypos, 2.) > 0.)
    if (col > 0.)
        col = 0.;
    else
        col = 1.;

If xpos (an integer) is even, then mod(xpos, 2.0) is 0.0. Otherwise the expression is 1.0.

If ypos (also an integer) is odd (and mod(ypos, 2.0) is 1.0 and greater than 0.0) then the value of col is swapped (0.0 for 1.0 and vice versa).

The same result can be acheived by:

float col = mod(xpos + mod(ypos, 2.0), 2.0);

In summary, an equivalent fragment shader is:

precision highp float;

uniform float size;

void main()
{
    float xpos = floor(gl_FragCoord.x / size);
    float ypos = floor(gl_FragCoord.y / size);
    float col = mod(xpos + mod(ypos, 2.0), 2.0);
    gl_FragColor = vec4(vec3(col), 0.90);
}

Example of use

The code below is a simple example of the use of the shader:

function setup()
    x = WIDTH / 2
    y = HEIGHT / 2
    a = 0
    xm = 5
    ym = 7
    am = 3
    myMesh = mesh()
    myMesh:addRect(0, 0, 200, 200)
    myMesh.shader = shader("Patterns:Checker")
    parameter.integer("size", 20, 100, 50)
end

function draw()
    background(255, 255, 0)
    x = x + xm
    y = y + ym
    a = (a + am) % 360
    if x < 0 then
        x = -x
        xm = -xm
    end
    if x > WIDTH then
        x = 2 * WIDTH - x
        xm = -xm
    end
    if y < 0 then
        y = -y
        ym = -ym
    end
    if y > HEIGHT then
        y = 2 * HEIGHT - y
        ym = -ym
    end
    translate(x, y)
    rotate(a)
    myMesh.shader.size = size
    myMesh:draw()
end

Updated