Vertex Shader and Fragment Shader: Differences and Roles
Programmable Stages in the Rendering Pipeline
In modern GPU rendering pipelines, vertex shaders and fragment shaders are two main programmable stages. The vertex shader processes data for each vertex (such as position, normal, UV), performing transformations and lighting calculations; the fragment shader processes each fragment (pixel candidate), computing the final color. They are connected by the rasterization stage, which interpolates vertex data into fragment data.
Vertex Shader
The vertex shader is the first programmable stage in the rendering pipeline, executed once per vertex. Its inputs include vertex attributes (position, normal, color, UV, etc.), and outputs include the transformed vertex position and interpolated data passed to the fragment shader.
Typical functions:
- **Coordinate Transformation**: Transform vertices from model space to world space, view space, and clip space.
- **Normal Transformation**: Handle normal matrices for non-uniform scaling.
- **Texture Coordinate Passing**: Pass UVs or compute special effects (such as animated UVs).
- **Per-Vertex Lighting**: Simple lighting calculations (e.g., Gouraud shading).
The vertex shader cannot create or delete vertices, only process existing ones.
Fragment Shader
The fragment shader (also called pixel shader) is executed once per fragment after rasterization (though some may be discarded by depth testing). Its input is interpolated data from the vertex shader (such as UV, color, world position, normal, etc.), and output is the fragment color (possibly including depth).
Typical functions:
- **Texture Sampling**: Get color from textures.
- **Per-Fragment Lighting**: Compute more accurate lighting (e.g., Phong shading).
- **Color Blending**: Apply alpha blending, fog effects, etc.
- **Post-Processing Effects**: Implement post-processing algorithms on full-screen quads.
The fragment shader is performance-critical because it executes far more times than the vertex shader.
Differences
| Feature | Vertex Shader | Fragment Shader |
|---------|---------------|-----------------|
| Execution Unit | Per vertex | Per fragment (pixel) |
| Execution Count | Fewer | More |
| Input | Vertex attributes | Interpolated fragment data |
| Output | Clip space coordinates, interpolated data | Fragment color |
| Common Functions | Coordinate transformation, skeletal animation | Texture sampling, lighting calculation |
| Performance Impact | Usually smaller | Usually larger |
Collaboration Example
A simple shader program:
Vertex shader:
```glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aUV;
out vec2 vUV;
uniform mat4 uMVP;
void main() {
vUV = aUV;
gl_Position = uMVP * vec4(aPos, 1.0);
}
```
Fragment shader:
```glsl
#version 330 core
in vec2 vUV;
out vec4 FragColor;
uniform sampler2D uTexture;
void main() {
FragColor = texture(uTexture, vUV);
}
```
The vertex shader transforms vertices and passes UVs, and the fragment shader samples the texture to output color.
Frequently Asked Questions
Why do we need two shaders?
Because GPU parallel architecture requires separating vertex processing and fragment processing to optimize throughput. The vertex shader handles geometry, and the fragment shader handles pixels, each with its own role.
Can we do texture sampling in the vertex shader?
Yes, but texture sampling usually requires fragment-level interpolation; vertex-level sampling can cause quality issues (like texture aliasing). Modern GPUs support vertex texture sampling but performance is poor.
Does the fragment shader always execute more times than the vertex shader?
Usually yes, because one triangle covers multiple pixels, but there are optimizations like early depth culling.
How to optimize fragment shader performance?
Reduce texture sampling, avoid complex calculations, use LOD, reduce branches, etc.
How does the geometry shader relate to them?
The geometry shader sits between vertex and fragment shaders, can process whole primitives and generate new vertices, but is less used in modern rendering.
FAQ
Why do we need two shaders?
Because GPU parallel architecture requires separating vertex processing and fragment processing to optimize throughput. The vertex shader handles geometry, and the fragment shader handles pixels.
Can we do texture sampling in the vertex shader?
Yes, but texture sampling usually requires fragment-level interpolation; vertex-level sampling can cause quality issues. Modern GPUs support it but performance is poor.
Does the fragment shader always execute more times than the vertex shader?
Usually yes, because one triangle covers multiple pixels, but there are optimizations like early depth culling.
How to optimize fragment shader performance?
Reduce texture sampling, avoid complex calculations, use LOD, reduce branches, etc.
How does the geometry shader relate to them?
The geometry shader sits between vertex and fragment shaders, can process whole primitives and generate new vertices, but is less used in modern rendering.