Lipschitz voxel#91
Open
snowbldr wants to merge 2 commits into
Open
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix VoxelSDF3 Lipschitz under-correction
Fixes one of the bugs in #85.
The bug
VoxelSDF3caches a source SDF on a grid and trilinear-interpolatesbetween 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 thestretch can be larger.
Unrescaled,
VoxelSDF3.Evaluateoverstates 3D distance, and the octreemarching-cubes renderer's
|sdf(center)| ≥ half-diagonalpruning skipscubes that contain surface, producing holes.
The fix
VoxelSDF3grows aninvStretch float64field measured at constructionfrom the largest adjacent-corner delta along each axis:
Evaluatemultiplies its trilinear-interpolated result byinvStretch,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_voxels—Sphere3D(2)cached at 16³ voxels, octreeat 80 cells.
sphere_fine_voxels— same sphere at 32³ voxels.rounded_box—Box3D({4,3,2}, round=0.3)at 32³ voxels.All assert zero boundary edges.
Box3Dwithround=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
isEmptyrule 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.