Mandelbrot Fractals in OpenGL

A Mandelbrot Fractal is a fractal from the Mandelbrot Set, in this article we'll discuss rendering the fractals in version 330 of the OpenGL Core Profile

Tags: OpenGL, C++, Math

Published: 05/15/2020

By: Brennen Green


The Theory

The Mandelbrot Set, named after mathematician Benoit B. Mandelbrot and defined as the set of values of c in the complex plane for which the orbit of 0 under iteration of the quadratic map \(z_{n+1} = z_{n}^{2}+c\) remains bounded. Thus, a complex number c is a member of the Mandelbrot set if, when starting with \(z_{0} = 0\) and applying the iteration repeatedly, the absolute value of \(z_{n}\) remains bounded for all \(n\gt0\). While the mathematic intricacies are interesting, they are out of the scope of this article and you can read more about them here

From Math to The Computer Screen

The math itself is extremely cool, but perhaps the coolest thing about fractals is the hypnotic infinite repition of their small details. Because of this, I and many other programmers and mathematicians have always set out to find cool ways to use a computer to visualize these fractals. In perhaps one of the most simplest ways, I accomplished this with Python and created this nice little image:

The issue with this however, is the lack of zooming capabilities or use of the GPU to decrease processing time. I wanted to be able to massively increase the time and quality of my render, so I looked to OpenGL to help me take advantage of the GPU. Rendering a 12k version of this image, took rougly 40 minutes on my (fairly high performance) computer.

OpenGL Implementation

My biggest reasoning for writing this blog post is the lack of information for achieving this visualization using the Core Profile of OpenGL. While indeed there are many resources for creating a Mandelbrot Fractal in the Compatibility Profile of OpenGL, these were often outdated and taught practices that are no longer seen as the best in the Computer Graphics domain. While I do not claim to be a graphics professional in anyway, I felt that it would be a great idea to outline these steps translated to the core profile. Because I did not come up with these myself, in fact I used one of the aforementioned resources, you can find that article post here by John Tsiombikas.

I would also like to not that, I will not be covering the basics of OpenGL. Setting up the window, controlling the camera, buffers, etc will be excluded and more information on the basics of OpenGL can be found below in the Additional Resources section.

Rendering a Quad and Texture Configuration

In order to render the fractal, we must have vertex data to pass into the graphics pipeline. We also will take use a of 1x256 palette texture to determine the color of every individual pixel. You can use this pre-defined vertex data or however you prefer to render a quad to the screen:

				
					float vertices[] = {
						1.0f,  1.0f, 0.0f, 1.0f, 1.0f, // top right
						1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom right
						-1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom left
						-1.0f,  1.0f, 0.0f, 0.0f, 1.0f, // top left 
					};
					unsigned int indices[] = {  // note that we start from 0!
						0, 1, 3,  // first Triangle
						1, 2, 3   // second Triangle
					};
				
			
You'll also want to set up a texture for the shader, you can use this as the texture:

The Fragment and Vertex Shaders

While probably the most important part of this implementation, these shaders are rather simple. To implement, the vertex shader is perhaps the most barebones vertex shader one can produce:

				
					// fractal.vert
					#version 330 core
					layout (location = 0) in vec3 aPos; // X Y Z Position
					layout (location = 1) in vec2 aTexCoord; // X Y Texture Cooridinate

					out vec2 TexCoord;

					void main()
					{
						TexCoord = aTexCoord;
						gl_Position = vec4(aPos, 1.0);
					}
				
			

The fragment shader however is a bit more beefy and deserves a bit more description with it. Here is the code, commented but also with description below:
				
					// fractal.frag
					out vec4 FragColor;
					in vec2 TexCoord;

					uniform sampler2D texture1; // Pallete Texture Sampler
					uniform vec2 center; // Reference Center of the Complex plane
					uniform float scale; // The zoom factor for scaling
					uniform int iter; // The maximum number of iterations

					void main()
					{
						vec2 z, c; // C here is the familiar c from the quadratic map given earlier in the blog post
						c.x = 1.3333 * (TexCoord.x - 0.5) * scale - center.x; // Properly scale the x coordinate of the complex number 
						c.y = (TexCoord.y - 0.5) * scale - center.y; // Properly scale the y coordinate of the complex number 

						int i; // Current iteration
						z = c; // Copy c to preserve value as we iterate
						// This for loop, is doing the non-trivial work.
						// It is doing the reptitive iteration we mentioned in the theory section of the article. 
						for (i=0; i < iter; i++) {
							float x = (z.x * z.x - z.y * z.y) + c.x;
							float y = (z.y * z.x + z.x * z.y) + c.y;

							if((x * x + y * y) > 4.0) break;
							z.x = x;
							z.y = y;
						}

						// Remember that we are using a 1D texture, so we only want to reference the x value of the texture
						// We either want to color the pixel as part of the set or color it based on its ability to go to infinity
						FragColor = texture(texture1, vec2((i == iter ? 0.0 : float(i)) / 100.0), 1); 
					}
				
			

Results

For me, this implementation produces the exact results as I was seeking. The quality is optimal and the time complexity is so small its not worth measuring.

Here is the fractal represented in its entirety, you can see the huge improvements to aesthetic and resolution even from afar:


For better understanding of the zooming capabilities, here is a zoomed in shot of what mathematicians refer to as "Sea Horse Valley"


I hope you enjoyed this article, and please let me know if there is any way I can assist you in getting this working properly. I have linked several additional resources for your alls convience to help in your endeavors. Thanks :)

Additional Resources: