Full implementation
The Dawn
series of sketches are a tribute to some of the beautiful work of Rik Oostenbroek.
I've been a huge fan of his work for a long time, and was particularly inspired by some of his pieces that feature lovely, vertically-oriented gradients with a simple, repeating, linear pattern and a grainy texture.
A couple of examples that inspired this shader:
Breakdown
This shader is surprisingly simple, but gives such an excellent result. It's a mixture of procedural color palettes, a fractionated pattern, and a grainy texture effect applied at the end, almost like a post-processing effect. There are 4 essential parts to this shader:
Gradient
This gradient uses our cosinePalette
function, which is a simple way to create a gradient of colors using a cosine function. I use this function a lot in my own work, and it's a great way to create a smooth, continuous gradient of colors.
// Get aspect-corrected UVs for the screen
const uv0 = screenAspectUV(screenSize).toVar()
// Palette arguments
const a = vec3(0.5, 0.5, 0.5)
const b = vec3(0.5, 0.5, 0.5)
const c = vec3(1.0, 1.0, 0.5)
const d = vec3(0.8, 0.9, 0.3)
// Create an animated vertically-oriented gradient using our UV coordinates
const col = cosinePalette(uv0.y.add(0.5).add(time.mul(0.01)), a, b, c, d)
Pattern
This linear pattern is a simple, repeating pattern using the fract
function. We then "soften" this pattern before applying it to the final shader by multiplying it by a small number.
// Get aspect-corrected UVs for the screen
const _uv = screenAspectUV(screenSize).toVar()
// Repeated sawtooth pattern in Y, softened
const repeatedPattern = fract(_uv.y.mul(24)).mul(0.3)
// Add pattern to color, boost with pow for punch
finalColor.assign(col.add(pow(repeatedPattern, 2.0)))
Texture
The grain
function adds a grainy texture to the image. This is a simple function that creates noise
and then applies it to the image.
// Get aspect-corrected UVs for the screen
const _uv = screenAspectUV(screenSize).toVar()
// Add grain for texture
const _grain = grain(_uv).mul(0.2)
finalColor.addAssign(_grain)
Punching up the output
The pow
function adds a bit of added contrast to the pattern. We want to try and make the bright parts brighter, and the dark parts darker here. It's not much, but it's enough to make the image more interesting.
// Get aspect-corrected UVs for the screen
const _uv = screenAspectUV(screenSize).toVar()
// Repeated sawtooth pattern in Y, softened
const repeatedPattern = fract(_uv.y.mul(24)).mul(0.3)
// The pow function squares the pattern value (s^2), which makes the bright parts brighter
// while keeping dark parts dark, creating more contrast and "punch"
finalColor.assign(col.add(pow(repeatedPattern, 2.0)))