【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);
}