GLSL Checkerboard Pattern
Asked Answered
T

6

7

I want to shade a quad with checkers:

f(P) = [floor(Px) + floor(Py)] mod 2.

My quad is:

glBegin(GL_QUADS);    
  glVertex3f(0,0,0.0);    
  glVertex3f(4,0,0.0);    
  glVertex3f(4,4,0.0);   
  glVertex3f(0,4, 0.0); 
glEnd();

The vertex shader file:

varying float factor;
float x,y;
void main(){
  x=floor(gl_Position.x);
  y=floor(gl_Position.y);
   factor = mod((x+y),2.0);
}

And the fragment shader file is:

varying float factor;
void main(){
  gl_FragColor = vec4(factor,factor,factor,1.0);
}

But I'm getting this:

alt text

It seems that the mod function doesn't work or maybe it's something else... Any help?

Todhunter answered 14/1, 2011 at 18:34 Comment(1)
Interesting. The result shows that the quad is actually rendered as two triangles (topleft/bottomright). Otherwise the result would be more like a smoothed checkerboard. If it were, you could use a thresholding function in your fragment shader to get a proper checkerboard. As it is, the interpolation is useless for a checkerboard effect.Seleucia
S
19

It is better to calculate this effect in fragment shader, something like that:

vertex program =>

varying vec2 texCoord;

void main(void)
{
   gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0);
   gl_Position = sign(gl_Position);
    
   texCoord = (vec2(gl_Position.x, gl_Position.y) 
             + vec2(1.0)) / vec2(2.0);      
}

fragment program =>

#extension GL_EXT_gpu_shader4 : enable
uniform sampler2D Texture0;
varying vec2 texCoord;

void main(void)
{
    ivec2 size = textureSize2D(Texture0, 0);
    float total = floor(texCoord.x * float(size.x)) +
                  floor(texCoord.y * float(size.y));
    bool isEven = mod(total, 2.0) == 0.0;
    vec4 col1 = vec4(0.0, 0.0, 0.0, 1.0);
    vec4 col2 = vec4(1.0, 1.0, 1.0, 1.0);
    gl_FragColor = (isEven) ? col1 : col2;
}

Output =>

alt text

Good luck!

Somatist answered 17/1, 2011 at 11:27 Comment(1)
why not use integer x,y and apply simple bit AND (no division needed inside fragment)Melancholy
I
6

Try this function in your fragment shader:

vec3 checker(in float u, in float v)
{
  float checkSize = 2;
  float fmodResult = mod(floor(checkSize * u) + floor(checkSize * v), 2.0);
  float fin = max(sign(fmodResult), 0.0);
  return vec3(fin, fin, fin);
}

Then in main you can call it using :

vec3 check = checker(fs_vertex_texture.x, fs_vertex_texture.y);

And simply pass x and y you are getting from vertex shader. All you have to do after that is to include it when calculating your vFragColor.

Keep in mind that you can change chec size simply by modifying checkSize value.

Instruct answered 7/2, 2015 at 13:43 Comment(0)
G
4

What your code does is calculate the factor 4 times (once for each vertex, since it's vertex shader code) and then interpolate those values (because it's written into a varying varible) and then output that variable as color in the fragment shader.

So it doesn't work that way. You need to do that calculation directly in the fragment shader. You can get the fragment position using the gl_FragCoord built-in variable in the fragment shader.

Giltzow answered 14/1, 2011 at 18:49 Comment(0)
E
4

May I suggest the following:

float result = mod(dot(vec2(1.0), step(vec2(0.5), fract(v_uv * u_repeat))), 2.0);
  • v_uv is a vec2 of UV values,
  • u_repeat is a vec2 of how many times the pattern should be repeated for each axis.
  • result is 0 or 1, you can use it in mix function to provide colors, for example:
gl_FragColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.0, 0.0, 0.0, 1.0) result);
Electro answered 3/11, 2020 at 5:11 Comment(0)
O
4

Another nice way to do it is by just tiling a known pattern (zooming out). Assuming that you have a square canvas:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;
    uv -= 0.5; // moving the coordinate system to middle of screen
    // Output to screen
    fragColor = vec4(vec3(step(uv.x * uv.y, 0.)), 1.);
}

Code above gives you this kind of pattern. square pattern

Code below by just zooming 4.5 times and taking the fractional part repeats the pattern 4.5 times resulting in 9 squares per row.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fract(fragCoord/iResolution.xy * 4.5);
    uv -= 0.5; // moving the coordinate system to middle of screen
    // Output to screen
    fragColor = vec4(vec3(step(uv.x * uv.y, 0.)), 1.);
}

square pattern repeated

Occlusive answered 19/12, 2020 at 13:49 Comment(0)
A
0

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:

8x8_checkboard

If the quad is rectangular, then the checkerboard will contain black and white rectangles instead of squares:

8x8_rectangles

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:

8x8_checkboard_adjusted_for_aspect_ratio

Adamite answered 5/10 at 13:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.