Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs/spark-renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const spark = new SparkRenderer({
| **preUpdate** | Controls whether to update the splats before or after rendering. For WebXR this *must* be false in order to complete rendering as soon as possible. (default: `false`)
| **originDistance** | Distance threshold for `SparkRenderer` movement triggering a splat update at the new origin. (default: `1.0`) This can be useful when your `SparkRenderer` is a child of your camera and you want to retain high precision coordinates near the camera.
| **maxStdDev** | Maximum standard deviations from the center to render Gaussians. Values `Math.sqrt(5)`..`Math.sqrt(9)` produce good results and can be tweaked for performance. (default: `Math.sqrt(8)`)
| **minPixelRadius** | Minimum pixel radius for splat rendering. (default: `0.0`)
| **maxPixelRadius** | Maximum pixel radius for splat rendering. (default: `512.0`)
| **minAlpha** | Minimum alpha value for splat rendering. (default: `0.5 * (1.0 / 255.0)`)
| **enable2DGS** | Enable 2D Gaussian splatting rendering ability. When this mode is enabled, any `scale` x/y/z component that is exactly `0` (minimum quantized value) results in the other two non-zero axes being interpreted as an oriented 2D Gaussian Splat instead of the usual approximate projected 3DGS Z-slice. When reading PLY files, scale values less than e^-30 will be interpreted as `0`. (default: `false`)
Expand Down
1 change: 1 addition & 0 deletions examples/editor/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@
debugFolder.add(spark, "focalAdjustment", 0.1, 2.0, 0.1).name("Tweak focalAdjustment");
spark.defaultView.sort32 = true;
debugFolder.add(spark.defaultView, "sort32").name("Float32 sort").listen();
debugFolder.add(spark, "minPixelRadius", 0, 16, 0.1).name("Min pixel radius").listen();
debugFolder.add(spark, "maxPixelRadius", 1, 1024, 1).name("Max pixel radius").listen();
debugFolder.add(spark, "minAlpha", 0, 1, 0.001).name("Min alpha").listen();

Expand Down
10 changes: 10 additions & 0 deletions src/SparkRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ export type SparkRendererOptions = {
* @default Math.sqrt(8)
*/
maxStdDev?: number;
/**
* Minimum pixel radius for splat rendering.
* @default 0.0
*/
minPixelRadius?: number;
/**
* Maximum pixel radius for splat rendering.
* @default 512.0
Expand Down Expand Up @@ -170,6 +175,7 @@ export class SparkRenderer extends THREE.Mesh {
needsUpdate: boolean;
originDistance: number;
maxStdDev: number;
minPixelRadius: number;
maxPixelRadius: number;
minAlpha: number;
enable2DGS: boolean;
Expand Down Expand Up @@ -294,6 +300,7 @@ export class SparkRenderer extends THREE.Mesh {
this.needsUpdate = false;
this.originDistance = options.originDistance ?? 1;
this.maxStdDev = options.maxStdDev ?? Math.sqrt(8.0);
this.minPixelRadius = options.minPixelRadius ?? 0.0;
this.maxPixelRadius = options.maxPixelRadius ?? 512.0;
this.minAlpha = options.minAlpha ?? 0.5 * (1.0 / 255.0);
this.enable2DGS = options.enable2DGS ?? false;
Expand Down Expand Up @@ -344,6 +351,8 @@ export class SparkRenderer extends THREE.Mesh {
renderToViewPos: { value: new THREE.Vector3() },
// Maximum distance (in stddevs) from Gsplat center to render
maxStdDev: { value: 1.0 },
// Minimum pixel radius for splat rendering
minPixelRadius: { value: 0.0 },
// Maximum pixel radius for splat rendering
maxPixelRadius: { value: 512.0 },
// Minimum alpha value for splat rendering
Expand Down Expand Up @@ -528,6 +537,7 @@ export class SparkRenderer extends THREE.Mesh {
this.uniforms.far.value = typedCamera.far;
this.uniforms.encodeLinear.value = viewpoint.encodeLinear;
this.uniforms.maxStdDev.value = this.maxStdDev;
this.uniforms.minPixelRadius.value = this.minPixelRadius;
this.uniforms.maxPixelRadius.value = this.maxPixelRadius;
this.uniforms.minAlpha.value = this.minAlpha;
this.uniforms.stochastic.value = viewpoint.stochastic;
Expand Down
10 changes: 7 additions & 3 deletions src/shaders/splatVertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ uniform uint numSplats;
uniform vec4 renderToViewQuat;
uniform vec3 renderToViewPos;
uniform float maxStdDev;
uniform float minPixelRadius;
uniform float maxPixelRadius;
uniform float time;
uniform float deltaTime;
Expand Down Expand Up @@ -199,11 +200,14 @@ void main() {
vec2 eigenVec1 = normalize(vec2((abs(b) < 0.001) ? 1.0 : b, eigen1 - a));
vec2 eigenVec2 = vec2(eigenVec1.y, -eigenVec1.x);

float scale1 = position.x * min(maxPixelRadius, maxStdDev * sqrt(eigen1));
float scale2 = position.y * min(maxPixelRadius, maxStdDev * sqrt(eigen2));
float scale1 = min(maxPixelRadius, maxStdDev * sqrt(eigen1));
float scale2 = min(maxPixelRadius, maxStdDev * sqrt(eigen2));
if (scale1 < minPixelRadius && scale2 < minPixelRadius) {
return;
}

// Compute the NDC coordinates for the ellipsoid's diagonal axes.
vec2 pixelOffset = eigenVec1 * scale1 + eigenVec2 * scale2;
vec2 pixelOffset = position.x * eigenVec1 * scale1 + position.y * eigenVec2 * scale2;
vec2 ndcOffset = (2.0 / scaledRenderSize) * pixelOffset;
vec3 ndc = vec3(ndcCenter.xy + ndcOffset, ndcCenter.z);

Expand Down
Loading