It’s primarily (for now) a renderer of colored Mandelbrot sets. The Mandelbrot set is fairly well-known:
This image is a made on a 2D plane whose upper-left coordinate is at (-2, -1) and lower-end coordinate is at (1, 1). For each point (x, y) of this plane, we consider the complex number z = x + yi, we do some operations (called iterations) repeatedly on z, and we see what is the result. If z grows larger and larger (if the sequence of operations diverges), the point z is not in the Mandelbrot set, and we color it grey. If it does not, or if we give up before seeing that it diverges, the point z in in the Mandelbrot set, and we color it black. The longer we wait before deciding that a point is in the set, the more precise the boundary is (because we have “more chances” that it diverges if it is going to diverge).
And it’s technically possible to zoom as much as you want on the border of the set and to get “interesting” results at infinite amount of zoom – every little part of the border has an infinite amount of detail.
Now one of the reason why Mandelbrot (and other similar computation) renderings are quite popular is that they’re colorful and pretty. In particular, I have a fair amount of screen wallpaper coming from Blatte’s Backgrounds, that contain some of this kind of images. The connection between my set above and the colorful images I linked is not a priori obvious.
Enter: coloring algorithms. The idea is to try to represent graphically how the points outside of the set diverge. The first algorithm I implemented was a escape time algorithm: if the point diverges after 5 iterations, color it in a given color, after 6, in an other color, after 50, in yet another color, and so on. And the fastest way to generate a color palette is to just generate it randomly (and to affect one color to each possible number of iterations), which can yield… fairly ugly images 😉
A variation of that approach is to affect to each number of iteration a color that is “proportional” (for instance, more or less saturated) to the number of points that actually reach that number of iterations.
Then the next idea is to go from “discrete coloring” to “continuous coloring” – right now, we have bands of colors that may have some aesthetic quality, but that may not work for what one has in mind in the end. To achieve that, we add a “continuous” component to our iteration computation (so that it’s not integer anymore) and we map it to the color palette.
The other coloring algorithm that I started exploring today is coloring by orbit traps. Instead of considering when the iteration escape, we look at how it escapes, and we try to represent that. The first idea is to take an arbitrary, “interesting” point (from an aesthetic point of view, and mostly found by trial and error at this stage), and to look at how close the iterations of the escaping points come to this fixed point). The colored values are the distances to this point. (And the image at the beginning of this post is a (low) zoom from this one 🙂 ) Note: on this one I also tweaked the palette computation to get more contrast. That was fun 🙂
Generally speaking, this project is also for me a nice visual sandbox to play around – on top of practicing my C++, my goal is to generate pretty images, but that typically requires a fair amount of “quality of life” updates:
- a very basic set of command-line options so that I could generate images without hard-coding all the values
- quicker than I would have thought: a minimal Qt UI that allows me to zoom and increase/decrease the number of iterations – and right now I kind of feel the need to expand that UI so that it’s… actually useable (being able to change parameters on the UI, re-scaling the window to fit the ratio of the rendered input, that sort of things)
- yesterday, I sped up the rendering by… well, adding threads 😛 (via the QtConcurrent library).
Generally speaking, it’s a fun project – and it’s actually something I can go back to quite quickly (once I go over the shock of “urgh C++” – I actually DO like C++, but I did AoC in Go, and it’s a fairly different language) and implement a thing or two here or there, which is nice. For instance, a few months ago, I went “given my current code, can I add support for Julia sets within 10 minutes before going to bed?” and the answer was yes: