1 Day 5 Shaders
Complete, runnable shaders for each demo in the notes.
1.1 ray-visualization
struct Ray {
vec3 origin;
vec3 dir;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
vec3 color = ray.dir * 0.5 + 0.5;
fragColor = vec4(color, 1.0);
}1.2 sphere-flat
struct Ray {
vec3 origin;
vec3 dir;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
float intersectSphere(Ray ray, vec3 center, float radius) {
vec3 delta = ray.origin - center;
float b = dot(delta, ray.dir);
float c = dot(delta, delta) - radius * radius;
float discriminant = b * b - c;
if (discriminant < 0.0) return -1.0;
float sqrtDisc = sqrt(discriminant);
float t1 = -b - sqrtDisc;
float t2 = -b + sqrtDisc;
if (t1 > 0.0) return t1;
if (t2 > 0.0) return t2;
return -1.0;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
float t = intersectSphere(ray, vec3(0.0, 0.0, -3.0), 1.0);
vec3 color = vec3(0.1, 0.1, 0.2);
if (t > 0.0) color = vec3(1.0, 0.0, 0.0);
fragColor = vec4(color, 1.0);
}1.3 sphere-normals
struct Ray {
vec3 origin;
vec3 dir;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
float intersectSphere(Ray ray, vec3 center, float radius) {
vec3 delta = ray.origin - center;
float b = dot(delta, ray.dir);
float c = dot(delta, delta) - radius * radius;
float discriminant = b * b - c;
if (discriminant < 0.0) return -1.0;
float sqrtDisc = sqrt(discriminant);
float t1 = -b - sqrtDisc;
float t2 = -b + sqrtDisc;
if (t1 > 0.0) return t1;
if (t2 > 0.0) return t2;
return -1.0;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
vec3 sphereCenter = vec3(0.0, 0.0, -3.0);
float sphereRadius = 1.0;
float t = intersectSphere(ray, sphereCenter, sphereRadius);
vec3 color = vec3(0.1, 0.1, 0.2);
if (t > 0.0) {
vec3 hitPoint = ray.origin + t * ray.dir;
vec3 normal = (hitPoint - sphereCenter) / sphereRadius;
color = normal * 0.5 + 0.5;
}
fragColor = vec4(color, 1.0);
}1.4 sphere-lit
struct Ray {
vec3 origin;
vec3 dir;
};
struct Hit {
float t;
vec3 point;
vec3 normal;
vec3 color;
};
struct Light {
vec3 dir;
vec3 color;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
Hit intersectSphere(Ray ray, vec3 center, float radius, vec3 color) {
Hit hit;
hit.t = -1.0;
vec3 delta = ray.origin - center;
float b = dot(delta, ray.dir);
float c = dot(delta, delta) - radius * radius;
float discriminant = b * b - c;
if (discriminant < 0.0) return hit;
float sqrtDisc = sqrt(discriminant);
float t1 = -b - sqrtDisc;
float t2 = -b + sqrtDisc;
if (t1 > 0.0) hit.t = t1;
else if (t2 > 0.0) hit.t = t2;
else return hit;
hit.point = ray.origin + hit.t * ray.dir;
hit.normal = (hit.point - center) / radius;
hit.color = color;
return hit;
}
vec3 shade(Hit hit, Light light, vec3 viewDir) {
float diffuse = max(0.0, dot(hit.normal, light.dir));
vec3 reflected = reflect(-light.dir, hit.normal);
float specular = pow(max(0.0, dot(reflected, viewDir)), 32.0);
vec3 diff = hit.color * light.color * diffuse;
vec3 spec = light.color * specular * 0.5;
return diff + spec;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
Light light = Light(normalize(vec3(1.0, 1.0, 1.0)), vec3(1.0));
Hit hit = intersectSphere(ray, vec3(0.0, 0.0, -3.0), 1.0, vec3(1.0, 0.0, 0.0));
vec3 color = vec3(0.1, 0.1, 0.2);
if (hit.t > 0.0) {
vec3 ambient = hit.color * 0.1;
color = ambient + shade(hit, light, -ray.dir);
}
fragColor = vec4(color, 1.0);
}1.5 sphere-orbit
struct Ray {
vec3 origin;
vec3 dir;
};
struct Hit {
float t;
vec3 point;
vec3 normal;
vec3 color;
};
struct Light {
vec3 dir;
vec3 color;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
mat3 rotateX(float a) {
float c = cos(a), s = sin(a);
return mat3(1, 0, 0, 0, c, -s, 0, s, c);
}
mat3 rotateY(float a) {
float c = cos(a), s = sin(a);
return mat3(c, 0, s, 0, 1, 0, -s, 0, c);
}
Ray orbitCamera(Ray ray, float distance) {
vec2 mouse = iMouse.xy / iResolution.xy;
float angleY = (mouse.x - 0.5) * 6.28;
float angleX = (0.5 - mouse.y) * 3.14;
mat3 rot = rotateX(angleX) * rotateY(angleY);
ray.origin = rot * vec3(0.0, 0.0, distance);
ray.dir = rot * ray.dir;
return ray;
}
Hit intersectSphere(Ray ray, vec3 center, float radius, vec3 color) {
Hit hit;
hit.t = -1.0;
vec3 delta = ray.origin - center;
float b = dot(delta, ray.dir);
float c = dot(delta, delta) - radius * radius;
float discriminant = b * b - c;
if (discriminant < 0.0) return hit;
float sqrtDisc = sqrt(discriminant);
float t1 = -b - sqrtDisc;
float t2 = -b + sqrtDisc;
if (t1 > 0.0) hit.t = t1;
else if (t2 > 0.0) hit.t = t2;
else return hit;
hit.point = ray.origin + hit.t * ray.dir;
hit.normal = (hit.point - center) / radius;
hit.color = color;
return hit;
}
vec3 shade(Hit hit, Light light, vec3 viewDir) {
float diffuse = max(0.0, dot(hit.normal, light.dir));
vec3 reflected = reflect(-light.dir, hit.normal);
float specular = pow(max(0.0, dot(reflected, viewDir)), 32.0);
vec3 diff = hit.color * light.color * diffuse;
vec3 spec = light.color * specular * 0.5;
return diff + spec;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
ray = orbitCamera(ray, 5.0);
Light light = Light(normalize(vec3(1.0, 1.0, 1.0)), vec3(1.0));
Hit hit = intersectSphere(ray, vec3(0.0), 1.0, vec3(1.0, 0.0, 0.0));
vec3 color = vec3(0.1, 0.1, 0.2);
if (hit.t > 0.0) {
vec3 ambient = hit.color * 0.1;
color = ambient + shade(hit, light, -ray.dir);
}
fragColor = vec4(color, 1.0);
}1.6 torus-analytical
struct Ray {
vec3 origin;
vec3 dir;
};
struct Hit {
float t;
vec3 point;
vec3 normal;
vec3 color;
};
struct Light {
vec3 dir;
vec3 color;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
mat3 rotateX(float a) {
float c = cos(a), s = sin(a);
return mat3(1, 0, 0, 0, c, -s, 0, s, c);
}
mat3 rotateY(float a) {
float c = cos(a), s = sin(a);
return mat3(c, 0, s, 0, 1, 0, -s, 0, c);
}
Ray orbitCamera(Ray ray, float distance) {
vec2 mouse = iMouse.xy / iResolution.xy;
float angleY = (mouse.x - 0.5) * 6.28;
float angleX = (0.5 - mouse.y) * 3.14;
mat3 rot = rotateX(angleX) * rotateY(angleY);
ray.origin = rot * vec3(0.0, 0.0, distance);
ray.dir = rot * ray.dir;
return ray;
}
// Torus intersection by Inigo Quilez
// https://iquilezles.org/articles/intersectors/
float intersectTorusRaw(Ray ray, vec2 tor) {
float po = 1.0;
float Ra2 = tor.x * tor.x;
float ra2 = tor.y * tor.y;
float m = dot(ray.origin, ray.origin);
float n = dot(ray.origin, ray.dir);
float h = n*n - m + (tor.x + tor.y) * (tor.x + tor.y);
if (h < 0.0) return -1.0;
float k = (m - ra2 - Ra2) / 2.0;
float k3 = n;
float k2 = n*n + Ra2*ray.dir.y*ray.dir.y + k;
float k1 = k*n + Ra2*ray.origin.y*ray.dir.y;
float k0 = k*k + Ra2*ray.origin.y*ray.origin.y - Ra2*ra2;
if (abs(k3*(k3*k3 - k2) + k1) < 0.01) {
po = -1.0;
float tmp = k1; k1 = k3; k3 = tmp;
k0 = 1.0/k0;
k1 = k1*k0;
k2 = k2*k0;
k3 = k3*k0;
}
float c2 = 2.0*k2 - 3.0*k3*k3;
float c1 = k3*(k3*k3 - k2) + k1;
float c0 = k3*(k3*(-3.0*k3*k3 + 4.0*k2) - 8.0*k1) + 4.0*k0;
c2 /= 3.0;
c1 *= 2.0;
c0 /= 3.0;
float Q = c2*c2 + c0;
float R = 3.0*c0*c2 - c2*c2*c2 - c1*c1;
float h2 = R*R - Q*Q*Q;
float z = 0.0;
if (h2 < 0.0) {
float sQ = sqrt(Q);
z = 2.0*sQ*cos(acos(R/(sQ*Q)) / 3.0);
} else {
float sQ = pow(sqrt(h2) + abs(R), 1.0/3.0);
z = sign(R)*abs(sQ + Q/sQ);
}
z = c2 - z;
float d1 = z - 3.0*c2;
float d2 = z*z - 3.0*c0;
if (abs(d1) < 1.0e-4) {
if (d2 < 0.0) return -1.0;
d2 = sqrt(d2);
} else {
if (d1 < 0.0) return -1.0;
d1 = sqrt(d1/2.0);
d2 = c1/d1;
}
float result = 1e20;
h2 = d1*d1 - z + d2;
if (h2 > 0.0) {
h2 = sqrt(h2);
float t1 = -d1 - h2 - k3;
float t2 = -d1 + h2 - k3;
t1 = (po < 0.0) ? 2.0/t1 : t1;
t2 = (po < 0.0) ? 2.0/t2 : t2;
if (t1 > 0.0) result = t1;
if (t2 > 0.0) result = min(result, t2);
}
h2 = d1*d1 - z - d2;
if (h2 > 0.0) {
h2 = sqrt(h2);
float t1 = d1 - h2 - k3;
float t2 = d1 + h2 - k3;
t1 = (po < 0.0) ? 2.0/t1 : t1;
t2 = (po < 0.0) ? 2.0/t2 : t2;
if (t1 > 0.0) result = min(result, t1);
if (t2 > 0.0) result = min(result, t2);
}
if (result > 1e10) return -1.0;
return result;
}
vec3 torusNormal(vec3 p, vec2 tor) {
float R = tor.x;
float k = sqrt(p.x*p.x + p.z*p.z);
return normalize(vec3(p.x * (1.0 - R/k), p.y, p.z * (1.0 - R/k)));
}
Hit intersectTorus(Ray ray, vec2 tor, vec3 color) {
Hit hit;
hit.t = intersectTorusRaw(ray, tor);
if (hit.t > 0.0) {
hit.point = ray.origin + hit.t * ray.dir;
hit.normal = torusNormal(hit.point, tor);
hit.color = color;
}
return hit;
}
vec3 shade(Hit hit, Light light, vec3 viewDir) {
float diffuse = max(0.0, dot(hit.normal, light.dir));
vec3 reflected = reflect(-light.dir, hit.normal);
float specular = pow(max(0.0, dot(reflected, viewDir)), 32.0);
vec3 diff = hit.color * light.color * diffuse;
vec3 spec = light.color * specular * 0.5;
return diff + spec;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
ray = orbitCamera(ray, 4.0);
Light light = Light(normalize(vec3(1.0, 1.0, 1.0)), vec3(1.0));
// Torus with major radius 1.0, minor radius 0.4
Hit hit = intersectTorus(ray, vec2(1.0, 0.4), vec3(0.0, 0.7, 1.0));
vec3 color = vec3(0.1, 0.1, 0.2);
if (hit.t > 0.0) {
vec3 ambient = hit.color * 0.1;
color = ambient + shade(hit, light, -ray.dir);
}
fragColor = vec4(color, 1.0);
}1.7 sdf-circle-2d
float sdCircle(vec2 p, float r) {
return length(p) - r;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 2.0;
float d = sdCircle(p, 1.0);
// Color by sign: blue inside, orange outside
vec3 color;
if (d < 0.0) {
color = vec3(0.2, 0.4, 0.8); // inside: blue
} else {
color = vec3(0.9, 0.6, 0.2); // outside: orange
}
// Darken near the boundary
color *= 1.0 - exp(-6.0 * abs(d));
// Contour lines
float contour = abs(fract(d * 4.0 + 0.5) - 0.5);
color = mix(vec3(1.0), color, smoothstep(0.0, 0.05, contour));
// Highlight the zero level set
color = mix(vec3(1.0), color, smoothstep(0.0, 0.02, abs(d)));
fragColor = vec4(color, 1.0);
}1.8 sdf-box-2d
float sdBox(vec2 p, vec2 halfSize) {
vec2 d = abs(p) - halfSize;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 2.0;
float d = sdBox(p, vec2(0.8, 0.5));
// Color by sign: blue inside, orange outside
vec3 color;
if (d < 0.0) {
color = vec3(0.2, 0.4, 0.8); // inside: blue
} else {
color = vec3(0.9, 0.6, 0.2); // outside: orange
}
// Darken near the boundary
color *= 1.0 - exp(-6.0 * abs(d));
// Contour lines
float contour = abs(fract(d * 4.0 + 0.5) - 0.5);
color = mix(vec3(1.0), color, smoothstep(0.0, 0.05, contour));
// Highlight the zero level set
color = mix(vec3(1.0), color, smoothstep(0.0, 0.02, abs(d)));
fragColor = vec4(color, 1.0);
}1.9 raymarch-sphere
struct Ray {
vec3 origin;
vec3 dir;
};
struct Hit {
float t;
vec3 point;
vec3 normal;
vec3 color;
};
struct Light {
vec3 dir;
vec3 color;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
mat3 rotateX(float a) {
float c = cos(a), s = sin(a);
return mat3(1, 0, 0, 0, c, -s, 0, s, c);
}
mat3 rotateY(float a) {
float c = cos(a), s = sin(a);
return mat3(c, 0, s, 0, 1, 0, -s, 0, c);
}
Ray orbitCamera(Ray ray, float distance) {
vec2 mouse = iMouse.xy / iResolution.xy;
float angleY = (mouse.x - 0.5) * 6.28;
float angleX = (0.5 - mouse.y) * 3.14;
mat3 rot = rotateX(angleX) * rotateY(angleY);
ray.origin = rot * vec3(0.0, 0.0, distance);
ray.dir = rot * ray.dir;
return ray;
}
float sceneSDF(vec3 p) {
return length(p) - 1.0; // Unit sphere at origin
}
vec3 calcNormal(vec3 p) {
float eps = 0.001;
return normalize(vec3(
sceneSDF(p + vec3(eps, 0, 0)) - sceneSDF(p - vec3(eps, 0, 0)),
sceneSDF(p + vec3(0, eps, 0)) - sceneSDF(p - vec3(0, eps, 0)),
sceneSDF(p + vec3(0, 0, eps)) - sceneSDF(p - vec3(0, 0, eps))
));
}
float raymarch(Ray ray) {
float t = 0.0;
for (int i = 0; i < 100; i++) {
vec3 p = ray.origin + t * ray.dir;
float d = sceneSDF(p);
if (d < 0.001) return t;
t += d;
if (t > 100.0) return -1.0;
}
return -1.0;
}
vec3 shade(Hit hit, Light light, vec3 viewDir) {
float diffuse = max(0.0, dot(hit.normal, light.dir));
vec3 reflected = reflect(-light.dir, hit.normal);
float specular = pow(max(0.0, dot(reflected, viewDir)), 32.0);
vec3 diff = hit.color * light.color * diffuse;
vec3 spec = light.color * specular * 0.5;
return diff + spec;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
ray = orbitCamera(ray, 5.0);
Light light = Light(normalize(vec3(1.0, 1.0, 1.0)), vec3(1.0));
float t = raymarch(ray);
vec3 color = vec3(0.1, 0.1, 0.2);
if (t > 0.0) {
vec3 p = ray.origin + t * ray.dir;
vec3 normal = calcNormal(p);
Hit hit;
hit.t = t;
hit.point = p;
hit.normal = normal;
hit.color = vec3(1.0, 0.0, 0.0);
vec3 ambient = hit.color * 0.1;
color = ambient + shade(hit, light, -ray.dir);
}
fragColor = vec4(color, 1.0);
}1.10 raymarch-torus
struct Ray {
vec3 origin;
vec3 dir;
};
struct Hit {
float t;
vec3 point;
vec3 normal;
vec3 color;
};
struct Light {
vec3 dir;
vec3 color;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
mat3 rotateX(float a) {
float c = cos(a), s = sin(a);
return mat3(1, 0, 0, 0, c, -s, 0, s, c);
}
mat3 rotateY(float a) {
float c = cos(a), s = sin(a);
return mat3(c, 0, s, 0, 1, 0, -s, 0, c);
}
Ray orbitCamera(Ray ray, float distance) {
vec2 mouse = iMouse.xy / iResolution.xy;
float angleY = (mouse.x - 0.5) * 6.28;
float angleX = (0.5 - mouse.y) * 3.14;
mat3 rot = rotateX(angleX) * rotateY(angleY);
ray.origin = rot * vec3(0.0, 0.0, distance);
ray.dir = rot * ray.dir;
return ray;
}
float sceneSDF(vec3 p) {
vec2 tor = vec2(1.0, 0.4); // major radius, minor radius
vec2 q = vec2(length(p.xz) - tor.x, p.y);
return length(q) - tor.y;
}
vec3 calcNormal(vec3 p) {
float eps = 0.001;
return normalize(vec3(
sceneSDF(p + vec3(eps, 0, 0)) - sceneSDF(p - vec3(eps, 0, 0)),
sceneSDF(p + vec3(0, eps, 0)) - sceneSDF(p - vec3(0, eps, 0)),
sceneSDF(p + vec3(0, 0, eps)) - sceneSDF(p - vec3(0, 0, eps))
));
}
float raymarch(Ray ray) {
float t = 0.0;
for (int i = 0; i < 100; i++) {
vec3 p = ray.origin + t * ray.dir;
float d = sceneSDF(p);
if (d < 0.001) return t;
t += d;
if (t > 100.0) return -1.0;
}
return -1.0;
}
vec3 shade(Hit hit, Light light, vec3 viewDir) {
float diffuse = max(0.0, dot(hit.normal, light.dir));
vec3 reflected = reflect(-light.dir, hit.normal);
float specular = pow(max(0.0, dot(reflected, viewDir)), 32.0);
vec3 diff = hit.color * light.color * diffuse;
vec3 spec = light.color * specular * 0.5;
return diff + spec;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
ray = orbitCamera(ray, 4.0);
Light light = Light(normalize(vec3(1.0, 1.0, 1.0)), vec3(1.0));
float t = raymarch(ray);
vec3 color = vec3(0.1, 0.1, 0.2);
if (t > 0.0) {
vec3 p = ray.origin + t * ray.dir;
vec3 normal = calcNormal(p);
Hit hit;
hit.t = t;
hit.point = p;
hit.normal = normal;
hit.color = vec3(0.0, 0.7, 1.0);
vec3 ambient = hit.color * 0.1;
color = ambient + shade(hit, light, -ray.dir);
}
fragColor = vec4(color, 1.0);
}1.11 scene-multi
struct Ray {
vec3 origin;
vec3 dir;
};
struct Hit {
float t;
vec3 point;
vec3 normal;
vec3 color;
};
struct Light {
vec3 dir;
vec3 color;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
mat3 rotateX(float a) {
float c = cos(a), s = sin(a);
return mat3(1, 0, 0, 0, c, -s, 0, s, c);
}
mat3 rotateY(float a) {
float c = cos(a), s = sin(a);
return mat3(c, 0, s, 0, 1, 0, -s, 0, c);
}
Ray orbitCamera(Ray ray, float distance) {
vec2 mouse = iMouse.xy / iResolution.xy;
float angleY = (mouse.x - 0.5) * 6.28;
float angleX = (0.5 - mouse.y) * 3.14;
mat3 rot = rotateX(angleX) * rotateY(angleY);
ray.origin = rot * vec3(0.0, 0.0, distance);
ray.dir = rot * ray.dir;
return ray;
}
float sdTorus(vec3 p, vec2 tor) {
vec2 q = vec2(length(p.xz) - tor.x, p.y);
return length(q) - tor.y;
}
float sceneSDF(vec3 p) {
float sphere = length(p - vec3(-1.5, 0.0, 0.0)) - 1.0;
float torus = sdTorus(p - vec3(1.5, 0.0, 0.0), vec2(0.8, 0.3));
float ground = p.y + 1.0;
return min(sphere, min(torus, ground));
}
vec3 calcNormal(vec3 p) {
float eps = 0.001;
return normalize(vec3(
sceneSDF(p + vec3(eps, 0, 0)) - sceneSDF(p - vec3(eps, 0, 0)),
sceneSDF(p + vec3(0, eps, 0)) - sceneSDF(p - vec3(0, eps, 0)),
sceneSDF(p + vec3(0, 0, eps)) - sceneSDF(p - vec3(0, 0, eps))
));
}
float raymarch(Ray ray) {
float t = 0.0;
for (int i = 0; i < 100; i++) {
vec3 p = ray.origin + t * ray.dir;
float d = sceneSDF(p);
if (d < 0.001) return t;
t += d;
if (t > 100.0) return -1.0;
}
return -1.0;
}
vec3 shade(Hit hit, Light light, vec3 viewDir) {
float diffuse = max(0.0, dot(hit.normal, light.dir));
vec3 reflected = reflect(-light.dir, hit.normal);
float specular = pow(max(0.0, dot(reflected, viewDir)), 32.0);
vec3 diff = hit.color * light.color * diffuse;
vec3 spec = light.color * specular * 0.5;
return diff + spec;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
ray = orbitCamera(ray, 6.0);
Light light = Light(normalize(vec3(1.0, 1.0, 1.0)), vec3(1.0));
float t = raymarch(ray);
vec3 color = vec3(0.1, 0.1, 0.2);
if (t > 0.0) {
vec3 p = ray.origin + t * ray.dir;
vec3 normal = calcNormal(p);
Hit hit;
hit.t = t;
hit.point = p;
hit.normal = normal;
hit.color = vec3(0.9, 0.5, 0.2);
vec3 ambient = hit.color * 0.1;
color = ambient + shade(hit, light, -ray.dir);
}
fragColor = vec4(color, 1.0);
}1.12 barth-sextic-final
struct Ray {
vec3 origin;
vec3 dir;
};
struct Hit {
float t;
vec3 point;
vec3 normal;
vec3 color;
};
struct Light {
vec3 dir;
vec3 color;
};
Ray generateRay(vec2 fragCoord) {
vec2 uv = (fragCoord / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float fov = 90.0;
float f = 1.0 / tan(radians(fov) / 2.0);
Ray ray;
ray.origin = vec3(0.0);
ray.dir = normalize(vec3(uv, -f));
return ray;
}
mat3 rotateX(float a) {
float c = cos(a), s = sin(a);
return mat3(1, 0, 0, 0, c, -s, 0, s, c);
}
mat3 rotateY(float a) {
float c = cos(a), s = sin(a);
return mat3(c, 0, s, 0, 1, 0, -s, 0, c);
}
Ray orbitCamera(Ray ray, float distance) {
vec2 mouse = iMouse.xy / iResolution.xy;
float angleY = (mouse.x - 0.5) * 6.28;
float angleX = (0.5 - mouse.y) * 3.14;
mat3 rot = rotateX(angleX) * rotateY(angleY);
ray.origin = rot * vec3(0.0, 0.0, distance);
ray.dir = rot * ray.dir;
return ray;
}
float polynomial(vec3 p) {
float phi = (1.0 + sqrt(5.0)) / 2.0;
float phi2 = phi * phi;
float x2 = p.x * p.x, y2 = p.y * p.y, z2 = p.z * p.z;
float a = (phi2 * x2 - y2) * (phi2 * y2 - z2) * (phi2 * z2 - x2);
float b = (x2 + y2 + z2 - 1.0);
return 4.0 * a - (1.0 + 2.0 * phi) * b * b;
}
vec3 gradient(vec3 p) {
float eps = 0.001;
return vec3(
polynomial(p + vec3(eps, 0, 0)) - polynomial(p - vec3(eps, 0, 0)),
polynomial(p + vec3(0, eps, 0)) - polynomial(p - vec3(0, eps, 0)),
polynomial(p + vec3(0, 0, eps)) - polynomial(p - vec3(0, 0, eps))
) / (2.0 * eps);
}
float sdBarth(vec3 p) {
// Rotate the Barth surface
mat3 spin = rotateY(iTime * 0.3);
p = spin * p;
// Bounding sphere
float bounds = length(p) - 2.5;
if (bounds > 0.01) return bounds;
// Distance estimate for algebraic variety
float f = polynomial(p);
vec3 g = gradient(p);
float glen = length(g);
if (glen < 0.001) return 0.1;
return 0.5 * abs(f) / glen;
}
float sceneSDF(vec3 p) {
float barth = sdBarth(p);
float floor = p.y + 1.8;
return min(barth, floor);
}
vec3 sceneColor(vec3 p) {
float barth = sdBarth(p);
float floor = p.y + 1.8;
if (barth < floor) {
return vec3(0.9, 0.7, 0.5); // Barth: gold
}
return vec3(0.3, 0.3, 0.35); // Floor: gray
}
vec3 calcNormal(vec3 p) {
float eps = 0.001;
return normalize(vec3(
sceneSDF(p + vec3(eps, 0, 0)) - sceneSDF(p - vec3(eps, 0, 0)),
sceneSDF(p + vec3(0, eps, 0)) - sceneSDF(p - vec3(0, eps, 0)),
sceneSDF(p + vec3(0, 0, eps)) - sceneSDF(p - vec3(0, 0, eps))
));
}
float raymarch(Ray ray) {
float t = 0.0;
for (int i = 0; i < 200; i++) {
vec3 p = ray.origin + t * ray.dir;
float d = sceneSDF(p);
if (d < 0.0005) return t;
t += d;
if (t > 100.0) return -1.0;
}
return -1.0;
}
float shadow(vec3 origin, vec3 lightDir, float maxDist) {
float t = 0.02;
for (int i = 0; i < 50; i++) {
float d = sceneSDF(origin + lightDir * t);
if (d < 0.001) return 0.0;
t += d;
if (t > maxDist) break;
}
return 1.0;
}
vec3 shade(Hit hit, Light light, vec3 viewDir, float shadowFactor) {
float diffuse = max(0.0, dot(hit.normal, light.dir));
vec3 reflected = reflect(-light.dir, hit.normal);
float specular = pow(max(0.0, dot(reflected, viewDir)), 64.0);
vec3 diff = hit.color * light.color * diffuse * shadowFactor;
vec3 spec = light.color * specular * 0.5 * shadowFactor;
return diff + spec;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Ray ray = generateRay(fragCoord);
ray = orbitCamera(ray, 5.0);
// Two colored lights
Light light1 = Light(normalize(vec3(1.0, 2.0, 1.0)), vec3(1.0, 0.8, 0.6));
Light light2 = Light(normalize(vec3(-1.0, 1.0, -0.5)), vec3(0.3, 0.4, 0.8));
float t = raymarch(ray);
vec3 color = vec3(0.05, 0.05, 0.1);
if (t > 0.0) {
vec3 p = ray.origin + t * ray.dir;
vec3 normal = calcNormal(p);
// Flip normal if facing away from camera (for Barth surface)
if (dot(normal, ray.dir) > 0.0) normal = -normal;
Hit hit;
hit.t = t;
hit.point = p;
hit.normal = normal;
hit.color = sceneColor(p);
float shadow1 = shadow(p, light1.dir, 20.0);
float shadow2 = shadow(p, light2.dir, 20.0);
vec3 ambient = hit.color * 0.1;
color = ambient + shade(hit, light1, -ray.dir, shadow1) + shade(hit, light2, -ray.dir, shadow2);
}
fragColor = vec4(color, 1.0);
}