Programmable Shaders

A programmable shader is a small program that runs on the GPU for each vertex or each pixel (fragment) as geometry flows through the rendering pipeline. Programmable shaders replaced the fixed-function pipeline, in which the transform-and-lighting and texture-combiner stages were hard-wired and configurable only through a fixed set of state switches. Instead of selecting from preset behaviors, developers now write the behavior directly, computing positions, colors, lighting, and texture effects in code that executes on the graphics hardware.

The transition was driven by both major graphics APIs. On the Windows side, Microsoft’s documentation states that “HLSL was created (starting with DirectX 9) to set up the programmable 3D pipeline. You can program the entire pipeline with HLSL instructions.” Direct3D organized these capabilities into shader models, successive versions defining what vertex and pixel shaders could do. The shader model 3 reference notes the tightening rules of the programmable era: shaders compiled for vs_3_0 or ps_3_0 “may not use either shader type with the fixed function pipeline,” underscoring that the programmable path had become a distinct, self-contained way to drive the GPU.

The same shift happened in OpenGL through the OpenGL Shading Language (GLSL). The GLSL 1.10 specification, authored under the Khronos/3Dlabs effort in the 2002-2004 period, defined a C-like language for writing vertex and fragment shaders that the OpenGL implementation compiles and runs on the GPU. GLSL gave cross-platform OpenGL applications the same programmable-pipeline capability that HLSL provided for Direct3D, so a developer could express lighting models, procedural textures, and post-processing effects as portable shader source.

Conceptually, the pipeline split into a few programmable stages around a fixed core. A vertex shader runs once per vertex and is responsible for transforming each vertex’s position and computing per-vertex outputs; a pixel or fragment shader runs once per rasterized fragment and computes its final color and other attributes before the fragment is written to the frame buffer. The fixed-function transform-and-lighting unit that the GeForce 256 had put into hardware in 1999 was, within a few years, replaced by these programmable units.

The flexibility of programmable shaders did more than improve graphics. Because a fragment shader is effectively a parallel program executed across many pixels, the model generalized naturally toward general-purpose computation on the GPU, the foundation on which later compute-shader and GPGPU work was built. But the original purpose, and the reason shader models and GLSL were standardized, was to let real-time rendering escape the limits of a fixed pipeline.

This entry uses a year-level date (the early-2000s emergence of Direct3D shader models and GLSL) because the programmable-shader transition unfolded across several API releases rather than on a single day.