Buddhabrot Buddhabrot
Buddhabrot 4D

Again, the Mandelbrot set's extraordinary properties allow us to raise to the next artistic level.

Key observation

The cool thing is that the Mandelbrot can be interpreted as a four-dimensional object. Let me clarify: in the function Zn = Zn-12 + C two complex numbers are involved, Z and C, and each one has two components, real and imaginary -so on every iteration we're playing with 4 variables, which can be understood as spacial dimensions. Painting Z on the plane produces the Buddhabrot, and painting C, the Mandelbrot, but they are both part of the same 4D data set.

For a three-dimensional object, we can name the dimensions depth, width and height, or X, Y, and Z. For this 4D object, we'll name them in the following manner: Z-real (Zr), Z-imaginary (Zi), C-real (Cr), y C-imaginary (Ci).

Visualizing the 4 dimensions

To try and picture the 4D object in your mind is close to impossible, but luckily, a computer has no problem doing that for us. But how does it do it?

Let's recall how to represent a 3D object on a plane -2 dimensions, X and Y-: we associate X with width, Y with height, and ignore depth. if we want different angles, we just change the dimension associated to X and Y -for example, for a profile view, X represents depth, and Y, height; for a half turned view, X will be width rotated 45º to depth, and Y, height. This way we can get any perspective of a 3D object.

Ok then, so in order to go from 4 to 3D the process is analogous: we simply ignore one of the 4 dimensions, and consider only the other three. This will result in a 3D object that can be represented on the plane with the usual procedure. Just like there's 3 capital planes for a 3D object (X, Y, y Z) -((X,Y), (X,Y), e (Y,Z))-, a 4D object (Zr, Zi, Cr, Ci) has 4 three-D capital volumes ((Zr,Zi,Cr), (Zr,Zi,Ci), (Cr,Ci,Zr), y (Cr,Ci,Zi)). For these capital volumes, we have only to associate each 3D dimension -X, Y, y Z- with one of the 4D object's dimensions -Zr, Zi, Cr, and Ci. Then again, just like a 3D object has an infinite number of planes, we can get an infinite number of 3D objects from a 4D one; for that, we have to associate a dimension of the 3D object with one of the 4D object's rotated a certain angle towards another one. It may be hard to imagine, but in a 4D object every dimension is at 90º from the other 3; so, if we paint the (Zr,Zi) plane, Cr and Ci are perpendicular to that plane.

If you want to go into more detail, click here -this site gives good explanations on how to visualize 4D.

Buddhabrot in an hypercube

Also known as teseract, the 4D cube is an appropriate recipient for the Buddhabrot. In the next image you can see how it looks. A comment on the legend: although it looks like there's truncated pyramids around the central cube, these are really perfect cubes, they've just been deformed -it's a visual trick to indicate on which side cubes are joined together-; additionally, the fact that the cubes are not in contact is another trick to keep the image clean -they are actually glued together on one plane. If you noticed, there's 2 equal 3D objects of each kind, except for the central one. Then where's the missing one? Well, it's "outside", in contact with the 6 external faces. Again, I didn't paint it to keep the image clean.

Rendering 4D onto 2D

The technique I've used to paint the Buddhabrot from different perspectives can be summarized in 2 steps:

  • 4D to 3D: First of all, we have to decide which 3D object we want to see. Consequently, we have to declare X, Y, and Z. If we want points between two of the Buddhabrot's dimensions to be in these coordinates, we'll also have to define the angle.
  • 3D to 2D: Once defined the 3D object's coordinates, we can easily proceed to 2D -we just have to place the point of view. For that, let's imagine the Buddhabrot is inside a sphere -that belongs to a hypersphere, but a sphere nonetheless. To define where the "observer" is, we'll use an angle to indicate the latitude, and another for longitude -this procedure would be also useful to render virtual images of the Earth, where it is necessary to "flatten" the visible part of the planet on the plane ( :,D). The generic formula to get the coordinates is this:
              coordX = -x * Sin(long) + y * Cos(long)
              coordY = (x * Cos(long) + y * Sin(long)) * Cos(lat) - z * Sin(lat)

As always, a practical example is much more enlightening; the following code will paint a high-angle shot, half turned, of the BuddhaBrot 3D (Zr,Zi,Cr), rotated 45º towards (Cr,Ci,Zi):

    float factor = ladoImg / 4f;

    float x, y, z;
    // If the observer is looking at a high-angle shot, half turned,
    // latitude and longitude are both 45º.
    // We need the sin and cos of both angles.
    float latitud, longitud;
    latitud = longitud = (float)(Math.PI / 4);
    float sinA1 = (float)Math.sin(latitud);
    float sinA2 = (float)Math.sin(latitud);
    float cosA1 = (float)Math.cos(longitud);
    float cosA2 = (float)Math.cos(longitud);
    // 45º rotation on each axis for the 4D rotation.

    float angRotX, angRotY, angRotZ;
    angRotX = angRotY = angRotZ = (float)Math.sin(Math.PI / 4);
    // For the Buddhabrot, you can use the regions optimization;
    // I haven't included them here to save some space.
    for (Cr = -2; Cr < 2; Cr += increm) {
      maxCi = (float)Math.sqrt(4 - Cr * Cr);
      for (Ci = 0; Ci < maxCi; Ci += increm) {        
        ZrAux = Zi = n = 0;
        while (n < maxN) {
          Zr = ZrAux * ZrAux - Zi * Zi + Cr;
          trayectoria[n][1] = Zi = 2 * ZrAux * Zi + Ci;
          trayectoria[n++][0] = ZrAux = Zr;
          if (n == limiteN) {
            x = trayectoria[limiteN - 1][0];
            y = trayectoria[limiteN - 1][1];
            for (j = limiteN - 2; j >= 0; j--) 
              if (x == trayectoria[j][0] && y == trayectoria[j][1]) j = -1;
            if (j == -2) { n = maxN; continue; }
          if (Zr * Zr + Zi * Zi > 4) {

            for (n--, i = 0; i < n; i++) {
              x = trayectoria[i][0] + (Cr - trayectoria[i][0]) * angRotX;
              y = trayectoria[i][1] + (Ci - trayectoria[i][1]) * angRotY;
              z = Cr + (trayectoria[i][1] - Cr) * angRotZ;
              coordX = 
                (int)(((-y * sinA2 + x * cosA2) + 2) * factor);
              coordY = 
                (int)((((y * cosA2 + x * sinA2) * cosA1 - z * sinA1) + 2) * factor);
              if (coordX >= 0 && coordX < ladoImg && coordY >= 0 && coordY < ladoImg) 
              z = Cr + (-trayectoria[i][1] - Cr) * angRotZ;
              coordX = 
                (int)(((y * sinA2 + x * cosA2) + 2) * factor);
              coordY = 
                (int)((((-y * cosA2 + x * sinA2) * cosA1 - z * sinA1) + 2) * factor);
              if (coordX >= 0 && coordX < ladoImg && coordY >= 0 && coordY < ladoImg) 
4D animation

To close this chapter, I've home-brewed an animation that I hope will illustrate the buddhabrot's endless complexity and infinite beauty.

To create this last animation, I've used "BuddhaFiles" (see the end of chapter 5), which are spot on to render a shitload of images in one go (:'D). Despite all that, I've needed 3 weeks to piece it all together!

You can watch it on youtube, or download via torrent or eMule.

Copyright © Albert Lobo Cusidó 2006-2014