The current answers all provide good solutions, but also all rely on floating point operations such as mod
, fract
and floor
. This approach converts floats to ints, uses a minimal number of operations, and therefore has the potential to be faster. At the very least, it demonstrates an alternative implementation.
Fragment shader:
const vec3 white = vec3(1.0F);
const vec3 black = vec3(0.0F);
in vec2 uv; // In range [0, 1]^2
uniform float scale = 8.0F; // The number of squares in one dimension
out vec4 fragColor;
void main()
{
ivec2 uvInt = ivec2(scale * uv); // Scale and truncate the fractional part
bool isOdd = bool((uvInt.x + uvInt.y) & 1); // Odd or even check using a bit-wise operation
fragColor = vec4(isOdd ? white : black, 1.0F);
}
This can be made more efficient by multiplying the quad's uv coordinates with scale
before these are sent to the GPU and leaving out the scaling in the fragment shader.
The original question was asked in 2011 and uses varying
, which means what an older GLSL version was targeted. Bit-wise operations may not have been available on all/most hardware then, as these were introduced in GLSL 130 (Nov 2009). In this case, the odd-or-even check can be replaced with (uvInt.x + uvInt.y) % 2 == 1
. If the remainder operator isn't available either (which is sometimes the case on older WebGL versions, for example), the following trick - using integer division - can be used:
int sum = uvInt.x + uvInt.y;
vec3 color = sum / 2 * 2 == sum ? white : black;
Output:
If the quad is rectangular, then the checkerboard will contain black and white rectangles instead of squares:
To adjust for this, the aspect ratio needs to be taken into account. Example code for ShaderToy for easy experimentation, compatible with WebGL 1:
const vec3 white = vec3(1.0);
const vec3 black = vec3(0.0);
const float scale = 8.0; // The number of *vertical* squares
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
float aspectRatio = iResolution.x / iResolution.y;
uv.x *= aspectRatio; // Adjust the horizontal scaling for the aspect ratio
ivec2 uvInt = ivec2(scale * uv);
int sum = uvInt.x + uvInt.y;
vec3 color = sum / 2 * 2 == sum ? white : black; // Odd or even check
fragColor = vec4(color, 1.0);
}
Note that in a standalone application, the computations for the scaling and adjustment for the aspect ratio can be absorbed into the uv-coordinates themselves, and thus eliminated from the shader code for better performance.
The result: