Rendering the Mandelbrot Set


Software / CPU Based version

Back to Main Page

For a long time, I have been meaning to learn more about graphics programming, but I'd always been deterred by refusing to learn in the correct way. For some reason, the only way I seem go about these things is to stubbornly smash my head against the problem until it all finally clicks into place (the program, not my head), and I understand how everything works.

I decided to primarily try using the power of the GPU to handle computational tasks, so I wanted to learn about compute shaders. Before I could do that though, I needed something to compute, and I settled on the classic of trippy youtube videos:
The Mandelbrot Set.

What is the Mandelbrot Set

Well, if you somehow haven't heard, it's a fractal you can create by following some quite simple rules. To somewhat oversimplify, for each point in 2D space, you perform a calculation over and over, and if the result continues getting larger and larger to infinity, then that point is not considered part of the set. If it stays in a stable orbit around the a small area, though, then it is part of the set. The calculation is just:

Z = Z2 + C

Where Z and C are complex numbers. Z starts as (0 + 0i), and C corresponds to the point's position in space.

Numberphile Video about the Mandelbrot Set

First: Software Rendering

Now, before I could jump straight in to fancy GPU accelerated stuff, I would need a reference implementation just using the CPU. This took me a bit to get working, as I was not particularly well versed on complex mathematics, but after fiddling about with it for a little while, I got it to display the outline of that classic cardioid.

Next, I wanted to get the smooth gradients and colours you often see in pictures of the fractal online. I looked into it a bit further, and found that they are usually coloured with the points in the set being black, and the points outside being set based on how many iterations it takes before the point is outside of the set. With this knowledge, it was pretty simple to get the program to keep track of iteration depth and to normalise it into the range 0.0 - 1.0. This value would be 0.0 if the point left the threshold after only 1 iteration, but would smoothly interpolate up to 1.0 the more iterations it took. Then, I could set the pixel's brightness based on this value:

After that, I tinkered around with some simple programmatic colour spectra, until it was producing nicer colours to look at:

Earlier on, I'd also spent some time implementing a pan and zoom system, so the screen could move around to look at certain areas more closely. This was a bit harder than I had initially thought, because the zoom amount needs to scale up non-linearly. Otherwise, it becomes slower the further in you zoom, until it basically stops. The panning also had to be inversely scaled to make sure the view moved by a smaller absolute amount the deeper you were zoomed in.

At this point, of course, I got completely distracted from my goal of trying to implement this for the GPU and began adding all kinds of features like an auto-zoom system for recording smooth zooms. (This eventually also got expanded to a full keyframe-based animation system, but I'll spare you the details...) I also made it capable of displaying various Julia sets, because they're related to the Mandelbrot set, and it didn't take much tinkering to get it working. Here's a GIF I made of it cycling through some of those:

Where was I?
Oh right, GPU stuff...
This was also obviously going to be necessary, as it was already kinda struggling to render at a relatively low iteration count, with this low resolution. (Although, I hadn't done any optimisation yet, so it could probably be a bit faster, if I spent more time tinkering with it)

Well, I figured I'd probably need a baseline understanding of a graphics library like OpenGL, so the next thing I did was the good-old OpenGL equivalent of a "hello world" program. But, now I think this page is long enough, so I guess look forward to an update soon-ish.


Back to Main Page