Computer Graphics
Shaders: GLSL, HLSL, WGSL
How does the GPU know what color to paint each pixel of a character in a game? How does Stable Diffusion generate images directly on a graphics card? The answer is shaders: tiny programs executed in parallel across thousands of GPU cores. This topic is the heart of GPU programming.
- **PBR in games (Unreal, Unity):** a fragment shader running the Disney BRDF equation - each pixel computes a light scattering integral in real time
- **Stable Diffusion / DLSS:** compute shaders - neural networks run as matrix operations on the GPU, no graphics pipeline involved
- **Shader compilation stutter:** the cause of frame drops in new games - the driver compiles SPIR-V to native code on first pipeline use
- **WebGPU and WGSL:** the new browser graphics standard enabling complex shaders in-browser at near-native performance
Vertex Shader
A single frame in Cyberpunk 2077 contains millions of vertices. The GPU does not draw them one by one - instead it runs a small program (a vertex shader) in parallel for each vertex. Shaders are what allow a GPU to process 10 million triangles per second.
A **vertex shader** is the first programmable stage of the GPU pipeline. It receives the data for one vertex (position, normal, UV coordinates) and must return its position in clip space. In between, it can do anything: deform a mesh, animate a character, pass data to the fragment shader.
**Key rule:** a vertex shader runs exactly once per vertex, and all invocations execute in parallel. There is no way to access a neighboring vertex from within a shader - only the current vertex data and uniform buffers are available.
What must a vertex shader always output?
Fragment Shader and PBR
After rasterization the GPU knows which pixels a triangle covers. For each such pixel (fragment) a fragment shader runs - this is where the final color is determined. All photorealistic effects in modern games - reflections, shadows, metallic sheen - live as code inside fragment shaders.
**PBR (Physically Based Rendering)** has been the industry standard since 2013 (Disney BRDF). The core idea: a pixel's color is the result of a physically accurate light scattering equation. Two key material parameters: **metallic** (metal vs dielectric) and **roughness** (surface smoothness).
**GLSL vs HLSL vs WGSL:** GLSL is the language for OpenGL and Vulkan (compiled to SPIR-V). HLSL is the language for DirectX, the de-facto standard for AAA games on Windows. WGSL is the new WebGPU language - type-safe, runs in the browser. Syntax differs, semantics are nearly identical.
In a PBR material, metallic = 1.0 means the surface:
Compute Shader
Stable Diffusion generates a 512x512 image in seconds directly on a GPU. No vertices, no triangles - only math on tensors. This is possible because of compute shaders: GPU programs that have no ties to the graphics pipeline.
A **compute shader** runs as a three-dimensional grid of workgroups. Each workgroup consists of threads (invocations) that share fast shared memory and can synchronize via barriers. This model makes the GPU powerful for GPGPU tasks: physics, ray tracing, machine learning.
**Compute shaders in games:** particle generation, post-processing (bloom, SSAO, TAA), draw call preparation (GPU culling), ray tracing (BVH traversal), cloth and fluid simulation. Modern games run hundreds of compute dispatches per frame.
What does `barrier()` do in a compute shader?
SPIR-V and Shader Compilation
Before 2015, every GPU vendor (NVIDIA, AMD, Intel) shipped its own shader compiler inside the driver. Developers complained: the same GLSL code compiled differently across vendors, giving different results and different performance. Khronos solved the problem radically by introducing **SPIR-V** - an intermediate representation that the vendor compiler receives instead of raw GLSL.
**SPIR-V** (Standard Portable Intermediate Representation) is a binary bytecode format for shaders, analogous to LLVM IR for CPUs. The developer compiles GLSL/HLSL/WGSL to SPIR-V ahead of time (at game build time), and the driver translates SPIR-V into the native instructions for the specific GPU. This separation removes non-determinism from driver-side compilation.
**Pipeline cache:** even the SPIR-V -> native code compilation takes time (the root cause of 'shader compilation stutter' in games). Vulkan allows caching the result via VkPipelineCache and saving it to disk. Modern games (God of War, Spider-Man) warm up the cache in the background on first launch.
SPIR-V is the final machine code that runs directly on the GPU
SPIR-V is an intermediate representation. The GPU driver compiles it into the native ISA (PTX for NVIDIA, GCN for AMD) when the pipeline is created.
GPUs from different vendors have different instruction sets (ISA). Creating a single universal 'native' format would be impossible. SPIR-V is the standard contract between the developer and the driver.
Why does SPIR-V exist if a driver could just compile GLSL directly?
Shaders: GLSL, HLSL, WGSL
- Vertex shader runs once per vertex in parallel - must output gl_Position in clip space
- Fragment shader determines each pixel's color - this is where PBR (metallic/roughness model) lives
- Compute shader - GPU computation outside the graphics pipeline: ML, physics, post-processing
- GLSL (OpenGL/Vulkan), HLSL (DirectX), WGSL (WebGPU) - different syntax, the same model
- SPIR-V is the intermediate representation: shaders compile to .spv at build time, the driver translates to ISA at runtime
- Pipeline cache addresses shader stutter - the SPIR-V compilation result is saved to disk
Related Topics
Shaders are the programmable part of the GPU pipeline. Running them in Vulkan requires understanding pipeline objects and the memory model.
- GPU Pipeline and Rasterization — Shaders plug into the pipeline at fixed, well-defined stages
- Vulkan and Modern Graphics API — How to load SPIR-V shaders and create a pipeline in Vulkan
Вопросы для размышления
- Why can a vertex shader not access neighboring vertex data, and how does skeletal animation work around this constraint?
- What is the fundamental difference between a fragment shader and a compute shader for image post-processing?
- If SPIR-V solves vendor compiler inconsistency, why does shader compilation stutter still occur in games?