3.3 KiB
Advanced Texture Mapping Detailed Reference
Prerequisites
- Screen-space derivatives (
dFdx,dFdy) textureGrad()function usage- Basic ray marching
Triplanar vs Biplanar Cost Analysis
| Aspect | Triplanar | Biplanar |
|---|---|---|
| Texture fetches | 3 | 2 |
| ALU operations | Lower | Higher (axis selection) |
| Bandwidth | Higher | Lower |
| Visual quality | Baseline | Equivalent (k≥8) |
| Best for | Bandwidth-rich GPUs | Mobile, bandwidth-limited |
Modern GPUs are typically bandwidth-limited rather than ALU-limited, making biplanar the better default choice.
Weight Remapping Mathematics
The biplanar weight formula clamp((w - 0.5773) / (1.0 - 0.5773), 0, 1) ensures:
- At normals aligned with one axis: weight = 1.0 (clean projection)
- At 45° diagonals where 2 axes are equal: smooth transition
- At the cube diagonal (1/√3 ≈ 0.5773): weight = 0.0, but this is the point where the third (discarded) projection would be needed — biplanar's approximation error is maximal here but visually acceptable
Gradient Propagation
Using textureGrad() instead of texture() is essential because:
- Axis selection (
ma,me) creates UV discontinuities at projection boundaries - Hardware
texture()computes mip from implicit derivatives, which spike at discontinuities → visible seams textureGrad()with manually propagateddFdx(p),dFdy(p)bypasses this, keeping gradients smooth across boundaries
Ray Differential Mathematics
Problem Statement
In rasterization, dFdx/dFdy of texture coordinates work naturally because adjacent pixels map to nearby surface points. In ray marching, adjacent pixels may hit completely different objects → broken mip selection.
Solution: Tangent Plane Intersection
Given:
- Primary ray hits surface at
poswith normalnor - Neighbor pixel ray
rd_neighbororiginates fromro_neighbor
The neighbor ray's intersection with the tangent plane at pos:
t_neighbor = dot(pos - ro_neighbor, nor) / dot(rd_neighbor, nor)
pos_neighbor = ro_neighbor + rd_neighbor * t_neighbor
The difference pos_neighbor - pos gives the world-space footprint of one pixel at the hit point.
For Perspective Cameras (Common Case)
ro is the same for all pixels, only rd varies:
dposdx = t * (rdx * dot(rd, nor) / dot(rdx, nor) - rd)
dposdy = t * (rdy * dot(rd, nor) / dot(rdy, nor) - rd)
Where rdx = rd + dFdx(rd) and rdy = rd + dFdy(rd).
Chain Rule for Texture Coordinates
If texture mapping function is uv = f(pos):
duvdx = Jacobian(f) × dposdx
duvdy = Jacobian(f) × dposdy
For simple planar mapping uv = pos.xz:
duvdx = dposdx.xz
duvdy = dposdy.xz
Texture Repetition Theory
Why Tiling is Visible
Human vision excels at detecting:
- Periodic patterns: Regular grid alignment
- Unique features: Distinctive spots/marks that repeat identically
- Phase alignment: All tiles start at the same phase
Breaking Repetition
Each method targets different cues:
- Random offset (Method A): Breaks phase alignment, 4 fetches
- Voronoi blend: Breaks grid structure entirely, 9 fetches (expensive)
- Virtual pattern (Method B): Breaks unique features cheaply, 2 fetches
Method B is preferred for real-time use — the low-frequency index variation is cache-friendly and the two texture fetches share locality.