diff --git a/shader/playground.glsl b/shader/playground.glsl index 8ec93da..fccdfd7 100644 --- a/shader/playground.glsl +++ b/shader/playground.glsl @@ -7,16 +7,97 @@ uniform float uTime; // 起動してからの秒数 uniform vec2 uResolution; // 画面の大きさ(ピクセル) uniform vec2 uMouse; // マウスの位置(ピクセル、左下が原点) +// ---- 第6章:符号付き距離関数(負=内側、0=境界、正=外側) ---- + +// 円(中心が原点、半径r) +float sdCircle(vec2 p, float r) { + return length(p) - r; +} + +// 箱(中心が原点、半分の大きさがb) +float sdBox(vec2 p, vec2 b) { + vec2 d = abs(p) - b; + return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); +} + +// 線分abまでの距離 +float sdSegment(vec2 p, vec2 a, vec2 b) { + vec2 pa = p - a; + vec2 ba = b - a; + float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba * h); +} + +// 角の丸い箱:小さい箱を作ってから、距離からrを引く +float sdRoundBox(vec2 p, vec2 b, float r) { + return sdBox(p, b - r) - r; +} + +// 符号付き距離から、アンチエイリアス付きの塗り(0〜1)を作る +float fill(float d) { + float w = fwidth(d); + return 1.0 - smoothstep(-w, w, d); +} + +// 滑らかな和集合(kは繋がる部分の大きさ) +float smin(float a, float b, float k) { + float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); + 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)); +} + void main() { - // ピクセル座標を0〜1に正規化 - vec2 uv = gl_FragCoord.xy / uResolution; + // 中心が原点、縦が-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); - // 時間で動くグラデーション - vec3 color = 0.5 + 0.5 * cos(uTime + uv.xyx + vec3(0.0, 2.0, 4.0)); + // 瞳:マウスの方を見る(目の中から出ない様に長さを制限) + 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 d = length(gl_FragCoord.xy - uMouse); - color = mix(color, vec3(1.0), 1.0 - smoothstep(18.0, 20.0, d)); + // 口:リングの下半分(リングと半平面の積集合) + 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)); out_color = vec4(color, 1.0); }