diff --git a/shader/playground.glsl b/shader/playground.glsl index fccdfd7..bee2109 100644 --- a/shader/playground.glsl +++ b/shader/playground.glsl @@ -45,59 +45,92 @@ float smin(float a, float b, float k) { return mix(b, a, h) - k * h * (1.0 - h); } -// 頭:円に2つの耳を滑らかにくっつける -float sdHead(vec2 p) { - float head = sdCircle(p, 0.6); - float earL = sdCircle(p - vec2(-0.45, 0.5), 0.17); - float earR = sdCircle(p - vec2(0.45, 0.5), 0.17); - return smin(head, min(earL, earR), 0.12); -} - // 図形を色で塗り重ねる vec3 paint(vec3 base, vec3 ink, float d) { return mix(base, ink, fill(d)); } +// ---- 第7章:座標の変換と繰り返し ---- + +// 回転行列(角度aはラジアン、反時計回り)。mat2は列毎に値を並べる +mat2 rot(float a) { + float c = cos(a), s = sin(a); + return mat2(c, s, -s, c); // 1列目 (c, s)、2列目 (-s, c) +} + +// fwidthを使わない塗り(fractの継ぎ目では隣のピクセルとの差が意味を持たない為) +float fillPx(float d) { + float px = 2.0 / uResolution.y; + return 1.0 - smoothstep(-px, px, d); +} + +// レンガの壁(第7章のタイル) +vec3 bricks(vec2 p) { + vec2 size = vec2(0.4, 0.2); + vec2 q = p / size; + q.x += 0.5 * mod(floor(q.y), 2.0); // 1段おきに半分ずらす + vec2 id = floor(q); + vec2 f = (fract(q) - 0.5) * size; + float d = sdRoundBox(f, size * 0.5 - 0.012, 0.02); + float shade = 0.85 + 0.15 * cos(id.x * 2.7 + id.y * 5.1); + return mix(vec3(0.55, 0.53, 0.50), vec3(0.70, 0.32, 0.22) * shade, fillPx(d)); +} + +// 角度の繰り返し:pを一番近い区画の中心(x軸上)に回す +vec2 repeatAngle(vec2 p, float count) { + float sector = 6.2832 / count; + float k = floor(atan(p.y, p.x) / sector + 0.5); + return rot(-k * sector) * p; +} + +// 針:12時の方向を向いたカプセルを、時計回りに回す +// 座標をrot(+a)で回すと、図形は-a、つまり時計回りに回る +float sdHand(vec2 p, float turn, float back, float len, float width) { + vec2 q = rot(turn * 6.2832) * p; + return sdSegment(q, vec2(0.0, -back), vec2(0.0, len)) - width; +} + +// 時針と分針を1つの距離に(影にも使う) +float sdHands(vec2 p, float t) { + float h = sdHand(p, fract(t / 43200.0), 0.08, 0.36, 0.028); // 時針:12時間で1周 + float m = sdHand(p, fract(t / 3600.0), 0.10, 0.54, 0.018); // 分針:1時間で1周 + return min(h, m); +} + void main() { // 中心が原点、縦が-1〜1になる座標(第5章) vec2 p = (2.0 * gl_FragCoord.xy - uResolution) / uResolution.y; - vec2 m = (2.0 * uMouse - uResolution) / uResolution.y; // マウスも同じ座標に - - // 背景:縦のグラデーション - vec3 color = mix(vec3(0.16, 0.12, 0.22), vec3(0.08, 0.06, 0.10), p.y * 0.5 + 0.5); - - // 影:同じ距離関数をずらして、ぼかして使う - float shadow = sdHead(p - vec2(0.05, -0.06)); - color *= 1.0 - 0.5 * (1.0 - smoothstep(-0.05, 0.12, shadow)); - - // 頭の塗りと輪郭 - float head = sdHead(p); - color = paint(color, vec3(1.00, 0.80, 0.30), head); - color = paint(color, vec3(0.35, 0.18, 0.10), abs(head) - 0.012); - - // ほっぺ:境界を広くぼかした円 - float cheek = min(sdCircle(p - vec2(-0.36, -0.12), 0.09), - sdCircle(p - vec2(0.36, -0.12), 0.09)); - color = mix(color, vec3(1.0, 0.50, 0.50), 0.6 * (1.0 - smoothstep(-0.04, 0.04, cheek))); - - // 目:縦のカプセル - vec2 eyeL = vec2(-0.2, 0.12); - vec2 eyeR = vec2(0.2, 0.12); - float eyes = min(sdSegment(p, eyeL - vec2(0.0, 0.06), eyeL + vec2(0.0, 0.06)), - sdSegment(p, eyeR - vec2(0.0, 0.06), eyeR + vec2(0.0, 0.06))) - 0.075; - color = paint(color, vec3(1.0), eyes); - color = paint(color, vec3(0.35, 0.18, 0.10), abs(eyes) - 0.01); - - // 瞳:マウスの方を見る(目の中から出ない様に長さを制限) - vec2 look = m - (eyeL + eyeR) * 0.5; - look = look / max(length(look), 1.0) * 0.05; - float pupils = min(sdCircle(p - eyeL - look, 0.04), sdCircle(p - eyeR - look, 0.04)); - color = paint(color, vec3(0.12, 0.08, 0.06), pupils); - - // 口:リングの下半分(リングと半平面の積集合) - float ring = abs(sdCircle(p - vec2(0.0, 0.02), 0.3)) - 0.022; - float below = p.y + 0.12; // y < -0.12 の部分が負 - color = paint(color, vec3(0.35, 0.18, 0.10), max(ring, below)); + + // 10時10分0秒から始まる時刻(秒) + float t = uTime + 10.0 * 3600.0 + 10.0 * 60.0; + + // 背景:少し暗くしたレンガの壁 + vec3 color = bricks(p) * 0.55; + + // 時計の影 + float shadow = sdCircle(p - vec2(0.04, -0.06), 0.8); + color *= 1.0 - 0.6 * (1.0 - smoothstep(-0.05, 0.1, shadow)); + + // 枠と文字盤 + float rim = sdCircle(p, 0.8); + float face = sdCircle(p, 0.72); + color = paint(color, vec3(0.20, 0.18, 0.22), rim); + color = paint(color, vec3(0.96, 0.93, 0.85), face); + + // 目盛り:60個の小さい物と12個の大きい物 + float minuteTick = sdBox(repeatAngle(p, 60.0) - vec2(0.65, 0.0), vec2(0.025, 0.004)); + float hourTick = sdBox(repeatAngle(p, 12.0) - vec2(0.62, 0.0), vec2(0.06, 0.014)); + color = paint(color, vec3(0.25), minuteTick); + color = paint(color, vec3(0.12), hourTick); + + // 針の影、時針と分針 + color *= 1.0 - 0.3 * (1.0 - smoothstep(-0.02, 0.03, sdHands(p - vec2(0.015, -0.025), t))); + color = paint(color, vec3(0.12, 0.12, 0.15), sdHands(p, t)); + + // 秒針:1秒毎にカチッと動く + float second = sdHand(p, fract(floor(t) / 60.0), 0.15, 0.6, 0.006); + second = min(second, sdCircle(p, 0.035)); + color = paint(color, vec3(0.85, 0.15, 0.15), second); out_color = vec4(color, 1.0); }