A GPU turns triangles into pixels through a fixed sequence of stages. Understand the pipeline — vertex processing, rasterization, fragment processing — because everything you do in graphics plugs into it.
The GPU renders by pushing geometry through a pipeline: your mesh’s vertices are transformed and assembled into triangles, those triangles are rasterized into fragments (candidate pixels), and each fragment is shaded to a color and written to the screen. Every graphics technique — lighting, texturing, effects — is something you do at one of these stages, so the pipeline is the map for the whole field.
The GPU pipeline (simplified):
VERTICES your mesh's points (position, normal, uv, ...)
| VERTEX SHADER transform each vertex to clip space (your code)
PRIMITIVES vertices assembled into triangles
| RASTERIZATION triangles -> FRAGMENTS (per-pixel candidates) (fixed)
FRAGMENTS one per covered pixel, with interpolated data
| FRAGMENT SHADER compute each fragment's COLOR (your code)
PER-FRAGMENT depth test, blending -> the framebuffer (fixed)
PIXELS on screen
You program the VERTEX and FRAGMENT shaders; the GPU does rasterization,
depth testing, and blending. Everything in graphics plugs into this flow.Rasterization is the fixed-function step that converts a triangle into the fragments it covers, deciding which pixels are inside it. As it does, it interpolates the vertices’ attributes — color, texture coordinates, normals — across the triangle’s surface, so each fragment gets smoothly blended values. This interpolation is why a gradient across a triangle "just works" and is central to how shaders receive per-pixel data.
Rasterization: triangle -> the pixels it covers, with INTERPOLATED data.
v0 (red)
*\
| \ each fragment inside the triangle gets values blended from
| \ the three vertices, by its position (barycentric weights):
| \ color, UV coords, normal, depth...
v1*------ *v2
(green) (blue) a pixel in the middle -> a mix of red/green/blue
That interpolation is why the fragment shader receives smooth per-pixel
inputs (a "varying"). The GPU does this for you, extremely fast.When triangles overlap, the GPU must decide which is in front. The depth buffer (z-buffer) stores the closest depth seen at each pixel; a fragment is only drawn if it is nearer than what is already there. This solves hidden-surface removal automatically, letting you draw geometry in any order and still get correct occlusion — a fundamental piece of real-time rendering.
The depth (z) buffer solves "which surface is in front?" per pixel:
for each fragment at pixel (x, y) with depth z:
if z < depthBuffer[x, y]: # nearer than what's there?
colorBuffer[x, y] = fragment.color
depthBuffer[x, y] = z # remember the new nearest depth
else:
discard # something closer already drawn
-> correct occlusion regardless of DRAW ORDER. You don't sort opaque geometry
by hand. (Transparency is the exception — it needs back-to-front blending.)You drive the pipeline through a graphics API — OpenGL, Vulkan, DirectX, Metal, or WebGL — and write shaders in that API’s language (GLSL, HLSL, or the portable SPIR-V). The concepts are identical across them; only the syntax and verbosity differ. This course uses GLSL because it is the most widely taught, and the ideas transfer directly to any engine or API.
Graphics APIs (drive the GPU) Shading languages (write shaders)
OpenGL / OpenGL ES / WebGL GLSL
Vulkan GLSL/HLSL -> SPIR-V (portable bytecode)
DirectX HLSL
Metal (Apple) Metal Shading Language
The PIPELINE + CONCEPTS are the same everywhere; only syntax + verbosity
differ. This course uses GLSL (the most widely taught). What you learn maps
straight to any engine (Unity/Unreal/Godot all expose shaders).