Shading Fractals

5 min read

KorigamiK
Table of Contents
  1. Introduction
  2. The Code
  3. WebGL Live Demo
  4. Fractal Theory
    1. The Mandelbrot
    2. The Julia
  5. The Technology
    1. OpenGL
  6. Shaders
  7. Conclusion
  8. Resources

Introduction

I’ll be talking about what I learned in the past few days about shaders and fractals.

This is the first time I actually use OpenGL and shaders, so I’m not sure if I’m doing things right.

All of my previous knowledge about graphics and rendering comes from writing software rendered homebrew for my Sony PSP XD

My goal here was only to learn how to use shaders and OpenGL, and whatching The Code Poet’s videos on YouTube made me absolutely love all of his work with these super cool fractals!

The Code

While the entire code is available at the GitHub repository.

I’ve been loving (and dreading) CMake and found out about generating automated documentation using Doxygen which all you Rust developers get for free 😋

So I’m hosting the documentation for the project Here.

It's not much, but it's honest work.

And here’s a live demo of the code running in the browser using WebGL:

WebGL Live Demo

Open in a new tab

Works best on desktop 🙃

  • Drag to pan
  • Use the Arrow Up and Down keys to change the render depth
  • And scroll to zoom in and out.

Fractal Theory

I’m not going to go into the theory of fractals, but I’ll try to explain what I’ve learned about them.

Fractals are a mathematical concept that can be used to describe a shape that is self-similar at different scales.

The most common fractal is the Mandelbrot Set,

The Mandelbrot

Condsider this function,

f(z):=z2+czCf(z) := z^2 + c \hspace{.5cm} z \in \mathbb{C}

The Mandelbrot set is the set of complex numbers cc for which the function ff does not diverge when iterated from z=0z = 0.

The Julia

The Julia set is the set of complex numbers cc for which the function ff does not diverge when iterated from z=cz = c.

But enough with the theory, let’s get to the code!

The Technology

So, developing everything cross-platform is what I’m aiming for, so I’m using CMake to generate the build files for the project.

And of course, the ol’ faithful SDL2 handling and managing the window events.

I’m using the Emscripten toochain to compile the code to WebAssembly which you played with in the live demo above.

OpenGL

I know you already have heard about OpenGL, but I’ll try to explain what I’ve learned about it.

OpenGL is a pain-in-the-ass cross-platform API for rendering 2D and 3D graphics.

It abstracts the graphics in a rendering pipeline which looks a little something like:

ApplicationAPI callsOpenGLCPUGPUGeometryVertex ShaderRasterizationFragment ShaderPixel Texture Units Texture\begin{CD} \text{Application} @>\text{API calls}>> \text{OpenGL}\\ @VV\text{CPU}V @VV\text{GPU}V \\ \text{Geometry} @>\text{Vertex Shader}>> \text{Rasterization} @>\text{Fragment Shader}>> \text{Pixel} \\ @. @VV\text{Texture Units}V \\ @. \text{Texture} \end{CD}

Now, I didn’t need to use textures for this project but it’ll be fun to try them out in the future.

The docs summarize how the whole project is structured and how the rendring is handled, I’ll quickly mention the most essential part that makes this possible.

Shaders

To talk about shaders, they are a way to run code on the GPU. The shaders can be compiled from the OpenGL Shading Language (GLSL). They look very similar to C but are even more fun to write.

To be honest I don’t fully understand much of the theory behind them, but I’ll briefly explain the common parts of the shaders.

A typical Vertex Shader looks like this:

#version 300 es
 
in vec2 a_position; // The position of the vertex
 
out vec2 v_texcoord; // Pass the texcoord to the fragment shader
 
void main() {
    gl_Position = vec4(a_position, 0.0, 1.0);
    v_texcoord = (a_position + vec2(1.0)) / vec2(2.0);
}

Which is pretty simple, it just passes the position of the vertex to the fragment shader:

#version 300 es
 
precision mediump float;
 
in vec2 v_texcoord; // The texcoord from the vertex shader
 
out vec4 out_color; // The color of the pixel
 
void main() {
    out_color = vec4(v_texcoord, 0.0, 1.0);
}

And the fragment shader just outputs the color of the pixel.

I’ll leave the rest of the code for you to explore in the GitHub repository.

Conclusion

Graphics programming is a lot of fun, I’ll try to write more about it in the future and possibly add more fractals to this project.

I’ll leave you with a few links to resources that may be helpful to you if you’re interested exploring more about this topic.

Resources