My first peek down the rabbit hole that is shader programming

The term shader gets thrown around a lot these days. In its crudest form - a shader is a small program that runs on your computers GPU. The most common types are vertex and fragment shaders and both are generally used when you draw any object onto the screen.

GLSL, sometimes known as OpenGL Shading Language is a turing complete programming language specifically designed for creating these types of programs. It is usually written with an imperative style; uses a C-style syntax and is statically typed.

OpenGL is an interface for the computers graphics hardware. Its used by games and web browsers for graphics acceleration.

Meshes

Everything in 3D computer graphics is generally formed of a mesh. A mesh is a collection of triganles. The triganle is made up of three vertercies; and when drawn on a screen generally overlaps with a certain number of pixels on that screen and each pixel becomes a part of the triangle.

When you define a vertex shader program - each verticies of the triangle will run it; doing things like passing on information for drawing each pixel and transforming the vertex to from local space clip space.

Each pixel within the triangle runs a fragment shader - this manages textures; mixing the triangle color and outputting colors for the screen.

Transformation pipelines

This is the process that handles how meshes and models get rendered onto a screen in 3D space.

Local space (a.k.a Object space)

This is the world - from the point of view of the current subject. Imagine a model for an NPC for a game. It has its own point of view in the game world. So does the model for the main character you control. They both have their own refernce for what is left, right, up and down; relative to that model in the game.

World space

Local space objects need to get transformed into one singular world space. Take the example of a Main playable character model and an NPC character model; Each object in world space is defined relative to the world - much like grid co-ordinates.

View space

World space is then transformed into View space.

In 3D programming and design - the concept of a “camera” is ubiquitous. A camera is able to see the world usually in one direction and 2 dimensions.

In View space things like position and roation is defined relative to the camera.

Clip space

View space is then transformed into clip space

Clip space is an abstract conept; Clip space is the result of running vertex shader progams. It is the sum of World + View + Projection transformations.

Things are now handed off to OpenGL - where Clip space is transformed into normalized device co-ordinate space; and then onto screen space where objects take their final position to be rendered onto.

Shader Inputs

Shader programs have 3 ways of setting parameters.

  • Uniforms
  • Attributes
  • Varyings

Uniforms

This is a read-only variable that is globally available within your program. This means that every shader instance (both vertex and fragment) reads the same value. This is a useful method for setting global pieces of state.

uniform vec4 initalAlpha;

Attributes

These are local variables for vertex shaders. Things like positions, texture values or colours. These can be controlled and changed throughout the lifetime of the program.

attribute vec4 currentAlpha;

Varyings

Varyings are an abstract one-way flow of data from the vertex to the fragment shader. Each verticies in a triangle runs the vertex shader - The GPU runs the program to transform and output the position of the vertex - each vertex also outputs varyings.

Varyings are not the same across all vertexes. Each pixel in the triangle also runs a fragment shader - which has access to uniform values available to the entire program - but also the varyings from the responsible vertex shader.

varying vec4 finalAlpha;

Final thoughts

A vertex shader is most commonly responsible for calculating gl_Position and supplying varyings to fragment shaders.

gl_Position is a built-in variable in GLSL - it is the final position of each vertex in the 3D space after all transformations in the pipeline have been applied.

A fragment shader is responsible for calculating gl_FragColor (also a built in variable in GLSL) - which is the final colour the fragment shader will render the pixel as.

both gl_Position and gl_FragColor are always vec4 values.

gl_Position consists of (X, Y, Z, W)

  • X - The horizontal position.
  • Y - The vertical position.
  • Z - The depth (distance from the camera).
  • W - The homogeneous coordinate (used for perspective division).

gl_FragColor consists of (R, G, B, A)

  • R - Red
  • G - Green
  • B - Blue
  • A - Alpha