Megatech


Julia Sets and the Burning Ship

Introduction

Lately, I’ve been working with a student interested in computer graphics. Right now, we’re stuck in terminal land (i.e., the land of bullshit std::cin-based menus), so drawing anything is kind of out of the question. Nonetheless, I thought converting my Mandelbrot set program into a standard I/O-based program would be pretty easy. I was right about this and started thinking about other fractals I could render this way.

This led me back to Wikipedia to look at other escape-time fractals. In particular, I picked out the Burning Ship fractal and an animation of various Julia sets to work on. Besides standard I/O implementations (which lack animation), I also wanted to implement these in OpenGL by forking my Mandelbrot set program.

Getting these new fractals working was pretty simple.

The Problem

The Burning Ship fractal is much simpler than the animated Julia sets, so I’ll cover that first. If you recall, the series z0 = 0, zn = zn-12 + c defines the Mandelbrot set. A complex coordinate c is in the Mandelbrot set when the z’s magnitude does not diverge to infinity for that complex coordinate. The Burning Ship fractal is the same. However, the series is z0 = 0, zn = (|Re(zn-1)| + |Im(zn-1)| * i)2 + c. In GLSL, you can implement this version by simply applying the abs function to the z vector. No biggie.

An image of the Burning Ship fractal.

The Julia sets are a little harder to get working. Here, the defining series is still zn = zn-12 + c. However, z0 is now the varying complex coordinate, and c is a complex constant. Selecting the constant c is crucial to getting any interesting output. In my case, I’ve chosen 0.7885 * eA * i, where A varies between 0 and . Yes, I did select this based on Wikipedia. I’m a programmer, so I’m not a serious engineer. Please don’t trust me to build a bridge. You’ll die! Implementing this requires new software on the host side, so a simple shader change won’t cut it like with the Burning Ship.

An image of a bunny Julia set fractal.

The Solution

For the Burning Ship, my solution is nearly identical to my Mandelbrot set implementation. It’s really as simple as a small shader edit changing:

for (i = 0; length(z) <= 65536 && i < max_iterations; ++i)
{
  z = cx_multiply(z, z) + c;
}

to

for (i = 0; length(z) <= 65536 && i < max_iterations; ++i)
{
  z = cx_multiply(abs(z), abs(z)) + c;
}

That’s really it.

An image of the Burning Ship fractal.

There are changes in the shader and host programs for the Julia sets. On the shader side, I first had to introduce a new uniform for the value c. I also had to modify the initial definition of z. In the Mandelbrot program, z starts as vec2(0, 0). However, for the Julia sets, it’s initialized to the fragment coordinate scaled between -2 and 2 on both axes. On the host side, I’m computing and supplying the value of c.

I’ve introduced a few constants (A_MIN, A_MAX, and ANIMATION_SPEED) to do this. A_MIN (a/k/a 0) and A_MAX (a/k/a ) represent the range of values to compute c from. c itself is computed assuming B * eA * i = B * cos(A) + B * sin(A) * i. Once per frame, this is computed for B = 0.7885. The value of A varies in steps of 0.01 multiplied by ANIMATION_SPEED and scaled by the frame’s delta time. It always starts at 0. This behavior reflects when A reaches A_MAX or A_MIN (i.e., An = An-1 + 0.01, A0 = 0 until An = 2π. After that point An = An-1 - 0.01, A0 = 2π until An = 0). The result is a smooth transition from 0 to .

An image of a Julia set fractal.

Since the Julia sets are not a single fractal but an animated transition through a series of fractals, I’ve also added a pause function. This allows users to stop the animation at any point and inspect any part of a particular frame. Getting this to work was a little finicky because pausing is a boolean, whereas the other controls are continuous.

In both cases, the result is a nice rendering of the respective fractals with the same parallel computation as my Mandelbrot program.

An image of a Julia set fractal.

Thoughts

I’m writing this immediately after finishing the previous Megatech-Vulkan article, so I don’t have much new on my mind. These programs mainly show how much you can get done without a solid mathematical foundation. I’m not mathematically skilled enough to explain why these programs generate the interesting images they do. On the other hand, I clearly am clever enough to make them work. I guess there’s a long tradition of this in the software world. Dijkstra is probably rolling in his grave.