// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;
uniform vec2 resolution;
uniform float time;
#define marchSteps 128
#define maxDist 300.0
#define surfDist 0.01
const float PI = acos(-1.0);
const float TAU = PI * 2.0;
float disturbRadius(float radius, vec3 axis) {
float minRes = min (resolution.x, resolution.y);
vec2 fc = gl_FragCoord.xy / minRes;
float wt = time * 2.0;
vec2 distortOffset = vec2(sin(wt + fc.y * TAU), sin(wt + fc.x * TAU)) * vec2(0.5);
axis.xy += distortOffset;
float disturbed = (
radius * 1.0 +
0.03 * sin(4.0 * time + axis.x * 7.0) +
0.04 * sin(3.0 * time + axis.y * 6.0) +
0.05 * sin(2.0 * time + axis.z * 5.0)
);
return disturbed;
}
float sphereSDF(vec3 p, float radius) {
radius = disturbRadius(radius, p);
return length(p) - radius;
}
float getDist(vec3 p) {
vec3 spherePos = vec3(0.0, 1.5, 0.0);
float sphereRadius = 1.0;
float sphereDist = sphereSDF(p - spherePos, sphereRadius);
float groundDist = p.y;
float dist = min(sphereDist, groundDist);
return dist;
}
float rayMarch(vec3 ro, vec3 rd) {
float dist = 0.0;
for (int i = 0; i < marchSteps; i++) {
vec3 p = ro + rd * dist;
float sceneDist = getDist(p);
dist += sceneDist;
if (dist > maxDist || sceneDist <= surfDist) { break; }
}
return dist;
}
vec3 getNormal(vec3 p) {
float dist = getDist(p);
vec2 closeSample = vec2(0.01, 0.0);
vec3 closeSampleV3 = vec3(
getDist(p - closeSample.xyy),
getDist(p - closeSample.yxy),
getDist(p - closeSample.yyx)
);
vec3 normal = dist - closeSampleV3;
return normalize(normal);
}
float getLight(vec3 p, inout vec3 normal) {
vec3 lightPos = vec3(3.0, 5.0, -1.0);
vec3 lightVec = normalize(lightPos - p);
vec3 lightNor = getNormal(p);
normal = lightNor;
float diffuse = clamp(dot(lightNor, lightVec), 0.0, 1.0);
float distToLight = rayMarch(p + lightNor * surfDist * 15.0, lightVec);
if (distToLight < length(lightPos - p)) { diffuse *= 0.1; }
return diffuse;
}
mat3 calcLookAtMatrix(vec3 origin, vec3 target, float roll) {
vec3 rr = vec3(sin(roll), cos(roll), 0.0);
vec3 ww = normalize(target - origin);
vec3 uu = normalize(cross(ww, rr));
vec3 vv = normalize(cross(uu, ww));
return mat3(uu, vv, ww);
}
void main(void) {
vec2 uv = ((gl_FragCoord.xy / resolution.xy) - 0.5) * vec2(resolution.x / resolution.y, 1.0);
vec3 col = vec3(0.0);
float camX = sin(time * 0.5) * 5.0;
float camY = 1.0;
float camZ = cos(time * 0.5) * 5.0;
vec3 ro = vec3(camX, camY, camZ);
vec3 camTarget = vec3(0.0, 1.0, 0.0);
float camRoll = sin(time * 0.25) * 0.1 + 0.1;
mat3 camMatrix = calcLookAtMatrix(ro, camTarget, camRoll);
vec3 rd = normalize(camMatrix * vec3(uv.x, uv.y, 1.0));
float dist = rayMarch(ro, rd);
vec3 p = ro + rd * dist;
vec3 normal;
float diffuse = getLight(p, normal);
col = vec3(diffuse) * vec3(0.7, 0.6, 1.0) + normal * 0.07;
gl_FragColor = vec4(col, 1.0);
}