# Spatial Aliasing on Grid Lines in GLSL: A Postmortem
## Summary
- Grid lines rendered with a GLSL fragment shader exhibit jagged triangular artifacts ("spatial aliasing")
- Occurs when computing grid line visibility using `mod()`, `dFdx`, and `dFdy` as distance estimators
- Aliasing manifests as sawtooth patterns along grid lines when camera isn't axis-aligned
- Artifacts appear near discontinuous mod boundaries (`N * gridCellSize`)
## Root Cause
- **Discontinuous derivatives**:
- `mod(worldPos.xz, gridCellSize)` creates discontinuities at grid cell boundaries
- `dFdx`/`dFdy` compute pixel differences within a 2x2 quad
- Derivative vectors become misaligned with grid edges when camera rotates
- **Inadequate distance calculation**:
- Using `mod(value)/dfddValue` approximates screen-space distance
- Fails at discontinuity boundaries due to inconsistent derivatives
- **Directional sensitivity**:
- Derivative vectors (`vec2 ly`, `vec2 lz`) contain orientation information
- Rotated camera causes anisotropic derivative distributions
- **Quad displacement**:
- Adjacent pixels in the 2x2 quad straddle mod discontinuity
- Causes abrupt `lod0a` jumps (black↔white) instead of gradients
## Why This Happens in Real Systems
- World-space derivatives (`dFdx`/`dFdy`) are screen-space operators
- Camera rotation creates projection skew:
- World-space grid lines no longer align with pixel grid
- Derivatives capture perspective distortion in pixel quads
- Mod discontinuities amplify derivative calculation errors
- Real-world grids operate in perspective-projected space
- Shader assumes linear derivatives which fail under perspective
## Real-World Impact
- Grid lines appear "broken" with triangle-shaped aliasing artifacts
- Visual distraction in CAD/engineering applications
- Breaks immersion in 3D editors and modeling tools
- Causes perception of rendering glitches
- Overblending when transparency is used
## Example Code
```glsl
// Problematic shader snippet
vec2 ly = length(vec2(dFdx(worldPos.x), dFdy(worldPos.x));
vec2 lz = length(vec2(dFdx(worldPos.z), dFdy(worldPos.z)));
vec2 dydz = vec2(ly, lz);
float lod0a = max(vec2(1.0) - mod(worldPos.xz, gridCellSize) / dydz);
How Senior Engineers Fix It
- Use screen-space distance fields:
- Compute actual world-position distances to grid lines
- Apply analytic filtering:
smoothstep() for antialiased edge transitions
- Example replacement:
float lineWidth = 0.02;
vec2 gridDist = abs(mod(worldPos.xz + 0.5*gridCellSize, gridCellSize) - 0.5*gridCellSize);
float gridAlpha = 1.0 - min(1.0, max(gridDist.x, gridDist.y)/(lineWidth*0.5));
gridAlpha = smoothstep(0.0, 1.0, gridAlpha);
- Derivative normalization:
- Replace
length(vec2(dFdx(·), dFdy(·)) with fwidth(·)
- Frequency clamping:
- Set minimum derivative thresholds for LOD transitions
- Screen-space antialiasing:
- Supersampling/MSAA for high-angle perspectives
Why Juniors Miss It
- Misunderstanding derivative spaces:
- Assuming
dFdx/dFdy operate in world space
- Mod discontinuity oversight:
- Not anticipating periodic function edge behavior
- Perspective projection gaps:
- Overlooking camera/world-space misalignment effects
- Lack of derivative visualization:
- Not debugging
dFdx/dFdy as vector fields
- Premature optimization mindset:
- Choosing “clever” math over robust distance fields