diff --git a/examples/editor/index.html b/examples/editor/index.html index 4963eb0..e18cbbb 100644 --- a/examples/editor/index.html +++ b/examples/editor/index.html @@ -524,6 +524,10 @@ debugFolder.add(spark.defaultView, "sort32").name("Float32 sort").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(); + spark.defaultView.stochastic = true; + debugFolder.add(spark.defaultView, "stochastic").name("Stochastic sort-free").listen().onChange(() => { + spark.prepareViewpoint(); + }); debugFolder.add(spark, "premultipliedAlpha").name("Premultiplied alpha").listen(); const accumFolder = debugFolder.addFolder("Accumulator encoding").close();; diff --git a/src/SparkRenderer.ts b/src/SparkRenderer.ts index 2f185ae..846dd9e 100644 --- a/src/SparkRenderer.ts +++ b/src/SparkRenderer.ts @@ -390,6 +390,8 @@ export class SparkRenderer extends THREE.Mesh { maxPixelRadius: { value: 512.0 }, // Minimum alpha value for splat rendering minAlpha: { value: 0.5 * (1.0 / 255.0) }, + // Enable stochastic splat rendering + stochastic: { value: false }, // Enable interpreting 0-thickness Gsplats as 2DGS enable2DGS: { value: false }, // Add to projected 2D splat covariance diagonal (thickens and brightens) @@ -565,6 +567,7 @@ export class SparkRenderer extends THREE.Mesh { this.uniforms.maxStdDev.value = this.maxStdDev; this.uniforms.maxPixelRadius.value = this.maxPixelRadius; this.uniforms.minAlpha.value = this.minAlpha; + this.uniforms.stochastic.value = viewpoint.stochastic; this.uniforms.enable2DGS.value = this.enable2DGS; this.uniforms.preBlurAmount.value = this.preBlurAmount; this.uniforms.blurAmount.value = this.blurAmount; @@ -633,6 +636,8 @@ export class SparkRenderer extends THREE.Mesh { accumulator.splats.splatEncoding?.lnScaleMax ?? LN_SCALE_MAX, ); this.geometry = geometry; + this.material.transparent = !this.viewpoint.stochastic; + this.material.depthWrite = this.viewpoint.stochastic; } else { // No Gsplats to display for this viewpoint yet this.uniforms.numSplats.value = 0; diff --git a/src/SparkViewpoint.ts b/src/SparkViewpoint.ts index 946b77b..6f7d3ad 100644 --- a/src/SparkViewpoint.ts +++ b/src/SparkViewpoint.ts @@ -123,6 +123,11 @@ export type SparkViewpointOptions = { * @default true */ sort32?: boolean; + /* + * Set this to true to enable sort-free stochastic splat rendering. + * @default false + */ + stochastic?: boolean; }; // A SparkViewpoint is created from and tied to a SparkRenderer, and represents @@ -156,6 +161,7 @@ export class SparkViewpoint { depthBias?: number; sort360?: boolean; sort32?: boolean; + stochastic: boolean; display: { accumulator: SplatAccumulator; @@ -218,6 +224,7 @@ export class SparkViewpoint { this.depthBias = options.depthBias; this.sort360 = options.sort360; this.sort32 = options.sort32; + this.stochastic = options.stochastic ?? false; this.orderingFreelist = new FreeList({ allocate: (maxSplats) => new Uint32Array(maxSplats), diff --git a/src/shaders/splatFragment.glsl b/src/shaders/splatFragment.glsl index 07f3b98..132f680 100644 --- a/src/shaders/splatFragment.glsl +++ b/src/shaders/splatFragment.glsl @@ -7,8 +7,11 @@ precision highp int; uniform float near; uniform float far; uniform bool encodeLinear; +uniform float time; +uniform bool debugFlag; uniform float maxStdDev; uniform float minAlpha; +uniform bool stochastic; uniform bool disableFalloff; uniform float falloff; @@ -67,10 +70,28 @@ void main() { if (encodeLinear) { rgba.rgb = srgbToLinear(rgba.rgb); } - - #ifdef PREMULTIPLIED_ALPHA - fragColor = vec4(rgba.rgb * rgba.a, rgba.a); - #else - fragColor = rgba; - #endif + + if (stochastic) { + uint uTime = floatBitsToUint(time); + uvec2 coord = uvec2(gl_FragCoord.xy); + uint state = uTime + 0x9e3779b9u * coord.x + 0x85ebca6bu * coord.y; + state = state * 747796405u + 2891336453u; + uint hash = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + hash = (hash >> 22u) ^ hash; + float rand = float(hash) / 4294967295.0; + // rand *= 2.0; + if (rand < rgba.a) { + fragColor = vec4(rgba.rgb, 1.0); + // gl_FragDepth = gl_FragCoord.z; + // fragColor = vec4(rgba.r, 0.0, 0.0, 1.0); + } else { + discard; + } + } else { + #ifdef PREMULTIPLIED_ALPHA + fragColor = vec4(rgba.rgb * rgba.a, rgba.a); + #else + fragColor = rgba; + #endif + } }