Files
skills/shader-dev/reference/texture-mapping-advanced.md
shihao 6487becf60 Initial commit: add all skills files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:52:49 +08:00

88 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
1. Axis selection (`ma`, `me`) creates UV discontinuities at projection boundaries
2. Hardware `texture()` computes mip from implicit derivatives, which spike at discontinuities → visible seams
3. `textureGrad()` with manually propagated `dFdx(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 `pos` with normal `nor`
- Neighbor pixel ray `rd_neighbor` originates from `ro_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:
1. **Periodic patterns**: Regular grid alignment
2. **Unique features**: Distinctive spots/marks that repeat identically
3. **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.