§ pass · advanced
Mandelbulb 分形
raymarch Mandelbulb 3D 分形(power 8 经典):球坐标 z=z^p+c 迭代 + 距离估算 + 四面体法线,凹处青绿、凸起琥珀。
返回实验室§ params
分形 power8.00
配色偏移0.00
旋转速度0.40
§ preview
- Mandelbulb distance estimator:球坐标 (theta,phi,r) 迭代 z = r^p·(sinθcosφ, sinφsinθ, cosθ) + c,距离 0.5·log(r)·r/dr
- 四面体差分法线(4 次 SDF 采样)比中心差分(6 次)省 1/3 开销
- 轨道陷阱 trap=min|z| 上色:trap 小=凹陷深处青绿,trap 大=凸起琥珀
- 桌面 64 步 / 移动端 32 步(RAYMARCH_STEPS 宏自动降级),分形内部迭代 8 次
§ fragment source
#version 300 es
precision highp float;
uniform float iTime;
uniform vec2 iResolution;
uniform float u_power;
uniform float u_colorShift;
uniform float u_rotate;
out vec4 fragColor;
#define uv (gl_FragCoord.xy / iResolution.xy)
// Mandelbulb 内部迭代次数(分形精度,与 raymarch 步数独立)
#define FRACT_ITER 8
// Mandelbulb distance estimator(公开公式)
// 返回到分形表面的距离估算,trap 输出轨道陷阱(用于上色)
float mandelbulbDE(vec3 pos, float power, out float trap) {
vec3 z = pos;
float dr = 1.0;
float r = 0.0;
trap = 1e10;
for (int i = 0; i < FRACT_ITER; i++) {
r = length(z);
trap = min(trap, r);
if (r > 2.0) break;
float invR = 1.0 / max(r, 1e-6);
// 球坐标
float theta = acos(clamp(z.z * invR, -1.0, 1.0));
float phi = atan(z.y, z.x);
// 导数累积:dr = p * r^(p-1) * dr + 1
dr = pow(max(r, 1e-6), power - 1.0) * power * dr + 1.0;
// z = z^power(球坐标幂)
float zr = pow(max(r, 1e-6), power);
theta *= power;
phi *= power;
z = zr * vec3(sin(theta) * cos(phi),
sin(phi) * sin(theta),
cos(theta));
// z = z^power + c(c = pos)
z += pos;
}
return 0.5 * log(max(r, 1e-6)) * r / dr;
}
// 四面体差分法线(4 次 SDE 采样)
vec3 calcNormal(vec3 p, float power) {
float e = 0.0015;
vec2 k = vec2(1.0, -1.0);
float t;
return normalize(
k.xyy * mandelbulbDE(p + k.xyy * e, power, t)
+ k.yyx * mandelbulbDE(p + k.yyx * e, power, t)
+ k.yxy * mandelbulbDE(p + k.yxy * e, power, t)
+ k.xxx * mandelbulbDE(p + k.xxx * e, power, t)
);
}
mat3 rotY(float a) {
float c = cos(a), s = sin(a);
return mat3(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
}
void main() {
vec2 p = uv * 2.0 - 1.0;
p.x *= iResolution.x / iResolution.y;
// 缓慢绕 Y 轴旋转的相机
float ang = iTime * u_rotate * 0.3;
vec3 ro = rotY(ang) * vec3(0.0, 0.0, 3.0);
vec3 rd = normalize(rotY(ang) * vec3(p, -2.0));
// raymarch 分形
float t = 0.0;
bool hit = false;
float trap = 1e10;
float minDist = 1e10;
for (int i = 0; i < RAYMARCH_STEPS; i++) {
vec3 pos = ro + rd * t;
float tr;
float d = mandelbulbDE(pos, u_power, tr);
minDist = min(minDist, d);
trap = min(trap, tr);
if (d < 0.0008 * t) { hit = true; break; }
t += d * 0.85;
if (t > 6.0) break;
}
vec3 teal = vec3(0.31, 0.82, 0.78);
vec3 amber = vec3(0.91, 0.69, 0.29);
vec3 deep = vec3(0.02, 0.025, 0.03);
vec3 col;
if (hit) {
vec3 pos = ro + rd * t;
vec3 n = calcNormal(pos, u_power);
vec3 v = -rd;
vec3 l = normalize(vec3(0.6, 0.7, -0.5));
// 轨道陷阱上色:trap 小=凹陷深处青绿,trap 大=凸起琥珀
float t01 = clamp(trap / 1.4, 0.0, 1.0);
vec3 base = mix(teal, amber, t01);
// u_colorShift 整体偏移:0 青绿主,1 琥珀主
base = mix(base, amber, u_colorShift);
float ndl = max(dot(n, l), 0.0);
vec3 diff = base * (0.25 + 0.75 * ndl);
// 琥珀 rim
float rim = pow(1.0 - max(dot(n, v), 0.0), 3.0);
diff += amber * rim * 0.5;
// 凹陷深处压暗到深底色
col = mix(deep, diff, smoothstep(0.0, 0.35, ndl + 0.15));
} else {
// 未命中:远处 glow
float g = exp(-minDist * 6.0);
col = mix(deep, teal * 0.45, g * 0.35);
col += amber * 0.06 * g;
}
// tonemap + gamma
col = col / (col + 1.0);
col = pow(col, vec3(1.0 / 2.2));
fragColor = vec4(col, 1.0);
}