Book of Shaders : Pollock
GLSL Study 4
May 22nd, 2022
In the Noise chapter of the The Book of Shaders, there is an interesting challenge to recreate a Pollock, using shader code. The following image is presented as an example.
![Jackson Pollock - Number 14 gray](/images/shaders/pollock.jpg)
The simplest part of this algorithm would be the drips. We know how to make a cicle, and we know how to get a random number based on an input, so all we need to do is create circles in a loop and use the loop index as the random input.
float circle(vec2 _st, float radius, vec2 offset){
vec2 circle_st = fract(_st) - 0.5;
circle_st -= offset;
float circle = step(length(circle_st), 0.5);
circle = 1.0 - smoothstep(radius-0.005, radius+0.0005, length(circle_st));
return circle;
}
float splats;
for(int i = 0; i < 200; i++){
float random_x = (fract(random(float(i) + 0.1)) - 0.5) * 0.99;
float random_y = (fract(random(float(i)) - 1.0) - 0.5) * 1.02;
float random_radius = fract(max(0.001, random(float(i) + 0.2) * 0.006));
splats += circle(st, random_radius, vec2(random_x, random_y));
}
![](/images/shaders/splatters.png)
But how do we get thin organic lines of the paint drips of the pollock? I noticed in one of the noise examples for *flow*, the st itself could be modulated by some noise to create some nice splotches.
float splatters(vec2 _st){
vec2 splatter_st = _st * 6.0;
color = smoothstep(.16, .2, noise(splatter_st);
}
![](/images/shaders/splatters2.png)
I realized that if I offset this shape and then subtract it from itself, it would produce some nice painterly lines.
float splatters(vec2 _st){
vec2 splatter_st = _st * 6.0;
color = smoothstep(.16, .2, noise(splatter_st);
color -= smoothstep(.17, .2, noise(splatter_st+0.0001);
}
If I add this to the loop like the circle drips, it starts looking like some grassy wallpaper, which is interesting in itself, but not the painterly style that is our goal.
![](/images/shaders/hay.png)
The pattern that is emerging here tells me that I need to add some variation to the rotation. We can accomplish this using the for loop as well.
mat2 rotate2d(float angle){
return mat2(cos(angle),-sin(angle),
sin(angle),cos(angle));
}
for(int i = 0; i < 200; i++){
float random_x = (fract(random(float(i) + 0.1)) - 0.5) * 0.99;
float random_y = (fract(random(float(i)) - 1.0) - 0.5) * 1.02;
float random_radius = fract(max(0.001, random(float(i) + 0.2) * 0.006));
splats += circle(st, random_radius, vec2(random_x, random_y));
rotated_st = rotate2D(rotated_st, 3.14*94); //added arbitrary angles here until it looked good.
splats += splatters(rotated_st);
}
![](/images/shaders/hay2.png)
Now we're getting close! We just need some some more variation. We can plug the index into the splatters function and add a few different subtractions. Let's also start adding some time. Here's the final result.
vec2 random2(vec2 st){
st = vec2( dot(st,vec2(127.1,311.7)),
dot(st,vec2(269.5,183.3)) );
return -1.0 + 2.0*fract(sin(st)*43758.5453123);
}
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
vec2 u = f*f*(3.0-2.0*f);
return mix( mix( dot( random2(i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) ),
dot( random2(i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x),
mix( dot( random2(i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ),
dot( random2(i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y);
}
vec2 rotate2D (vec2 _st, float _angle) {
_st -= 0.5;
_st = mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle)) * _st;
_st += 0.5;
return _st;
}
float circle(vec2 _st, float radius, vec2 offset){
vec2 circle_st = fract(_st) - 0.5;
circle_st -= offset;
float circle = step(length(circle_st), 0.5);
circle = 1.0 - smoothstep(radius-0.005, radius+0.0005, length(circle_st));
return circle;
}
float splatters(vec2 _st, int index){
vec3 color;
float _index = float(index);
if(_index == 0.){
vec2 splatter_st = _st * 10.5;
color = vec3(1.) * smoothstep(.1, .2, noise(splatter_st + float(index)));
color -= smoothstep(.1, .2, noise(splatter_st+0.25+float(index)));
} else {
vec2 splatter_st = _st * 6. + mod(_index, 12.);
if(mod(_index, 10.0) < 5.0){
splatter_st = min(_st * 2.0, _st * mod(_index, 12.0));
color = vec3(1.) * smoothstep(.19, .2, noise(splatter_st+ float(index)));
color -= smoothstep(.18, .2, noise(splatter_st+0.025+float(index)));
} else {
splatter_st = max(_st * 12., splatter_st);
color = vec3(1.) * smoothstep(.16, .2, noise(splatter_st+ float(index)));
color -= smoothstep(.17, .2, noise(splatter_st+0.0001*_index+float(index)));
}
}
color = smoothstep(0.4, 1., color);
return max(0.0, color.r);
}
mat2 rotate2d(float angle){
return mat2(cos(angle),-sin(angle),
sin(angle),cos(angle));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord){
vec2 st = fragCoord.xy/iResolution.xy;
st += vec2(iTime*0.03, 0.0);
float splats;
vec2 rotated_st = st;
for(int i = 0; i < 200; i++){
float slow_time = iTime * 1. * random((float(i)+1.)*(float(i)+1.0));
float sin_time = sin(slow_time);
float random_x = (fract(random(float(i) + 0.1)) - 0.5) * 0.99;
float random_y = (fract(random(float(i)) - 1.0) - 0.5) * 1.02;
float random_radius = fract(max(0.001, (random(float(i) + 0.2) + sin_time) * 0.006));
splats += circle(st, random_radius, vec2(random_x, random_y));
splats += splatters(rotated_st, i);
rotated_st = rotate2D(rotated_st, 3.14*95.);
}
vec3 color = (1.0 - splats) * vec3(1.0, 0.97, 0.9);
fragColor = vec4(vec3(color),1.0);
}