Skip to content

Precise clipping by the camera's near/far planes #226

@soundform

Description

@soundform

Spark already trims splats whose centers are outside the camera's frustum. I'd suggest to make a small, but valuable improvement: detect splats that cross the near/far planes and render only the portion of the splat that's inside the frustum. That will involve some math with the error function, which has a simple approximation:

  // integrate( exp(-x*x) * 2.0/SQRT_PI, 0..x ) = -1..1
  // https://en.wikipedia.org/wiki/Error_function
  float erf(float x) {
    if (abs(x) > 3.5) return sign(x); // optional
    return sign(x)*sqrt(1. - exp2(-SQRT_PI*x*x));
  }

With this change it's possible to compute shadows, add extra light sources and so on. For example, to do a raymarching-style render one can render the scene in thin (near, far=near+eps) slices and blend them together:

for (let z = near; z < far; z += eps) {
  camera.near = z;
  camera.far = z + eps;
  await spark.prepare(); // sort splats that are between (z, z+eps)
  await spark.render(); // these layers will be accumulated in the render target
}

This will make a difference when too many splats overlap and cause z-order fighting.

Shadows can be computed in a similar manner, but from the perspective of the light source's frustum. For example, to properly illuminate splats with an extra light source, one can set the camera where the new light source is, render 100-1000 slices of the scene, and use the alpha value of the nearest slice as the amount of light reaching the splat:

let shadowMap = new THREE.WebGLArrayRenderTarget(..., ..., numLayers);
let camera = new THREE.PerspectiveCamera(...);
camera.position.set(lightSource.position);

for (let i = 0; i < numLayers; i++) {
  camera.near = i*eps;
  camera.far = (i+1)*eps;
  renderer.setRenderTarget(shadowMap, i);
  await spark.prepare();
  await spark.render(); // only `color.a` component is needed
}

splatMesh.objectModifier = ... // use shadowMap to find the amount of light reaching the splat.xyz

Another possibility is to create 3d voxel maps with color densities: use the orthographic camera to render the splats in very thin slices, save volumetric densities as 8 bit rgba, then compress. This might be useful for converting splats to https://code.blender.org/2025/10/volume-grids-in-geometry-nodes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions