Skip to content

Instantly share code, notes, and snippets.

@vassvik
Last active February 12, 2026 10:29
Show Gist options
  • Select an option

  • Save vassvik/6a145d1e4efb503438816b02d40e8156 to your computer and use it in GitHub Desktop.

Select an option

Save vassvik/6a145d1e4efb503438816b02d40e8156 to your computer and use it in GitHub Desktop.
Short description of a spherically distorted frustum grid. Authored with Claude Code, so some inconsistencies and oversights might have slipped through.

Spherically Distorted Frustum Grid

This document describes a variant of the frustum grid that uses spherical depth shells instead of planar depth slices, while retaining the standard perspective lateral coordinates.

Planar Grid Overview

The planar frustum grid uses a piecewise depth distribution. Key parameters:

  • κ = 2·tan(θ/2)/N — pixel cone slope (θ = FOV, N = pixels along one axis)
  • s — voxel size (world-space units)
  • r_d = (2 + κ)/(2 - κ) — depth ratio between consecutive far-field slices

Depth distribution:

  • Far field (k ≥ 0): Exponential depth z_k = z₁ · r_d^k, giving cubic froxels (depth ≈ lateral extent)
  • Near field (k < 0): Linear depth z_k = z₁ + k·s with constant slice thickness s
  • Crossover (k = 0): At depth z₁ = s/ln(r_d) ≈ s/κ, where froxel depth extent matches voxel size

The spherical variant replaces planar depths z with radial distances, keeping the same piecewise structure and lateral pixel coordinates.

Interactive diagram: Desmos geometry graph

image

The diagram shows both grid types side by side:

  • Left (spherical depth): Circular arc depth boundaries. Froxels maintain uniform shape across the FOV — each cell has similar proportions regardless of angle from center.
  • Right (planar frustum): Straight vertical depth boundaries (z = const). Froxels at the edges are visibly elongated along the view ray.
  • Red (near field): Finer linear subdivision near the camera in both cases.
  • Dashed black lines: LOD level boundaries where froxel-to-voxel size ratio ρ = 1, 2, and 4 respectively. At ρ = 1 (crossover), froxels match voxel size; beyond each dashed line, froxels are 2× larger than at the previous boundary.

The edge elongation on the right is the shape distortion that motivates this variant. The left shows how spherical shells eliminate it at the cost of lateral narrowing (cells are narrower at the edges, but still well-proportioned). In the planar grid, froxel axes are misaligned with the view ray at edges, causing trilinear interpolation to mix depth samples in a position-dependent way.

image

At 90° FOV, the distortion difference becomes more pronounced. Edge rays are at 45° from the view axis, so in the planar frustum (right), edge froxels are elongated by a factor of √2 compared to center froxels. The spherical variant (left) maintains uniform cell proportions even at this extreme angle.

Approach

Replace planar depth slices with spherical depth shells (r = const), while keeping perspective lateral coordinates (i, j):

  • Depth coordinate: radial distance r from camera
  • Lateral coordinates: standard pixel grid (i, j)

This gives each froxel a depth direction aligned with its view ray, eliminating the shape distortion at edges.

Spherical Depth Formulas

The piecewise structure carries over directly, with radial distance r replacing z-depth:

  • Crossover radius: r₁ = s/ln(r_d) ≈ s/κ = s·N/(2·tan(θ/2)) (scales linearly with pixel count)
  • Far field (k ≥ 0): r_k = r₁ · r_d^k
  • Near field (k < 0): r_k = r₁ + k·s

Inverse mappings (r → k):

  • Far field (r ≥ r₁): k = floor(ln(r/r₁) / ln(r_d))
  • Near field (r < r₁): k = floor((r - r₁) / s)

Trade-off

The lateral width of froxels decreases toward the edges of the frustum:

lateral_width ∝ r · cos(θ)

where θ is the angle from the view axis. At radial distance r, the z-depth is z = r·cos(θ), so the perspective lateral width κ·z becomes κ·r·cos(θ).

This is a scale factor, not a shape distortion — easily compensated in volume integration. Scale variation is simpler to handle than anisotropic skew.

Coordinate Conversion

Rasterized geometry produces (i, j, z). Convert to spherical depth:

r = z / cos(θ)

where θ is the precomputable angle from view axis for pixel (i, j):

θ = atan(√((i - W/2)² + (j - H/2)²) · κ)

Equivalently, store sec(θ) per pixel and multiply: r = z · sec(θ).

Compatibility

This variant integrates trivially with standard perspective rendering:

  • The (i, j) pixel grid is unchanged
  • Only the depth interpretation changes
  • Rasterizer output converts with one multiply per pixel
  • All existing lateral pixel-based operations work as before

Local Flatness

At practical resolutions, each froxel is locally indistinguishable from a flat-faced truncated pyramid. The spherical shell curvature over one pixel's angular extent is second-order:

sagitta ≈ r · (1 - cos(κ/2)) ≈ r · κ²/8

For κ ≈ 1/1000, this is hundredths of a percent of the froxel size — negligible.

The spherical structure determines where depth boundaries fall globally, but per-froxel operations (interpolation, volume calculation, ray intersection) can use the same flat-face approximations as the planar grid.

Summary

Aspect Planar Frustum Spherical Depth Variant
Depth surfaces Planar (z = const) Spherical (r = const)
Lateral coordinates Perspective (i, j) Perspective (i, j)
Edge froxel shape Elongated along ray Uniform, narrower laterally
Interpolation quality Varies across FOV Uniform across FOV
Rasterizer compatibility Native One multiply per pixel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment