Link: Mitch Richling's Home Page

© 2009 Mitch Richling

3D Mandelbrot Sets


Table Of Contents

Introduction

Images of the Mandelbrot set are what originally captured the attention of the general public and fired interest in fractal geometry. Credit is given to Benoit Mandelbrot for generating the first images of the set that now bears his name. Much of the mathematics behind Mandelbrot's work has it's foundation in the work of Maurice Julia's around 1918, but it was the amazing pictures generated by Mandelbrot and his computers at IBM's T.J. Watson Research Center that brought Julia's work back to life. The science of Fractals didn't turn out to be the "science that would revolutionize science" that many thought it would become. In fact, in mathematical circles at least, it is largely dead. What lives in it's place is the study of dynamical systems.

Growing up, I had seen pictures of the set, and heard the rumblings of the new science of "Fractal geometry". I really only discovered the Mandelbrot set for myself while an undergraduate mathematics major in 1992 when I spent time on the mathematics surrounding the object. Soon, I was writing computer programs left an right to draw the thing. It took hours, sometimes days, to generate images that only take seconds to draw with modern hardware. I sound like my grandfather telling stories about the perils of walking to school when he was a kid!

This page is primarily dedicated to a class of 3D renderings of the Mandelbrot Set (commonly called Mandelbrot Mountains) that I could never have generated with the hardware available to me in 1992!

Algorithms

Some of the material in this section requires a bit of mathematical understanding of the Mandelbrot set. To help with this, I have prepared a short list of mathematical facts that you can read in PDF form: "Mathematical Facts about the Mandelbrot Set". The LaTeX source available.

Some notes about the programs

I have used ISO C++ for the programs on this page with a graphics API of my own design. The graphics API is straight forward enough that I don't think it should be a barrier to understanding the programs. I no longer have the Java versions on this page as I am tired of the hordes of Java wienies constantly e-mailing me with questions about how to install the JVM so they can run the programs.

Drawing the Mandelbrot Set

The most common approach is to draw the outside of the set and to color that set using one of the functions L, G, or E that I described in "Mathematical Facts about the Mandelbrot Set". By far the most common choice is L because it is easy to understand and trivial to implement. The general idea is to map the array of pixels making up the screen image to a rectangular region of the complex plane. Each pixel of the image is then scanned to determine what color it should be based upon the function used

To compute the value of L, we simply start with zero and iterate until we get a value that is more than 2 units away from the origin or we reach some predefined maximum iteration count. This simple algorithm leads to the following C++ program:

           #include "ramCanvas.h"
           int main(void) {
             int x, y, count;
             float xr, yr, zx, zy, tempx;
             int R1, R2, R3;
             ramCanvas theRamCanvas = ramCanvas(768, 768, -2.0, 2, -2, 2);
             for(x=0;x<theRamCanvas.get_numXpix();x++)
               for(y=0;y<theRamCanvas.get_numYpix();y++) {
                 for(xr=theRamCanvas.int2realX(x),yr=theRamCanvas.int2realY(y),zx=zy=0.0,count=0;
                     (zx*zx+zy*zy<4)&&(count<768);
                     count++,tempx=zx*zx-zy*zy+xr,zy=2*zx*zy+yr,zx=tempx) ;
                   if(count < 767)
                     theRamCanvas.drawPoint(x, y, theRamCanvas.cmpFireRamp768(count*20));
               }
             theRamCanvas.writeTGAfile(stdout);
           }
        

The above program will generate this picture:

The problem with this is that the range of the function L is a set of integers, and thus can not be continuous if the range is considered to be real. What we need is a function that is continuous if we wish to have a smooth surface to draw for our mountain. The functions G and E are just what we want. To actually render the mountain, you need some 3D software like Povray. Instead of generating a triangle mesh for the surface, we will use Povray's ability to render special images called "height fields". You can generate a suitable height field with a program like this one:

          #include "ramCanvas.h"
          #include "math.h"
          
          #define MAXITR 768
          #define BALLSIZE 100000.0
          
          int main(void) {
            int x, y, count;
            double xr, yr, zx, zy, tempx;
            double pot, maxPot=0, minPot=0;
            ramCanvas theRamCanvas = ramCanvas(2000, 2000, -2, 2, -2, 2);
            
            /* Compute the min and max */
            for(x=0;x<theRamCanvas.get_numXpix();x++) {
              for(y=0;y<theRamCanvas.get_numYpix();y++) {
                for(xr=theRamCanvas.int2realX(x),yr=theRamCanvas.int2realY(y),zx=zy=0.0,count=0;
                    (zx*zx+zy*zy<BALLSIZE)&&(count<MAXITR);
                    count++,tempx=zx*zx-zy*zy+xr,zy=2*zx*zy+yr,zx=tempx) ;
                if(count < MAXITR) {
                  pot=0.5*log(zx*zx+zy*zy)/pow(2.0,count);
                  if(pot < 0.0) pot = 0.0;
                  theValues[x][y]=pot;
                  if(x == 0 && y == 0) {
                    minPot = pot;
                    maxPot = pot;
                  } else {
                    if(maxPot < pot) { maxPot = pot; }
                    if(minPot > pot) { minPot = pot; }
                  }
                } else {
                  theValues[x][y] = -1;
                }
              }
            }
            fprintf(stderr, "MIN: %10.5f  MAX: %10.5f\n", (float)minPot, (float)maxPot);  
            
            /* Draw */
            for(x=0;x<theRamCanvas.get_numXpix();x++) {
              for(y=0;y<theRamCanvas.get_numYpix();y++) {
                for(xr=theRamCanvas.int2realX(x),yr=theRamCanvas.int2realY(y),zx=zy=0.0,count=0;
                    (zx*zx+zy*zy<BALLSIZE)&&(count<MAXITR);
                    count++,tempx=zx*zx-zy*zy+xr,zy=2*zx*zy+yr,zx=tempx) ;
                if(count < MAXITR) {
                  pot=0.5*log(zx*zx+zy*zy)/pow(2.0,count);
                  if(pot < 0.0) pot = 0;
                  pot = pot - minPot;
                  if(pot < 0.0) pot = 0;
                  pot=(0xffff-1)-pot*(0xffff-2)/(maxPot-minPot);
                  theRamCanvas.drawPoint(x, y, theRamCanvas.cmpPovray65536((int)pot));
                } else {
                  theRamCanvas.drawPoint(x, y, theRamCanvas.cmpPovray65536(0xffff-1));
                }
              }
            }
            
            theRamCanvas.writeTGAfile(stdout);
          }
        

The program above is VERY inefficient because it actually computes the value of G twice for each pixel. It is an easy matter to store the values in an array, but I leave that exercise to you. The method cmpPovray65526 is key in the above program because it computes a color appropriate for a special kind of 16-bit TGA file that Povray can use for a height field. If the output from this program is stored into a file called "theM.tga", then the following Povray file will render a "fractal mountain":

          #include "colors.inc"
          
          camera { 
            location <4, 4, -4>
            look_at 0
          }
          
          light_source { <0,100,-100> White }
          light_source { <100,0,-100> White*0.5 }
          light_source { <100,100,-100> White*0.5 }
          
          height_field {
            tga "theM.tga"
            smooth
            pigment { 
              gradient y
                color_map {
                  [0.0 color Black ]
                  [0.999 color Blue ]
                  [0.9999 color Red ]
                  [1.0 color White ]
                }
            }
            translate <-0.5, -0.5, -0.5>
            scale <10, 3.0, 10>
          }
        

The first image is the special Povray height field, and the second is the rendered product:

References

I strongly recommend the book "The Science of Fractal Images" to anyone who actually wants to use a computer to generate pictures. This book has an all star group of contributing authors.

For a very interesting introduction to the history of Chaos I would suggest James Glick's book: "CHAOS: Making a New Science" (ISBN: 0-14-009250-1). This book has almost no mathematics or formal material regarding Fractals or dynamical systems, but is well worth the read for the historical perspective.

If you are looking for a good introduction to dynamical systems, I would suggest Steven Storgatz's book: "Nonlinear Dynamics and Chaos" ISBN 0-7382-0453-6).

For a good, encyclopedic, introduction to the field in general, I strongly suggest "Chaos and Fractals: New Frontiers of Science" by Peitgen, Jurgens, and Saupe. This book is notable because of it's clear presentation and breadth of coverage. It's a great book to have around for the casual reader because it is broken up into semi self-contained sections that one can just pick up and read.

© 2009 Mitch Richling