diff --git a/shader/playground.glsl b/shader/playground.glsl index 8ec93da..9282bc9 100644 --- a/shader/playground.glsl +++ b/shader/playground.glsl @@ -7,16 +7,80 @@ uniform float uTime; // 起動してからの秒数 uniform vec2 uResolution; // 画面の大きさ(ピクセル) uniform vec2 uMouse; // マウスの位置(ピクセル、左下が原点) +// 1にするとマウスで光を動かす、0にすると時間で光が回る +#define FOLLOW_MOUSE 0 + +// 表面の材質 +struct Material { + vec3 albedo; // 表面の色 + float specular; // 鏡面反射の強さ + float shininess; // 光沢度(大きい程ハイライトが小さく鋭い) +}; + +const float RADIUS = 0.6; // 球の半径 +const float WALL_Z = -RADIUS; // 壁の位置(球の真後ろ) +const float AMBIENT = 0.1; // アンビエントの強さ +const vec3 LIGHT_COLOR = vec3(1.0, 0.95, 0.85) * 3.0; // 光の色と明るさ +const vec3 ATTENUATION = vec3(1.0, 0.7, 1.8); // 減衰の定数(定数項、1次、2次) + +const Material SPHERE = Material(vec3(0.8, 0.2, 0.3), 0.5, 64.0); +const Material WALL = Material(vec3(0.7, 0.7, 0.75), 0.1, 8.0); + +// 1つの光からの拡散反射と鏡面反射(ブリン・フォン) +// n:法線、v:目への向き、l:光への向き(全て単位ベクトル) +vec3 blinnPhong(vec3 n, vec3 v, vec3 l, Material m, vec3 lightColor) { + float diffuse = max(dot(n, l), 0.0); + float specular = 0.0; + if (diffuse > 0.0) { + vec3 h = normalize(l + v); + specular = pow(max(dot(n, h), 0.0), m.shininess); + } + return (m.albedo * diffuse + m.specular * specular) * lightColor; +} + +// ポイントライト:光の位置から向きと減衰を求める +vec3 pointLight(vec3 pos, vec3 n, vec3 v, Material m, vec3 lightPos, vec3 lightColor) { + vec3 toLight = lightPos - pos; + float d = length(toLight); // 光までの距離 + vec3 l = toLight / d; // normalize(toLight)と同じ + float attenuation = 1.0 / (ATTENUATION.x + ATTENUATION.y * d + ATTENUATION.z * d * d); + return blinnPhong(n, v, l, m, lightColor) * attenuation; +} + +// 表面の1点の色 +vec3 shade(vec3 pos, vec3 n, Material m, vec3 lightPos) { + vec3 v = vec3(0.0, 0.0, 1.0); // 目への向き + vec3 color = m.albedo * AMBIENT; + color += pointLight(pos, n, v, m, lightPos, LIGHT_COLOR); + return color; +} + void main() { - // ピクセル座標を0〜1に正規化 - vec2 uv = gl_FragCoord.xy / uResolution; + vec2 p = (2.0 * gl_FragCoord.xy - uResolution) / uResolution.y; + float px = 2.0 / uResolution.y; + + // 光の位置(zは球より手前) +#if FOLLOW_MOUSE + vec2 mouse = (2.0 * uMouse - uResolution) / uResolution.y; // pと同じ座標にする + vec3 lightPos = vec3(mouse, 0.7); +#else + vec3 lightPos = vec3(0.9 * cos(uTime * 1.25), 0.6 * sin(uTime * 1.25), 0.7); +#endif + + // 球 + float z = sqrt(max(RADIUS * RADIUS - dot(p, p), 0.0)); + vec3 spherePos = vec3(p, z); + vec3 sphere = shade(spherePos, spherePos / RADIUS, SPHERE, lightPos); + + // 壁(z = WALL_Zの平面、法線は手前を向く) + vec3 wall = shade(vec3(p, WALL_Z), vec3(0.0, 0.0, 1.0), WALL, lightPos); - // 時間で動くグラデーション - vec3 color = 0.5 + 0.5 * cos(uTime + uv.xyx + vec3(0.0, 2.0, 4.0)); + float mask = 1.0 - smoothstep(RADIUS - px, RADIUS, length(p)); + vec3 color = mix(wall, sphere, mask); - // マウスの周りに白い円 - float d = length(gl_FragCoord.xy - uMouse); - color = mix(color, vec3(1.0), 1.0 - smoothstep(18.0, 20.0, d)); + // 光の位置に小さな白い点を描く + float marker = 1.0 - smoothstep(0.03 - px, 0.03, length(p - lightPos.xy)); + color = mix(color, vec3(1.0), marker); out_color = vec4(color, 1.0); }