Shaders

What are shaders?

Shaders are the instructions that the gpu uses to calculate the colour data of each individual pixel, and are made up of two parts: a fragment shader and a vertex shader.

Fragment shaders calculate the colour data of each pixel and passes it on to the gpu, while the vertex shader calculates the geometry of the shapes and models.

For simplicity I will only be talking about the fragment shader as the vertex shader is much more complex.

What does a fragment shader look like?

Here is a very basic example of a fragment shader:

This shader takes in the x and y coordinates relative to the canvas size and outputs it to the red and green colour channels respectively.

The shader code is written in a language called GLSL (openGL shading language) which is a language very similar to C, and looks like this:

      precision highp float;
      out vec4 outColor;
      void main() {
        outColor = vec4(gl_FragCoord.xy/400., 0, 1);
      }
  

However the only part we need to look at is this:

      outColor = vec4(gl_FragCoord.xy/400., 0, 1);

What this is doing is applying the colour of each individual pixel to a vector4 (note that although there are only 3 arguments, gl_FragCoord.xy is a vector2) where the first two elements are the value of the pixel coordinates in relation to the canvas size, and the last element being 1, so each pixel is at full opacity, as we are using RGBA.

What can you do with a fragment shader?

Because fragment shaders can be manipulated with code, this makes them very versatile as you can make any image you can program which is what allows the vertex shader to be used in conjunction with it to display the models in anything 3 dimensional.

However, without a vertex shader, fragment shaders are limited to 2 dimensions (excluding any tedious maths you might think of to make a 3d image).

This isnt as useful as 3 dimensions of course, however, because it can be manipulated with code, it makes it very useful to render mathmatical imagery such as fractals or visualising functions.

For example, heres a render of the mandlebrot set:

This is done with the code:

      precision highp float;
      uniform vec2 z;
      uniform vec2 c;
      uniform float col;
      out vec4 outColor;

      vec2 f(vec2 z, vec2 c) {
        return mat2(z, -z.y, z.x) * z + c;
      }

      void main() {
        vec2 c = gl_FragCoord.xy;
        c = vec2(c.x - 300., c.y-200.)/200.;
        vec2 z = vec2(0.);
        float col = 0.;
        for (int i=0; i < 1000; i++) {
          z = f(z, c);
          if (length(z) > 2.) {
            col = 1.;
            break;
          }
        }
        outColor = vec4(vec3(1) * col, 1);
      }
  

This is just one of many complicated things you can render with a fragment shader, if you want to try it out for yourself without having to figure out how to set it up with WEBGL, I recommend shadertoy.