Ultimate Anti-Buddhagram preview

this is one unchanging 4D object, not morphing, just rotating

inspired by
superliminal.com/fractals/bgra bottom video "ZrZi to ZrCr - only points Inside the m-set"

"Ultimate" means only the points in the limit cycle (found by 's root finding method) are plotted, not all the iterates along the way.

Depth 13 at 640x360p60 renders at around 7x slower than realtime, I think I have enough RAM for up to depth 15. Higher depth means higher density of points means higher quality image, especially at larger output resolutions.

Rotations are based on random walk of unit quaternion pairs.

Need to port the point plotting code to OpenGL, doing it on CPU is the main bottleneck.

Each pringle is a 2D surface embedded in 4D (c, z) space. The surface is defined implicitly by:

$$ f_c^p(z) - z = 0 $$

which provides a way to calculate the surface normal vector:

$$ n = \left( \frac{ \partial f^p }{ \partial c } , \frac{ \partial f^p }{ \partial z } - 1 \right) $$

The derivatives can be calculated by the usual iterative process:

dc = 0
dz = 1
z = z_0
p times do:
dc := 2 * dc * z + 1
dz := 2 * dz * z
z := z * z + c

This allows for 4D lighting calculations, pretty much the same as in 3D (see hollasch.github.io/ray4/Four-S ).

But when I tried it the results were too confusing because of the 4D to 3D perspective projection: `ray4` for example uses slicing along the 4th dimension instead of projecting the whole space.

Show thread

Slicing works, but unfortunately leads to artifacts where the grid points in the cloud aren't fine enough, so you can see the individual dots spaced apart. not enough RAM to compute more detail.

Trying a different approach: rendering the implicit surface directly with distance fields

d_p = |f_c^p(z)-z| - t
d = min{d_p : p \in N^+}

where t is large enough to make the surface visible and small enough that it isn't blobby. This isn't an exact distance, but multiplying it by a small number gives stable images with a sphere tracer. Unfortunately it looks nothing like what I want, because the condition

$$ \left|\frac{ \partial f_c^p(z) }{ \partial z }\right| <= 1 $$

is not taken into account.

Show thread

float DE(vec4 cz)
vec2 c = cz.xy;
vec2 z0 = cz.zw;
vec2 z = z0;
vec2 dz = vec2(1.0, 0.0);
float de = 1.0 / 0.0;
for (int p = 0; p < MaxPeriod; ++p)
dz = 2.0 * cMul(dz, z);
z = cSqr(z) + c;
float de1 = max(length(z - z0), length(dz) - 1.0) - Thick;
de = min(de, de1);
return de * 0.01;

Mostly works but is a bit blobby near parabolic bond points. It's also slow, the 0.01 means I need to bump up the number of ray steps crazy high.

Show thread

accumulating all the slices per frame, rotating in 4D - this is more like traditional buddhagram rendering

Show thread

another view, colours adjusted with darktable

thread recap:

- started using Newton's methods to solve z = f_c^p(z) for candidate periods, collecting all the (c,z) pairs into a point cloud rendered on the CPU with additive blending

- ported the point cloud rasterization from CPU to GPU using OpenGL

- switched from perspective projection to slicing along the 4th dimension

- worked out a distance estimate for the surface defined implicitly

- accumulated multiple slices into a single frame, giving a similar effect to the start of the thread (but orthographic instead of perspective for the 4D->3D reduction)

So almost full circle, except that the output image quality is no longer limited by RAM for point cloud storage, and all the calculations are done in OpenGL shaders (in FragM).

Show thread
Sign in to participate in the conversation

Welcome to post.lurk.org, an instance for discussions around cultural freedom, experimental, new media art, net and computational culture, and things like that.