Using a screen-tone is a bit like using tracing paper. They are used by black and white comic book artists (particularly manga artists) to create patterns that look like gradients but are in fact just combinations of black and white (clear) dots. They are used so that the comics are easier to print; they can be likened to the dots seen in pop-art.
My screen-tone patterns
For this program I use 5 different patterns / screen-tones. They are: Nothing (white), Dots, Horizontal Lines, Intersecting Vertical and Horizontal Lines, and Full Black. They are pictured below:
We need to get the coordinates of each pixel, but that's easy enough. The only way to create these patterns in a shader is to use if-statements, it's unavoidable. They are as follows:
// texcoord contains the normalized screen coordinates that are between 0 and 1
// SCREEN_WIDTH is a constant 1920
// SCREEN_HEIGHT is a constant 1080
uint positionX = uint(texcoord.x * SCREEN_WIDTH);
uint positionY = uint(texcoord.y * SCREEN_HEIGHT);
// White does not need an if statement, just return float3(1, 1, 1) when we need to use this
// Dots:
if(positionX % 2 == 0 && positionY % 2 == 0)
// Lines:
if(positionY % 2 == 0)
// Squares or hashes:
if(positionX % 2 == 0 || positionY % 2 == 0)
// Black does not need a statement either, just return float3(0, 0, 0)
To determine where to apply each pattern, I check the surrounding 8 pixels of each pixel and itself and calculate its luminosity. I created a couple of helper methods to do this:
float3 findColourRelative(float2 texcoord, float2 relativeCoords)
{
relativeCoords *= float2(INV_WIDTH, INV_HEIGHT);
relativeCoords += texcoord;
return tex2D(s0, relativeCoords).rgb;
}
float luminosity(float3 col)
{
return (col.r + col.g + col.b) / 3.0;
}
float topLeft = luminosity(findColourRelative(texcoord, float2(-1, -1)));
float average = (topLeft +
topMiddle +
topRight +
middleLeft +
middleMiddle +
middleRight +
bottomLeft +
bottomMiddle +
bottomRight) / 9;
So I write some if statements to find which band the current pixels fit in, and in those, I put the if-statements I wrote for each pattern:
float3 white = float3(1, 1, 1);
float3 black = float3(0, 0, 0);
if(average > 0.8)
{
return white;
}
if(average > 0.6)
{
if(positionX % 2 == 0 && positionY % 2 == 0)
return black;
return white;
}
if(average > 0.4)
{
if(positionY % 2 == 0)
return black;
return white;
}
if(average > 0.2)
{
if(positionX % 2 == 0 || positionY % 2 == 0)
return black;
return white;
}
return black;