メインコンテンツまでスキップ

mandelbulbSDF: Three-Dimensional Mandelbrot Fractal

Iterative Fractal Geometry in Distance Field Space

The mandelbulbSDF function generates a signed distance field for the Mandelbulb fractal, a three-dimensional extension of the classic Mandelbrot set. This function returns both distance information and iteration count, enabling complex fractal visualization with detailed surface properties.

Mathematical Foundation

The Mandelbulb extends the Mandelbrot iteration formula to three dimensions using spherical coordinates:

zn+1=znn+cz_{n+1} = z_n^n + c

where znnz_n^n is computed in spherical coordinates (r,ϕ,θ)(r, \phi, \theta):

xnew=rnsin(nϕ)cos(nθ)ynew=rnsin(nϕ)sin(nθ)znew=rncos(nϕ)\begin{align} x_{new} &= r^n \sin(n\phi) \cos(n\theta) \\ y_{new} &= r^n \sin(n\phi) \sin(n\theta) \\ z_{new} &= r^n \cos(n\phi) \end{align}

The distance estimation uses the derivative method:

d=14log(zn)znznd = \frac{1}{4} \log(|z_n|) \frac{\sqrt{|z_n|}}{|z_n'|}

Function Parameters

ParameterTypeDescription
stvec3Sample point position in 3D space

Return Value

ComponentTypeDescription
.xfloatDistance estimate to fractal surface
.yfloatNumber of iterations performed

Implementation Demonstrations

ライブエディター
const fragment = () => {
      const up = vec3(0, 1, 0)
      const eps = vec3(0.001, 0, 0)
      const eye = rotate3dY(iTime).mul(vec3(3))
      const march = Fn(([eye, dir]: [Vec3, Vec3]) => {
              const p = eye.toVar()
              const result = mandelbulbSDF(p).toVar()
              const d = result.x.toVar()
              Loop(128, ({ i }) => {
                      If(d.lessThanEqual(eps.x), () => {
                              const dx = mandelbulbSDF(p.add(eps.xyy)).x.sub(d)
                              const dy = mandelbulbSDF(p.add(eps.yxy)).x.sub(d)
                              const dz = mandelbulbSDF(p.add(eps.yyx)).x.sub(d)
                              const normal = vec3(dx, dy, dz).normalize()
                              const iterations = result.y.div(64)
                              return vec4(normal.mul(0.5).add(0.5).mul(iterations.add(0.3)), 1)
                      })
                      p.addAssign(d.mul(dir))
                      result.assign(mandelbulbSDF(p))
                      d.assign(result.x)
              })
              return vec4(0)
      })
      const z = eye.negate().normalize()
      const x = z.cross(up)
      const y = x.cross(z)
      const scr = vec3(uv.sub(0.5), 1.5)
      const dir = mat3(x, y, z).mul(scr).normalize()
      return march(eye, dir)
}