xxxxxxxxxx
/*
Hi #WCCChallenge people!
Here's a somewhat cursed rethought, recoded version of
the art from a great album that happens to share the name
with this week's challenge theme, Blue. This perhaps
qualifies as abuse of p5's default shapes.
The blur is done with this:
https://github.com/davepagurek/p5.filterRenderer?tab=readme-ov-file#gaussian-blur
*/
let fbo
let mx, my
let blurRenderer
let boldFont, narrowFont
function preload() {
// Archivo Black
boldFont = loadFont('https://fonts.gstatic.com/s/archivoblack/v21/HTxqL289NzCGg4MzN6KJ7eW6OYuP_x7yx3A.ttf')
// Archivo Narrow
narrowFont = loadFont('https://fonts.gstatic.com/s/archivonarrow/v30/tss5ApVBdCYD5Q7hcxTE1ArZ0Zz8oY2KRmwvKhhvLFGKpHOtFCQ76Q.ttf')
}
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL)
pixelDensity(1)
fbo = createFramebuffer()
mx = width
my = height
mouseX = width
mouseY = height
blurRenderer = createGaussianBlurRenderer()
blurRenderer.setSamples(20)
colorFilter = createFilterShader(`
precision highp float;
varying vec2 vTexCoord;
uniform sampler2D tex0;
uniform vec2 canvasSize;
float random(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * .1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
void main() {
float brightness = clamp(
pow(texture2D(tex0, vTexCoord).x, 2.) - random(vTexCoord * canvasSize)*0.2,
0.,
1.
);
vec4 dark = vec4(34., 42., 102., 255.)/255.;
vec4 mid = vec4(107., 195., 242., 255.)/255.;
vec4 light = vec4(1.);
if (brightness < 0.5) {
gl_FragColor = mix(dark, mid, brightness * 2.);
} else {
gl_FragColor = mix(mid, light, (brightness - 0.5) * 2.);
}
}
`)
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight)
}
function draw() {
const s = min(width, height) / 600
const hs = height / 600
// Get a smoothed version of mouseX and mouseY
mx = lerp(mx, mouseX, 0.05)
my = lerp(my, mouseY, 0.05)
blurRenderer.setDof(10 * s)
blurRenderer.setIntensity(0.25 * s)
blurRenderer.draw(() => {
background(0)
const t = millis()
ambientLight(0)
lightFalloff(0.0001, 0, 0.000002/hs/hs)
pointLight(
255, 255, 255,
-width*0.15,
-height*0.3,
lerp(-1, 1, pow(map(t, 0, 3000, 0, 1, true), 0.5)) * height
)
noStroke()
// orbitControl()
// Camera shake
translate(
30 * map(noise(t * 0.0002, 0), 0, 1, -1, 1),
30 * map(noise(t * 0.0002, 1000), 0, 1, -1, 1),
30 * map(noise(t * 0.0002, 2000), 0, 1, -1, 1),
)
ctx(() => {
translate(width * 0.15, 0)
scale(s)
rotateY(map(mx, 0, width, -1, 1) * PI * 0.05)
rotateX(map(my, 0, height, 1, -1) * PI * 0.05)
rotate(PI*0.05)
ctx(() => {
translate(0, 50, -60)
rotateX(-PI*0.1)
rotate(PI/2)
capsule(150, 50)
})
ctx(() => {
translate(0, -70, 0)
scale(1, 0.8, 1)
sphere(110)
})
ctx(() => {
// Hair
translate(0, -100)
for (let i = 0; i < 20; i++) {
ctx(() => {
const angle = map(i + noise(i, 0), 0, 20, -PI*0.4, PI*0.4) + PI/2
translate(110*cos(angle), 0, 110*sin(angle))
const len = pow(1 - max(0, min(i-5, 15-i))/10, 2) * 130 + 10
translate(0, len/2)
rotate(PI/2 + PI*0.1*noise(i, 20)*map(i, 0, 10, -0.5, 1, true))
capsule(len, 8)
})
}
for (let i = 0; i < 20; i++) {
ctx(() => {
translate(10, 0)
const angle = map(i + noise(i, 0), 0, 20, -PI*0.4, PI*0.4) + PI/2
rotateY(angle)
rotateZ(PI*0.1)
translate(-100, 20, 0)
rotate(PI/2 + PI*0.1*noise(i, 20))
capsule(40, 8)
})
}
for (let i = 0; i < 20; i++) {
ctx(() => {
translate(10, 0)
const angle = map(i + noise(i, 0), 0, 20, -PI*0.4, PI*0.4) + PI/2
rotateY(angle)
rotateZ(PI*0.3)
translate(-80, 30, 0)
rotate(PI/2 + PI*0.1*noise(i, 20))
capsule(40, 8)
})
}
for (let i = 0; i < 30; i++) {
ctx(() => {
const angle = map(i + noise(i, 0), 0, 30, -PI*0.7, PI*0.7) - PI/2
translate(115*cos(angle), 125, 115*sin(angle))
rotate(PI/2 + PI*0.1*noise(i, 21)*map(i, 15, 30, 1, 0, true))
capsule(250, 8)
})
}
})
ctx(() => {
translate(0, 50, 80)
rotateZ(PI/2)
capsule(50, 30)
})
for (const side of [-1, 1]) {
ctx(() => {
scale(side, 1)
// Cheekbones
push()
translate(50, 10, 70)
rotateY(PI*0.15)
capsule(40, 30)
pop()
// Jaw
push()
translate(20, 50, 55)
rotateZ(-PI*0.3)
rotateY(PI*0.08)
capsule(80, 45)
pop()
// Eyes
push()
translate(-35, -10, 100)
sphere(20)
// Lashes
for (let i = 0; i <= 8; i++) {
ctx(() => {
rotate(map(i, 0, 8, -PI*0.25, PI*0.25))
rotateX(PI*0.1)
translate(0, 20, 10)
rotateX(PI*0.2)
rotate(PI/2)
capsule(5, 2)
})
}
pop()
})
}
ctx(() => {
// Brow
translate(0, -25, 80)
capsule(100, 25)
})
ctx(() => {
translate(0, 0, 10)
rotateX(PI*0.15)
rotateZ(PI/2)
capsule(150, 60)
})
ctx(() => {
// Nose
push()
translate(0, 0, 120)
rotateX(PI*0.1)
rotateZ(PI/2)
scale(1, 0.75, 1)
capsule(50, 15)
pop()
push()
translate(0, 30, 120)
rotateX(PI*0.2)
scale(1, 0.6, 1)
capsule(20, 12.5)
pop()
})
ctx(() => {
// Mouth
translate(0, 75, 120)
scale(1, 0.5)
torus(15, 5)
fill(0)
circle(0, 0, 20)
blurRenderer.focusHere()
})
})
})
ctx(() => {
translate(-width*0.2, -height * 0.05)
scale(s)
textFont(boldFont)
textSize(80)
fill(150 * map(millis(), 0, 5000, 0, 1, true))
textAlign(CENTER)
text('BLUE', 0, 0)
})
ctx(() => {
translate(-width*0.2, height * 0.025)
scale(s)
textFont(narrowFont)
textSize(40)
fill(255 * map(millis(), 2000, 7000, 0, 1, true))
textAlign(CENTER)
text('JONI MITCHELL', 0, 0)
})
filter(colorFilter)
}
function capsule(length, radius) {
push()
rotateZ(PI/2)
cylinder(radius, length)
pop()
push()
translate(-length/2, 0)
sphere(radius)
pop()
push()
translate(length/2, 0)
sphere(radius)
pop()
}
function ctx(callback) {
push()
callback()
pop()
}