Creating a halftone shader

halftone pattern applied to a full moon image

Learn how to create a halftone shader using the GLSL programming language. Create a halftone pattern using multiplication, aspect ratio correction, conversion to greyscale, smoothing and the distance to the centre of a circle.

I used these beautiful images of a full moon and a blue tit bird sitting on a branch for the tutorial.

full moon
Full moon
blue tit bird
Blue tit bird

Aspect ratio

Calculate the aspect ratio.

float w = 1.0 / min(resolution.x, resolution.y);
vec2 aspectRatio = resolution * w;

Texture coordinates

Two types of texture coordinates are used. The first is in the 0 to 1 range called texCoord. The second is aspect ratio corrected to make sure the halftone circles are not elliptical in shape.

vec2 uv = texCoord * aspectRatio;

Circle total

Define the total number of circles using the minimum resolution, which is passed into the shader as a uniform and represents the texture size. Use the fract function to define the number of circles and offset the position variable so that the circles are centred.

float total = min(resolution.x, resolution.y);
vec2 pos = fract(uv * total);
pos -= 0.5;

Greyscale

Get the current pixel colour, and convert it to greyscale using weights.

// Current pixel colour
vec4 pixel = texture(textureUnit, texCoord);

// Convert to greyscale
const vec3 GREY_WEIGHTS = vec3(0.299f, 0.587f, 0.114f);
float greyResult = dot(pixel.rgb, GREY_WEIGHTS);

Circle distance

Get the distance to the centre of the circle. Use the inverse of the greyscale brightness to define the radius of each circle.

float radius = 1.0 - greyResult;
float c = Circle(pos, radius);

Circle function:

float Circle(vec2 pos, float radius)
{
    return length(pos) - radius;
}

Smoothing

Apply smoothing to the circle edges.

float SMOOTHING = 4.0;
float s = SMOOTHING * w;
c = 1.0 - smoothstep(s, -s, c);

Pixel alpha

Set the output fragment pixel alpha.

fragColour.a = pixel.a;

Multiply

Multiply the current pixel colour with the circle distance value.

fragColour.rgb = pixel.rgb * c;
full moon image with multiply halftone applied
Full moon image with multiply halftone pattern applied
blue tit bird image with multiply halftone applied
Blue tit bird image with multiply halftone pattern applied

Black and white

Or multiply with the greyscale value to get a black and white image.

fragColour.rgb = vec3(greyResult * c);
halftone pattern applied to a full moon image
Halftone pattern applied to a full moon image
halftone pattern applied to a zoomed in full moon image
Halftone pattern applied to a zoomed in full moon image
blue tit image with a halftone pattern applied
Blue tit bird image with a halftone pattern applied

Conclusion

Thank you for reading this tutorial. Let me know in the comments section if you enjoyed it, or have any questions!

See part two for more blending options.

2 responses to “Creating a halftone shader”

Leave a comment