I wanted to play around with some shaders recently so I was looking for simple things to implement. One of the nicest ways to get started in a new graphics framework is to get some fractals going—so this is what I did.

The Mandelbrot shader

We first require some GLSL magic to make the Mandelbrot fractal happen:

uniform int iterations;

uniform highp vec2 centre;
uniform highp float scale;

varying highp vec2 texture_out;

void main()
{
  vec2 z;
  vec2 c;
  c.x = scale * ( 3.0 * texture_out.x - 2.0 ) + centre.x;
  c.y = scale * ( 2.0 * texture_out.y - 1.0 ) + centre.y;

  z = c;

  int i = 0;
  for(; i < iterations; ++i)
  {
    float x = z.x*z.x - z.y*z.y + c.x;
    float y = z.x*z.y + z.y*z.x + c.y;

    if( x*x + y*y > 4.0 )
      break;

    z.x = x;
    z.y = y;
  }

  vec4 color = vec4(0.0);

  if(i < iterations - 1)
  {
    color.x = sin(float(i) / 3.0);
    color.y = sin(float(i) / 6.0);
    color.z = cos(float(i) / 12.0 + 3.141 / 4.0);
  }

  gl_FragColor = color;
}

Basically, I am filling the texture coordinates with colours, based on the number of iterations it took for the series to reach the desired norm of 4.0. The scale and centre parameter exist only for the purpose of modifying the view within the controlling application.

The Julia set shader

Now let's add another shader for the Julia set:

uniform int iterations;

uniform highp vec2 c;
uniform highp vec2 centre;
uniform highp float scale;

varying highp vec2 texture_out;

void main()
{
  vec2 z;
  z.x = scale * ( 3.0 * texture_out.x - 2.0 ) + centre.x;
  z.y = scale * ( 2.0 * texture_out.y - 1.0 ) + centre.y;

  int i = 0;
  for(; i < iterations; ++i)
  {
    float x = z.x*z.x - z.y*z.y + c.x; 
    float y = z.x*z.y + z.y*z.x + c.y; 

    if( x*x + y*y > 4.0 )
      break;

    z.x = x;
    z.y = y;
  }

  vec4 color = vec4(0.0);

  if(i < iterations - 1)
  {
    color.x = sin(float(i) / 3.0);
    color.y = sin(float(i) / 6.0);
    color.z = cos(float(i) / 12.0 + 3.141 / 4.0);
  }

  gl_FragColor = color;
}

Note that the differences between the shaders are minuscule at best.

Using Qt 5 to put it together

Putting the fractals on the screen then turned out to be a rather easy exercise with Qt 5. Using a QGLShaderProgram, I only need to render a quad onto the screen, bind the appropriate shader, and we are done. Check out the code on GitHub for more details.

Screenshots

Click to see the screenshots in their original resolution.

The Mandelbrot fractal in all its glory. Such colours, such self-similarity. Wow!
Mandelbrot fractal
A zoomed-in portion of the Mandelbrot fractal. Still nice, I guess.
Mandelbrot
fractal, zoomed in
A particular view on the Julia set.
Julia set