-
Notifications
You must be signed in to change notification settings - Fork 124
Description
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.xyzAnother 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.