Skip to content

Lipschitz voxel#91

Open
snowbldr wants to merge 2 commits into
deadsy:masterfrom
snowbldr:lipschitz_voxel
Open

Lipschitz voxel#91
snowbldr wants to merge 2 commits into
deadsy:masterfrom
snowbldr:lipschitz_voxel

Conversation

@snowbldr
Copy link
Copy Markdown
Contributor

Fix VoxelSDF3 Lipschitz under-correction

Fixes one of the bugs in #85.

The bug

VoxelSDF3 caches a source SDF on a grid and trilinear-interpolates
between corner samples. Trilinear interpolation can stretch the gradient
by up to √3 even when the source is Lipschitz-1 — each of the three
axis-aligned partial derivatives can independently approach 1, and the
gradient magnitude is √(∂x² + ∂y² + ∂z²). For a non-SDF source the
stretch can be larger.

Unrescaled, VoxelSDF3.Evaluate overstates 3D distance, and the octree
marching-cubes renderer's |sdf(center)| ≥ half-diagonal pruning skips
cubes that contain surface, producing holes.

The fix

VoxelSDF3 grows an invStretch float64 field measured at construction
from the largest adjacent-corner delta along each axis:

Lᵢ = max|c[idx + ê_i] − c[idx]| / voxelSize.i      for i ∈ {x, y, z}
σ² = Lx² + Ly² + Lz²
invStretch = 1 /σ²       (clamped to1)

Evaluate multiplies its trilinear-interpolated result by invStretch,
giving a Lipschitz-1 distance estimator the renderer can prune against.

This is tight: the trilinear interpolant's gradient is exactly
bounded by the L² norm of the per-axis Lipschitz constants, and we
measure those directly from the grid rather than assuming any property
of the source.

Tests

render/voxel_test.go:

  • sphere_coarse_voxelsSphere3D(2) cached at 16³ voxels, octree
    at 80 cells.
  • sphere_fine_voxels — same sphere at 32³ voxels.
  • rounded_boxBox3D({4,3,2}, round=0.3) at 32³ voxels.

All assert zero boundary edges. Box3D with round=0 (sharp corners)
trips a separate edge-extrapolation issue at the bbox boundary that's
out of scope here, so the test uses a rounded box instead.

Architecture-specific note

Same family of bug as the high-taper screw rendering issue from #84
non-1-Lipschitz SDF that the octree's isEmpty rule wrongly trusts.
Borderline configurations may render holes on x86_64 (FMA / FTZ
rounding differences) but not on Apple Silicon. The conservative
1/√σ² constant has plenty of slack either way.

snowbldr added 2 commits May 8, 2026 20:06
Trilinear interpolation of grid samples can stretch the gradient by up
to √3 even when the source SDF is Lipschitz-1 — each axis contributes
its own partial derivative independently. Without correction, VoxelSDF3
overstates 3D distance and the octree marching-cubes renderer's
|sdf(center)| ≥ half-diagonal pruning skips cubes that contain
surface, producing holes.

VoxelSDF3 grows an invStretch field measured at construction from the
maximum adjacent-corner deltas along each grid axis:

    Lᵢ = max|c[idx+ê_i] − c[idx]| / voxelSize.i
    σ² = Lx² + Ly² + Lz²
    invStretch = 1/√σ²    (clamped to ≤ 1)

This is tight: it captures the true Lipschitz of the trilinear
interpolant (which is at most the L² norm of the per-axis Lipschitz
constants), accounting for non-SDF sources where Lᵢ may exceed 1.

render/voxel_test.go: watertight tests on Sphere3D at coarse (16
voxels) and fine (32 voxels) cache resolution, plus a rounded box at
32 voxels. All render via the octree at 80 cells with zero boundary
edges.
11 source × voxel-cache combinations:
  - sphere at voxel cells {8, 16, 32, 48} — smooth gradient
  - rounded box at {16, 32} — face/edge transitions
  - cylinder at {16, 32} — mixed flat / curved
  - capsule — convex smooth
  - sphere-minus-sphere shell at {24, 32} — non-convex two-surface

Plus a separate test sweeping render resolution (40, 60, 80, 100,
120, 150) for a fixed source × voxel grid so octree cube centers
land at varied positions within the voxel cells.

All assert zero boundary edges from the octree.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant