【GLSL】レイマーチングで複数の光源が作る影
See the Pen shadow by multiple lights by aadebdeb (@aadebdeb) on CodePen.
#define PI 3.14159265359 #define TWO_PI PI * 2.0 #define HALF_PI PI / 2.0 precision mediump float; uniform float u_time; uniform vec2 u_mouse; uniform vec2 u_resolution; struct Camera { vec3 position; vec3 ray; }; struct Light { vec3 position; vec3 ambientWeight; vec3 diffuseWeight; vec3 specularWeight; }; float random(float v) { return fract(sin(v) * 43758.5453123); } float smoothmin(float d1, float d2, float k) { return -log(exp(-k * d1) + exp(-k * d2)) / k; } vec3 repeat(vec3 p, vec3 interval) { return mod(p, interval) - interval / 2.0; } vec3 repeatX(vec3 p, float interval) { return vec3(mod(p.x, interval) - interval / 2.0, p.y, p.z); } vec3 repeatY(vec3 p, float interval) { return vec3(p.x, mod(p.y, interval) - interval / 2.0, p.z); } vec3 repeatZ(vec3 p, float interval) { return vec3(p.x, p.y, mod(p.z, interval) - interval / 2.0); } vec3 rotateX(vec3 p, float theta) { float c = cos(-theta); float s = sin(-theta); mat3 m = mat3(vec3(1, 0.0, 0.0), vec3(0.0, c, -s), vec3(0.0, s, c)); return m * p; } vec3 rotateY(vec3 p, float theta) { float c = cos(-theta); float s = sin(-theta); mat3 m = mat3(vec3(c, 0.0, s), vec3(0.0, 1.0, 0.0), vec3(-s, 0.0, c)); return m * p; } vec3 rotateZ(vec3 p, float theta) { float c = cos(-theta); float s = sin(-theta); mat3 m = mat3(vec3(c, -s, 0.0), vec3(s, c, 0.0), vec3(0.0, 0.0, 1.0)); return m * p; } vec3 translate(vec3 p, vec3 t) { mat4 m = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(-t.x, -t.y, -t.z, 1.0)); return (m * vec4(p, 1.0)).xyz; } float calcBoxDistance(vec3 p, vec3 size) { return length(max(abs(p) - size, 0.0)); } float calcRoundBoxDistance(vec3 p, vec3 size, float r) { return calcBoxDistance(p, size) - r; } float calcSphereDistance(vec3 p, float size) { return length(p) - size; } float calcPlainDistance(vec3 p, vec3 n) { return dot(p, n); } float calcTorusDistance(vec3 p, vec2 size) { vec2 q = vec2(length(p.xz) - size.x, p.y); return length(q) - size.y; } float calcCylinderDistance(vec3 p, vec3 size) { return length(p.xz - size.xy) - size.z; } float calcDistance(vec3 p) { float d = 10000000.0; d = min(d, calcSphereDistance(translate(p, vec3(0.0, 3.0, 0.0)), 1.0)); d = min(d, calcPlainDistance(p, vec3(0.0, 1.0, 0.0))); return d; } vec3 calcNormal(vec3 p) { float delta = 0.00001; return normalize(vec3( calcDistance(p + vec3(delta, 0.0, 0.0)) - calcDistance(p - vec3(delta, 0.0, 0.0)), calcDistance(p + vec3(0.0, delta, 0.0)) - calcDistance(p - vec3(0.0, delta, 0.0)), calcDistance(p + vec3(0.0, 0.0, delta)) - calcDistance(p - vec3(0.0, 0.0, delta)) )); } Camera getPerspectiveCamera(vec2 pos, vec3 eye, vec3 center, vec3 top, float fov) { float camRadian = fov / 2.0 * PI / 180.0; vec3 viewDir = normalize(center - eye); vec3 camSide = cross(viewDir, top); vec3 camTop = cross(camSide, viewDir); Camera camera; camera.position = eye; camera.ray = normalize(camTop * sin(camRadian * pos.y) + camSide * sin(camRadian * pos.x) + viewDir * cos(camRadian * pos.x)); return camera; } Camera getOrthographicCamera(vec2 pos, vec3 eye, vec3 center, vec3 top, float width, float height) { vec3 viewDir = normalize(center - eye); vec3 camSide = cross(viewDir, top); vec3 camTop = cross(camSide, viewDir); Camera camera; camera.position = eye + vec3(camTop * pos.y * height / 2.0 + camSide * pos.x * width / 2.0); camera.ray = viewDir; return camera; } float calcShadow(vec3 pos, vec3 light) { vec3 lightDir = normalize(light - pos); float depth = 0.01; float d; for (int i = 0; i < 16; i++) { d = calcDistance(pos + lightDir * depth); depth += d; } return d < 0.001 ? 0.5 : 1.0; } float calcSoftShadow(vec3 pos, vec3 light) { vec3 lightDir = normalize(light - pos); float depth = 0.001; float bright = 1.0; float d; float shadowIntensity = 0.7; float shadowSharpness = 8.0; for (int i = 0; i < 32; i++) { d = calcDistance(pos + lightDir * depth); if (d < 0.001) { return 1.0 - shadowIntensity; } bright = min(bright, shadowSharpness * d / depth); depth += d; } return 1.0 - (1.0 - bright) * shadowIntensity; } void main(void) { vec2 st = (gl_FragCoord.xy * 2.0 - u_resolution) / min(u_resolution.x, u_resolution.y); vec2 mouse = (u_mouse * 2.0 - u_resolution) / min(u_resolution.x, u_resolution.y); float time = u_time * 0.001; vec3 cameraPosition = vec3(20.0 * cos(mouse.x * PI), 10.0 + 5.0 * mouse.y, 20.0 * sin(mouse.x * PI)); vec3 viewCenter = vec3(0.0); vec3 cameraTop = vec3(0.0, 1.0, 0.0); Camera camera = getPerspectiveCamera(st, cameraPosition, viewCenter, vec3(0.0, 1.0, 0.0), 60.0); // Camera camera = getOrthographicCamera(st, cameraPosition, viewCenter, vec3(0.0, 1.0, 0.0), 50.0, 50.0); vec3 rayPosition = camera.position; vec3 rayDirection = camera.ray; vec3 color = vec3(0.0); float d; for (int i = 0; i < 256; i++) { d = calcDistance(rayPosition); rayPosition += rayDirection * d; } Light lights[3]; lights[0].position = vec3(10.0 * cos(time),10.0 + 3.0 * sin(time / 3.0), 10.0 * sin(time)); lights[0].ambientWeight = vec3(1.0, 0.0, 0.0); lights[0].diffuseWeight = vec3(1.0, 0.0, 0.0); lights[0].specularWeight = vec3(1.0, 0.0, 0.0); lights[1].position = vec3(10.0 * cos(time * 2.0), 10.0 + 3.0 * sin(time / 2.0), 10.0 * sin(time * 2.0)); lights[1].ambientWeight = vec3(0.0, 1.0, 0.0); lights[1].diffuseWeight = vec3(0.0, 1.0, 0.0); lights[1].specularWeight = vec3(0.0, 1.0, 0.0); lights[2].position = vec3(10.0 * cos(time * 3.0), 10.0 + 3.0 * sin(time), 10.0 * sin(time * 3.0)); lights[2].ambientWeight = vec3(0.0, 0.0, 1.0); lights[2].diffuseWeight = vec3(0.0, 0.0, 1.0); lights[2].specularWeight = vec3(0.0, 0.0, 1.0); if (d < 0.0001) { vec3 normal = calcNormal(rayPosition); color = vec3(0.0); for (int i = 0; i < 3; i++) { Light light = lights[i]; vec3 vecToLight = normalize(light.position - rayPosition); float diffuseWeight = max(dot(normal, vecToLight), 0.0); vec3 reflectVec = normalize(reflect(-vecToLight, normal)); float specularWeight = pow(max(dot(reflectVec, -rayDirection), 0.0), 16.0); float shadow = calcSoftShadow(rayPosition + normal * 0.01, light.position); vec3 c = vec3(0.3, 0.3, 0.3) * light.ambientWeight + vec3(0.5, 0.5, 0.5) * light.diffuseWeight * diffuseWeight * shadow + vec3(1.0, 1.0, 1.0) * light.specularWeight * specularWeight; // c *= shadow; color += c; // color *= shadow; } } gl_FragColor = vec4(color, 1.0); }