Foliage Atlas: Thinning Canopies, Grey Fringes, and Border Flashes
An outdoor scene uses a 4096² foliage atlas: 64 leaf-card clusters, each in a 512² tile with an 8-pixel gutter filled by RGB dilation. Textures are stored as sRGB with straight alpha, and the engine generates a full 13-level mip chain with a box filter that averages R, G, B, and A independently, on the sRGB-encoded values. Alpha-test cutoff is 0.5, filtering is 8× anisotropic. Three symptoms appear. At 40–90 m the canopy thins and trees look sparser than their silhouettes at 5 m. Leaf edges acquire a grey-black fringe that worsens with distance. When the camera rolls, thin bright lines flash along tile borders at mip level 5 and above. Artists cannot repack tiles or add geometry; they get one atlas rebuild plus arbitrary engine changes. The mobile path lacks alpha-to-coverage, desktop has it, and both must ship. The frame budget rules out full-resolution alpha blending.
1. Diagnose each of the three symptoms and name the mechanism behind it.
Read the worked answer
1. Thinning canopy. Alpha test is a binary decision on the filtered alpha. Where a card occupies only a few texels, box filtering averages leaf alpha with the surrounding zeros, pulling the mean below 0.5 for thin or ragged regions; those fragments are then discarded entirely. Coverage therefore shrinks with distance - a mip-level-dependent silhouette. Note that the sparsity is not in the source art (silhouettes match at 5 m) but introduced by averaging plus thresholding.
2. Grey-black fringe. The RGB stored in transparent texels is typically black or undefined, and any partially covered texel mixes that black into visible leaf colour, darkening edges. Two aggravators: the filter averages straight (non-premultiplied) alpha, so colour is weighted by coverage incorrectly, and it runs on sRGB-encoded values, which is a nonlinear average and biases results dark. The effect worsens with distance because more transparent texels contribute at coarser levels.
3. Border flashes at mip 5+. The 8 px gutter is fixed while a mip-n texel covers 2^n source texels, so the gutter shrinks to 8/2^n texels. At mip 3 it is 1 texel (marginal); at mip 5 it is 0.25 texel. Bilinear and especially 8× anisotropic taps then reach across into the neighbouring tile, pulling in whatever bright colour that tile contains. The roll dependency appears because anisotropic tap length depends on the screen-space axis direction.
2. Give the gutter arithmetic and the mip-generation changes, and rank the fixes by cost and risk.
Read the worked answer
Gutter arithmetic. For a mip pyramid down to level N you need a margin of at least 2^N source texels on every side so that a bilinear tap at the coarsest level stays inside the tile. At mip 13 that is 8,192 texels - impossible in a 512 tile. Practical options:
- cap the atlas mip level where the margin is still ≥ 1 texel, i.e. mip 3 (8 → 1), and supply distant trees from a separate far atlas or a texture array of per-card textures;
- move to a texture array / bindless per-card sampling with clamp addressing, which eliminates cross-tile bleed structurally and is the correct long-term fix;
- if keeping the atlas, widen the margin to 32–64 texels by shrinking each tile, accepting fewer effective texels per card, and remember anisotropic filtering extends taps further than isotropic, so add 2× headroom.
Mip generation changes. Generate in linear light: decode sRGB → linear, then build the chain. Filter colour weighted by alpha (sum of RGB·A divided by sum of A) or premultiply alpha before filtering, so transparent black never contaminates visible colour; fill gutter texels by dilation before the chain is built.
Ranking. (1) Alpha-weighted, linear-light mip generation - cheapest, fixes the fringe, low risk. (2) Mip-level cap or texture-array migration - fixes borders; the cap is cheap but blurrier, the array is more work but correct. (3) Coverage preservation for thinning, below.
3. Propose a coverage-preserving alpha solution that ships on both the desktop and the mobile path, and state the trade-offs.
Read the worked answer
The root problem is that a binary cutoff destroys the sub-texel coverage information that mip filtering has already computed. Convert that fractional alpha into partial sample coverage instead of discarding it.
Desktop (has A2C and MSAA). Bind MSAA (e.g. 4×) and enable alpha-to-coverage with the standard dither mask. A leaf whose filtered alpha is 0.3 covers roughly 30% of the samples, so the statistical silhouette is preserved and distant canopies keep their density instead of eroding. Keep the depth write, which is what A2C buys you over blending. Watch the cost of MSAA in a bandwidth-sensitive frame, and remember that A2C interacts with order-independent transparency if foliage later needs blending.
Mobile (no A2C). Two viable paths:
- Stochastic/dithered alpha with a blue-noise or rotated-Bayer threshold and temporal accumulation. Coverage is preserved on average, at the cost of noise and TAA ghosting on thin geometry.
- Coverage-preserving mip rescaling: during mip generation, rescale the stored alpha so that thresholding the mip reproduces the original level-0 coverage fraction. Cheap, no noise, but it saturates alpha and can look crunchy at coarse levels.
Trade-off summary. A2C is the best quality/cost point where available; dithering is the honest cross-platform fallback but must be paired with a TAA that is tuned for alpha-tested geometry; alpha rescaling is the safest if the mobile GPU is already at its bandwidth limit. Ship A2C on desktop, alpha rescaling plus dithered fade on mobile, and validate with a coverage-versus-distance metric rather than screenshots, since the symptom is statistical.