160 lines
6.6 KiB
GLSL
160 lines
6.6 KiB
GLSL
// Name: Just Another Cube
|
||
// Author: mrange
|
||
// URL: https://www.shadertoy.com/view/3XdXRr
|
||
#version 330 core
|
||
precision highp float;
|
||
|
||
out vec4 FragColor;
|
||
in vec2 vUV;
|
||
uniform vec2 iResolution;
|
||
uniform float iTime;
|
||
|
||
// CC0: Just another cube
|
||
// Glowtracers are great for compact coding, but I wanted to see how much
|
||
// I could squeeze a more normal raymarcher in terms of characters used.
|
||
|
||
// Twigl: https://twigl.app?ol=true&ss=-OW-y9xgRgWubwKcn0Nd
|
||
|
||
// == Globals ==
|
||
// Single-letter variable names are used to save characters (code golfing).
|
||
mat2 R; // A 2D rotation matrix, calculated once per frame in mainImage and used by D.
|
||
float d=1. // Stores the most recent distance to the scene from the ray's position.
|
||
, z=0. // Stores the total distance traveled along the ray (initialized to avoid undefined behavior)
|
||
, G=9. // "Glow" variable. Tracks the closest the ray comes to the object (for volumetric glow effect).
|
||
, M=1e-3
|
||
;
|
||
// == Distance Function (SDF - Signed Distance Field) ==
|
||
// This function calculates the shortest distance from a given point 'p' to the scene geometry.
|
||
// A positive result means the point is outside an object, negative is inside, and zero is on the surface.
|
||
// This is the core of "raymarching", as it tells us the largest safe step we can take along a ray.
|
||
float D(vec3 p) {
|
||
// Apply two rotations to the point's coordinates. This twists the space the object
|
||
// exists in, making the simple cube shape appear more complex and animated.
|
||
p.xy *= R;
|
||
p.xz *= R;
|
||
|
||
// Create a higher-frequency version of the coordinate for detailed surface patterns.
|
||
vec3 S = sin(123.*p);
|
||
|
||
// This creates a volumetric glow effect by tracking the minimum distance
|
||
// to either the existing glow value or a glowing shell around the object.
|
||
G = min(
|
||
G
|
||
// The glowing shell
|
||
, max(
|
||
abs(length(p)-.6)
|
||
// The main object distance calculation:
|
||
// 1. A superquadric (rounded cube shape) is created using an L8-norm.
|
||
// The expression `pow(dot(p=p*p*p*p,p),.125)` is a golfed version of
|
||
// `pow(pow(p.x,8)+pow(p.y,8)+pow(p.z,8), 1./8.)`.
|
||
// The `- .5` defines the object's size.
|
||
, d = pow(dot(p*=p*p*p,p),.125) - .5
|
||
// 2. Surface detail subtraction. This creates small surface variations
|
||
// using high-frequency sine waves for more appealing reflections.
|
||
- pow(1.+S.x*S.y*S.z,8.)/1e5
|
||
)
|
||
);
|
||
|
||
return d;
|
||
}
|
||
|
||
// == Main Render Function ==
|
||
// This function is called for every pixel on the screen to determine its color.
|
||
// 'o' is the final output color (rgba). 'C' is the input pixel coordinate (xy).
|
||
void mainImage(out vec4 o, vec2 C) {
|
||
// Single-letter variable names are used to save characters (code golfing).
|
||
vec3 p // The current point in 3D space along the ray.
|
||
, O // Multi-purpose vector: color accumulator, then normal vector, then final color.
|
||
, r=vec3(iResolution.xy, iResolution.y) // 'r' holds screen resolution, later re-used for the epsilon vector and reflection.
|
||
// 'I' is the Ray Direction vector. It's calculated once per pixel.
|
||
// This converts the 2D screen coordinate 'C' into a 3D direction, creating the camera perspective.
|
||
, I=normalize(vec3(C-.5*r.xy, r.y))
|
||
// Base glow color (dark bluish tint).
|
||
, B=vec3(1,2,9)*M
|
||
;
|
||
|
||
// == Raymarching Loop ==
|
||
// This loop "marches" a ray from the camera out into the scene to find what it hits.
|
||
// It uses a golfed structure where the body of the loop updates the ray position 'p',
|
||
// and the "advancement" step moves the ray forward.
|
||
for(
|
||
// -- Initializer (runs once before the loop) --
|
||
// Calculate the rotation matrix for this frame based on time.
|
||
R = mat2(cos(.3*iTime+vec4(0,11,33,0)))
|
||
// -- Condition --
|
||
// Loop while total distance 'z' is less than 9 and we are not yet touching a surface (d > 1e-3).
|
||
; z<9. && d > M
|
||
// -- Advancement --
|
||
// The ray advances by the safe distance 'd' returned by D(p).
|
||
// The result of D(p) is also assigned to the global 'd' inside the function.
|
||
; z += D(p)
|
||
)
|
||
// -- Loop Body --
|
||
// Calculate the current position 'p' in world space.
|
||
// The camera starts at (0,0,-2) and points forward.
|
||
p = z*I
|
||
, p.z -= 2.
|
||
;
|
||
|
||
// -- Hit Condition --
|
||
// If the loop finished because z exceeded the max distance, we hit nothing. Otherwise, we hit the surface.
|
||
if (z < 9.) {
|
||
// -- Calculate Surface Normal --
|
||
// Estimate the gradient ∇D at the hit point 'p' via central differences on the SDF D.
|
||
// We use ε = 1e-3 and loop over each axis (x, y, z):
|
||
// • Zero r, then set r[i] = ε.
|
||
// • Compute O[i] = D(p + r) – D(p – r).
|
||
// After the loop, O holds the unnormalized normal vector.
|
||
for (
|
||
int i=0 // axis index: 0→x, 1→y, 2→z (initialized to avoid warnings)
|
||
; i < 3
|
||
; O[i++] = D(p+r) - D(p-r)
|
||
)
|
||
r -= r // clear r to vec3(0)
|
||
, r[i] = M // set only the i-th component
|
||
;
|
||
|
||
// -- Lighting and Shading --
|
||
// 'z' is re-purposed to store a fresnel factor (1 - cos(angle)) for edge brightness.
|
||
// `dot(O, I)` calculates how much the surface faces away from the camera.
|
||
// O is also normalized here to become a proper normal vector.
|
||
z = 1.+dot(O = normalize(O),I);
|
||
|
||
// 'r' is re-purposed to store the reflection vector.
|
||
r = reflect(I,O);
|
||
|
||
// Calculate a point 'C' along the reflection vector 'r' to sample a background color.
|
||
// For upward reflections (r.y > 0), this finds the intersection with the plane y=5.
|
||
C = (p+r*(5.-p.y)/abs(r.y)).xz;
|
||
|
||
// Calculate the final color 'O' of the hit point.
|
||
O =
|
||
// Multiply by the fresnel factor squared for stronger edge reflections.
|
||
z*z *
|
||
// Use a ternary operator to decide the color based on where the reflection ray goes.
|
||
(
|
||
// If the reflection vector points upward...
|
||
r.y>0.
|
||
// ...sample a procedural "sky" with a radial gradient and blue tint.
|
||
? 5e2*smoothstep(5., 4., d = sqrt(length(C*C))+1.)*d*B
|
||
// ...otherwise, sample a "floor" with a deep blue exponential falloff.
|
||
: exp(-2.*length(C))*(B/M-1.)
|
||
)
|
||
// Add rim lighting (brighter on upward-facing surfaces).
|
||
+ pow(1.+O.y,5.)*B
|
||
;
|
||
}
|
||
|
||
// == Tonemapping & Output ==
|
||
// Apply final effects and map the High Dynamic Range (HDR) color to a displayable range.
|
||
// Add glow contribution: smaller G values (closer ray passes) create a brighter blue glow.
|
||
o = sqrt(O+B/G).xyzx;
|
||
}
|
||
|
||
void main() {
|
||
vec2 fragCoordPixels = vUV * iResolution;
|
||
vec4 outColor;
|
||
mainImage(outColor, fragCoordPixels);
|
||
FragColor = outColor;
|
||
}
|