This is part of the WebGL image processing series and it relies on information in previous articles. See all articles here.
It is designed to be used on desktop.
We'll improve our thresholding algorithms from the previous article by replacing block colors with patterns that, from a distance, look like a shade of a color. Here's an example of rendering a horizontal gradient with different dithering effects we'll cover in this article:
Dithering patterns on a gradient
Ordered Dithering
We use an algorithm called ordered dithering to create this effect. We can define our patterns with a dither matrix such as:
[0.20.60.80.4]
Each pixel of our image is checked against this matrix, repeating for the size of the image. For the matrix above the first pixel on the top row will be thresholded to the value 0.2. Anything above 0.2 turns white and anything below turns black. The second pixel by 0.8 and the third pixel will loop back to the first value in our matrix 0.2. The second pixel row will start from 0.6. This will repeat for the whole image.
This 2x2 matrix can give us 5 different thresholds, as shown in the image below.
The data for patterns is normally stored as integers, and we use this equation to create the threshold values.
[0231]di,j=1+m×n1+Mi,j
Here is our GLSL code that will take our dither_matrix, convert it into threshold data, and output our result:
Loading code...
Patterns
The beauty of this algorithm is that we can easily expand on it with different patterns and matrix sizes for a number of different results:
We can also add color to our image by reusing our duotone function from the duotone article in this series. Take the grayscale halftone image we produced earlier and pass into our duotone (mix) function to add a gradient map to it.
constint dither_matrix_2x2[4]=int[](0,3,2,1);floatdither2x2(vec2 uv,float luma){float dither_amount =2.0;int x =int(mod(uv.x, dither_amount));int y =int(mod(uv.y, dither_amount));int index = x + y *int(dither_amount);float limit =(float(dither_matrix_2x2[index])+1.0)/(1.0+4.0);return luma < limit ?0.0:1.0;}
voidmain(){// load texture and set texel as outColor// get image grayscalevec4 luma =vec4(0.299,0.587,0.114,0);float grayscale =dot(outColor, luma); outColor =vec4( outColor.r *dither(gl_FragCoord.xy, grayscale), outColor.g *dither(gl_FragCoord.xy, grayscale), outColor.b *dither(gl_FragCoord.xy, grayscale),1.0);}