Initial commit: add all skills files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 16:52:49 +08:00
commit 6487becf60
396 changed files with 108871 additions and 0 deletions

View File

@@ -0,0 +1,525 @@
# Fractal Rendering — Detailed Reference
This document is a detailed supplement to [SKILL.md](SKILL.md), containing prerequisites, step-by-step explanations, mathematical derivations, variant descriptions, in-depth performance analysis, and complete combination example code.
## Prerequisites
- **GLSL Basics**: uniform, varying, built-in functions (`dot`, `length`, `normalize`, `abs`, `fract`)
- **Complex Number Arithmetic**: representing complex numbers as `vec2`, multiplication `(a+bi)(c+di) = (ac-bd, ad+bc)`
- **Vector Math**: dot product, cross product, matrix transforms
- **Ray Marching Basics** (required for 3D fractals): stepping along a ray, using distance fields for collision detection
- **Coordinate Normalization**: mapping pixel coordinates to the `[-1, 1]` range
## Core Principles in Detail
The essence of fractal rendering is **visualization of iterative systems**. Core algorithm patterns fall into three categories:
### 1. Escape-Time Algorithm
For each point `c` on the complex plane, repeatedly iterate `Z <- Z^2 + c`, counting the number of steps needed for Z to escape (`|Z| > R`). More steps means closer to the fractal boundary.
**Distance Estimation** computes the precise distance from a point to the fractal by simultaneously tracking the derivative `Z'`:
```
Z <- Z^2 + c (value iteration)
Z' <- 2*Z*Z' + 1 (derivative iteration)
d(c) = |Z|*log|Z| / |Z'| (Hubbard-Douady potential function)
```
Distance estimation produces smoother coloring than pure escape-time step counting, and is a prerequisite for ray marching in 3D fractals.
### 2. Iterated Function System (IFS)
Apply a set of transforms (folding `abs()`, scaling `Scale`, offset `Offset`) to points in space, iterating repeatedly to produce self-similar structures. Core steps of KIFS (Kaleidoscopic IFS) commonly used in 3D:
```
p = abs(p) // Fold (symmetrize)
sort p.xyz descending // Sort (select symmetry axis)
p = Scale * p - Offset * (Scale-1) // Scale and offset
```
### 3. Spherical Inversion Fractal
Apollonian-type fractals use `fract()` for space folding + spherical inversion `p *= s/dot(p,p)`:
```
p = -1.0 + 2.0 * fract(0.5*p + 0.5) // Fold space to [-1,1]
r^2 = dot(p, p)
k = s / r^2 // Inversion factor
p *= k; scale *= k // Spherical inversion
```
All 3D fractals are rendered using **Sphere Tracing (Ray Marching)**: stepping along the view ray by the distance field value at each step, until close enough to the surface.
## Implementation Steps in Detail
### Step 1: Coordinate Normalization
**What**: Map pixel coordinates to standard coordinates centered on the screen with aspect ratio correction.
**Why**: All fractal calculations must be performed in mathematical space, independent of pixel resolution.
```glsl
vec2 p = (2.0 * fragCoord - iResolution.xy) / iResolution.y;
// p now has y range [-1,1], x scaled by aspect ratio
```
### Step 2: 2D Fractal — Mandelbrot Escape-Time Iteration
**What**: For each pixel point as complex number `c`, iterate `Z <- Z^2 + c` while tracking the derivative.
**Why**: Escape time produces fractal structure; derivative tracking enables distance estimation coloring.
```glsl
float distanceToMandelbrot(in vec2 c) {
vec2 z = vec2(0.0);
vec2 dz = vec2(0.0); // Derivative
float m2 = 0.0;
for (int i = 0; i < MAX_ITER; i++) {
if (m2 > BAILOUT * BAILOUT) break;
// Z' -> 2*Z*Z' + 1 (complex derivative chain rule)
dz = 2.0 * vec2(z.x*dz.x - z.y*dz.y,
z.x*dz.y + z.y*dz.x) + vec2(1.0, 0.0);
// Z -> Z^2 + c (complex squaring)
z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c;
m2 = dot(z, z);
}
// Distance estimation: d(c) = |Z|*log|Z| / |Z'|
return 0.5 * sqrt(dot(z,z) / dot(dz,dz)) * log(dot(z,z));
}
```
### Step 3: 3D Fractal — Distance Field Function (Mandelbulb Example)
**What**: Implement the Mandelbulb power-N iteration using spherical coordinates, returning a distance estimate.
**Why**: 3D fractals cannot be directly colored via escape-time on pixels; they require distance fields for ray marching.
```glsl
float mandelbulb(vec3 p) {
vec3 z = p;
float dr = 1.0; // Derivative (distance scaling factor)
float r;
for (int i = 0; i < FRACTAL_ITER; i++) {
r = length(z);
if (r > BAILOUT) break;
// Convert to spherical coordinates
float theta = atan(z.y, z.x);
float phi = asin(z.z / r);
// Derivative: dr -> power * r^(power-1) * dr + 1
dr = pow(r, POWER - 1.0) * dr * POWER + 1.0;
// z -> z^power + p (spherical coordinate exponentiation)
r = pow(r, POWER);
theta *= POWER;
phi *= POWER;
z = r * vec3(cos(theta)*cos(phi),
sin(theta)*cos(phi),
sin(phi)) + p;
}
// Distance estimation
return 0.5 * log(r) * r / dr;
}
```
### Step 4: 3D Fractal — IFS Distance Field (Menger Sponge Example)
**What**: Construct a KIFS fractal distance field through fold-sort-scale-offset iteration.
**Why**: IFS fractals produce self-similar structures through spatial transforms rather than numerical iteration; distance is tracked via `Scale^(-n)` scaling.
```glsl
float mengerDE(vec3 z) {
z = abs(1.0 - mod(z, 2.0)); // Infinite tiling
float d = 1000.0;
for (int n = 0; n < IFS_ITER; n++) {
z = abs(z); // Fold
if (z.x < z.y) z.xy = z.yx; // Sort
if (z.x < z.z) z.xz = z.zx;
if (z.y < z.z) z.yz = z.zy;
z = SCALE * z - OFFSET * (SCALE - 1.0); // Scale + offset
if (z.z < -0.5 * OFFSET.z * (SCALE - 1.0))
z.z += OFFSET.z * (SCALE - 1.0);
d = min(d, length(z) * pow(SCALE, float(-n) - 1.0));
}
return d - 0.001;
}
```
### Step 5: 3D Fractal — Spherical Inversion Distance Field (Apollonian Type)
**What**: Construct an Apollonian fractal using fract folding + spherical inversion iteration, while recording orbit traps.
**Why**: Spherical inversion `p *= s/dot(p,p)` produces sphere packing structures; orbit traps provide color and AO information.
```glsl
vec4 orb; // Global orbit trap
float apollonianDE(vec3 p, float s) {
float scale = 1.0;
orb = vec4(1000.0);
for (int i = 0; i < INVERSION_ITER; i++) {
p = -1.0 + 2.0 * fract(0.5 * p + 0.5); // Fold space to [-1,1]
float r2 = dot(p, p);
orb = min(orb, vec4(abs(p), r2)); // Record orbit trap
float k = s / r2; // Inversion factor
p *= k;
scale *= k;
}
return 0.25 * abs(p.y) / scale;
}
```
### Step 6: Ray Marching (Sphere Tracing)
**What**: Step along the ray direction, advancing by the distance field value at each step, until hitting the surface.
**Why**: The distance field guarantees safe stepping (won't pass through the surface), and is the standard method for rendering implicit 3D fractals.
```glsl
float rayMarch(vec3 ro, vec3 rd) {
float t = 0.01;
for (int i = 0; i < MAX_STEPS; i++) {
float precis = PRECISION * t; // Relax precision with distance
float h = map(ro + rd * t);
if (h < precis || t > MAX_DIST) break;
t += h * FUDGE_FACTOR; // fudge < 1.0 improves safety
}
return (t > MAX_DIST) ? -1.0 : t;
}
```
### Step 7: Normal Calculation (Finite Differences)
**What**: Sample the distance field gradient around the hit point as the surface normal.
**Why**: Implicit surfaces have no analytical normals and require numerical approximation. Tetrahedral sampling (4-tap) saves 1/3 of the cost compared to central differences (6-tap).
```glsl
// 6-tap central difference method (more intuitive)
vec3 calcNormal_6tap(vec3 pos) {
vec2 e = vec2(0.001, 0.0);
return normalize(vec3(
map(pos + e.xyy) - map(pos - e.xyy),
map(pos + e.yxy) - map(pos - e.yxy),
map(pos + e.yyx) - map(pos - e.yyx)));
}
// 4-tap tetrahedral method (more efficient, recommended)
vec3 calcNormal_4tap(vec3 pos, float t) {
float precis = 0.001 * t;
vec2 e = vec2(1.0, -1.0) * precis;
return normalize(
e.xyy * map(pos + e.xyy) +
e.yyx * map(pos + e.yyx) +
e.yxy * map(pos + e.yxy) +
e.xxx * map(pos + e.xxx));
}
```
### Step 8: Shading and Lighting
**What**: Compute Lambertian diffuse + ambient + AO for hit surfaces.
**Why**: Lighting gives 3D fractals depth and material quality. Orbit trap values (`orb`) can serve both as color mapping and as simple AO.
```glsl
vec3 shade(vec3 pos, vec3 nor, vec3 rd, vec4 trap) {
vec3 light1 = normalize(LIGHT_DIR);
float diff = clamp(dot(light1, nor), 0.0, 1.0);
float amb = 0.7 + 0.3 * nor.y;
float ao = pow(clamp(trap.w * 2.0, 0.0, 1.0), 1.2); // Orbit trap AO
vec3 brdf = vec3(0.4) * amb * ao // Ambient
+ vec3(1.0) * diff * ao; // Diffuse
// Map material color from orbit trap
vec3 rgb = vec3(1.0);
rgb = mix(rgb, vec3(1.0, 0.8, 0.2), clamp(6.0*trap.y, 0.0, 1.0));
rgb = mix(rgb, vec3(1.0, 0.55, 0.0), pow(clamp(1.0-2.0*trap.z, 0.0, 1.0), 8.0));
return rgb * brdf;
}
```
### Step 9: Camera Setup
**What**: Build a look-at camera matrix, converting pixel coordinates to 3D ray directions.
**Why**: All 3D fractal ray marching requires a unified camera framework to generate rays.
```glsl
void setupCamera(vec2 uv, vec3 ro, vec3 ta, float cr,
out vec3 rd) {
vec3 cw = normalize(ta - ro); // forward
vec3 cp = vec3(sin(cr), cos(cr), 0.0); // roll
vec3 cu = normalize(cross(cw, cp)); // right
vec3 cv = normalize(cross(cu, cw)); // up
rd = normalize(uv.x * cu + uv.y * cv + 2.0 * cw); // FOV ~ 2.0
}
```
## Common Variants in Detail
### 1. 2D Mandelbrot (Distance Estimation Coloring)
Difference from base version (3D Apollonian): pure 2D computation, no ray marching needed, uses complex iteration + distance coloring.
```glsl
// Replace entire mainImage
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 p = (2.0*fragCoord - iResolution.xy) / iResolution.y;
// Animated zoom
float tz = 0.5 - 0.5*cos(0.225*iTime);
float zoo = pow(0.5, 13.0*tz);
vec2 c = vec2(-0.05, 0.6805) + p * zoo; // Tunable: zoom center point
// Iteration
vec2 z = vec2(0.0), dz = vec2(0.0);
for (int i = 0; i < 300; i++) { // Tunable: iteration count
if (dot(z,z) > 1024.0) break;
dz = 2.0*vec2(z.x*dz.x-z.y*dz.y, z.x*dz.y+z.y*dz.x) + vec2(1.0,0.0);
z = vec2(z.x*z.x-z.y*z.y, 2.0*z.x*z.y) + c;
}
float d = 0.5*sqrt(dot(z,z)/dot(dz,dz))*log(dot(z,z));
d = clamp(pow(4.0*d/zoo, 0.2), 0.0, 1.0); // Tunable: 0.2 controls contrast
fragColor = vec4(vec3(d), 1.0);
}
```
### 2. Mandelbulb Power-N (3D Spherical Coordinate Fractal)
Difference from base version: uses spherical coordinate trigonometric functions instead of spherical inversion, with a tunable `POWER` parameter controlling the fractal shape.
```glsl
#define POWER 8.0 // Tunable: 2-16, higher = more complex structure
#define FRACTAL_ITER 4 // Tunable: 2-8, more = more detail
float mandelbulbDE(vec3 p) {
vec3 z = p;
float dr = 1.0;
float r;
for (int i = 0; i < FRACTAL_ITER; i++) {
r = length(z);
if (r > 2.0) break;
float theta = atan(z.y, z.x);
float phi = asin(z.z / r);
dr = pow(r, POWER - 1.0) * dr * POWER + 1.0;
r = pow(r, POWER);
theta *= POWER;
phi *= POWER;
z = r * vec3(cos(theta)*cos(phi), sin(theta)*cos(phi), sin(phi)) + p;
}
return 0.5 * log(r) * r / dr;
}
```
### 3. Menger Sponge (KIFS Folding Type)
Difference from base version: uses abs() folding + conditional sorting instead of spherical inversion, producing regular geometric fractals.
```glsl
#define SCALE 3.0 // Tunable: scaling factor, 2.0-4.0
#define OFFSET vec3(0.92858,0.92858,0.32858) // Tunable: offset vector, changes shape
#define IFS_ITER 7 // Tunable: iteration count
float mengerDE(vec3 z) {
z = abs(1.0 - mod(z, 2.0)); // Infinite tiling
float d = 1000.0;
for (int n = 0; n < IFS_ITER; n++) {
z = abs(z);
if (z.x < z.y) z.xy = z.yx; // Conditional sorting
if (z.x < z.z) z.xz = z.zx;
if (z.y < z.z) z.yz = z.zy;
z = SCALE * z - OFFSET * (SCALE - 1.0);
if (z.z < -0.5*OFFSET.z*(SCALE-1.0))
z.z += OFFSET.z*(SCALE-1.0);
d = min(d, length(z) * pow(SCALE, float(-n)-1.0));
}
return d - 0.001;
}
```
### 4. Quaternion Julia Set
Difference from base version: uses quaternion algebra `Z <- Z^2 + c` (4D), Julia sets use a fixed `c` parameter instead of per-point `c`, visualized by taking 3D cross-sections.
```glsl
// Quaternion squaring
vec4 qsqr(vec4 a) {
return vec4(a.x*a.x - a.y*a.y - a.z*a.z - a.w*a.w,
2.0*a.x*a.y, 2.0*a.x*a.z, 2.0*a.x*a.w);
}
float juliaDE(vec3 p, vec4 c) {
vec4 z = vec4(p, 0.0);
float md2 = 1.0;
float mz2 = dot(z, z);
for (int i = 0; i < 11; i++) { // Tunable: iteration count
md2 *= 4.0 * mz2; // |dz| -> 2*|z|*|dz|
z = qsqr(z) + c; // z -> z^2 + c
mz2 = dot(z, z);
if (mz2 > 4.0) break;
}
return 0.25 * sqrt(mz2 / md2) * log(mz2);
}
// Animated Julia parameter c:
// vec4 c = 0.45*cos(vec4(0.5,3.9,1.4,1.1) + time*vec4(1.2,1.7,1.3,2.5)) - vec4(0.3,0,0,0);
```
### 5. Minimal IFS Field (2D, No Ray Marching)
Difference from base version: pure 2D implementation, only ~20 lines of code, using `abs(p)/dot(p,p) + offset` for iteration, producing a density field through weighted accumulation.
```glsl
float field(vec3 p) {
float strength = 7.0 + 0.03 * log(1.e-6 + fract(sin(iTime) * 4373.11));
float accum = 0.0, prev = 0.0, tw = 0.0;
for (int i = 0; i < 32; ++i) { // Tunable: iteration count
float mag = dot(p, p);
p = abs(p) / mag + vec3(-0.5, -0.4, -1.5); // Tunable: offset values change shape
float w = exp(-float(i) / 7.0); // Tunable: 7.0 controls decay
accum += w * exp(-strength * pow(abs(mag - prev), 2.3));
tw += w;
prev = mag;
}
return max(0.0, 5.0 * accum / tw - 0.7);
}
// Sample field() directly on fragCoord as brightness/color
```
## Performance Optimization Details
### Bottleneck Analysis
The core bottleneck in fractal rendering is **nested loops**: outer ray marching steps x inner fractal iterations. A single pixel may execute `200 steps x 8 iterations = 1600` distance field evaluations.
### Optimization Techniques
#### 1. Reduce Ray Marching Steps
Lower `MAX_STEPS` from 200 to 60-100, compensating precision loss with a fudge factor (0.7-0.9).
```glsl
t += h * 0.7; // Fudge factor < 1.0, allows larger steps but reduces penetration risk
```
#### 2. Adaptive Precision
Relax the collision threshold as distance increases; far objects don't need pixel-level precision.
```glsl
float precis = 0.001 * t; // Precision grows linearly with distance
```
#### 3. Early Exit
In fractal iteration, break immediately once `|z|^2 > bailout`.
```glsl
if (m2 > 4.0) break; // Don't continue useless iterations
```
#### 4. Reduce Iteration Count
Fractal iteration counts (`INVERSION_ITER`, `IFS_ITER`) reduced from 8 to 4-5 have minimal visual impact but significant performance gains.
#### 5. Use 4-Tap Instead of 6-Tap for Normals
The tetrahedral method requires only 4 `map()` calls instead of 6, saving 33% normal computation cost.
#### 6. AA Downgrade
Use `#define AA 1` during development, switch to `AA 2` for release. `AA 3` has massive performance impact (9x overhead).
#### 7. Distance Field Scaling
For non-unit-sized fractals, scale the space first then scale the distance value to avoid precision issues.
```glsl
float z1 = 2.0;
return mandelbulb(p / z1) * z1;
```
#### 8. Avoid `pow()` Inside Loops
`pow(r, power)` in Mandelbulb is expensive; low powers (e.g., 2, 3) can be manually expanded instead.
## Combination Suggestions
### 1. Fractal + Volumetric Lighting
Accumulate scattered light passing through fractal gaps during ray marching, producing "god rays" effects.
```glsl
// Accumulate additionally in ray march loop
float glow = 0.0;
for (...) {
float h = map(ro + rd*t);
glow += exp(-10.0 * h); // Closer to surface = larger contribution
...
}
col += glowColor * glow * 0.01;
```
### 2. Fractal + Post-Processing (Tone Mapping / FXAA)
3D fractals have rich high-frequency detail, prone to aliasing. Use ACES Tone Mapping + sRGB correction + FXAA post-processing.
```glsl
// ACES tone mapping
vec3 aces_approx(vec3 v) {
v = max(v, 0.0) * 0.6;
float a=2.51, b=0.03, c=2.43, d=0.59, e=0.14;
return clamp((v*(a*v+b))/(v*(c*v+d)+e), 0.0, 1.0);
}
col = aces_approx(col);
col = pow(col, vec3(1.0/2.4)); // sRGB gamma
```
### 3. Fractal + Transparent Refraction (Multi-Bounce Refraction)
Used for "crystal ball" effects on volumetric fractals like Mandelbulb. Uses negative distance fields for reverse ray marching inside, combined with Beer's law absorption.
```glsl
// Invert distance field for interior stepping
float dfactor = isInside ? -1.0 : 1.0;
float d = dfactor * map(ro + rd * t);
// Beer's law light absorption
ragg *= exp(-st * beer); // beer = negative color vector
// Refraction direction
vec3 refr = refract(rd, sn, isInside ? 1.0/ior : ior);
```
### 4. Fractal + Orbit Trap Texture Mapping
Orbit trap values can be mapped to HSV color space for rich coloring, or mapped as self-emission for glowing fractal effects.
```glsl
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
// Map orbit trap to HSV
vec3 col = hsv2rgb(vec3(trap.x * 0.5, 0.9, 0.8));
```
### 5. Fractal + Soft Shadow
Perform an additional ray march from the fractal surface toward the light source, accumulating the minimum `h/t` ratio to generate soft shadows.
```glsl
float softshadow(vec3 ro, vec3 rd, float mint, float k) {
float res = 1.0;
float t = mint;
for (int i = 0; i < 64; i++) {
float h = map(ro + rd*t);
res = min(res, k * h / t); // Larger k = harder shadows
if (res < 0.001) break;
t += clamp(h, 0.01, 0.5);
}
return clamp(res, 0.0, 1.0);
}
```