mirror of
https://github.com/storytold/spark.git
synced 2026-10-16 00:03:01 +00:00
Custom/extended splat encoding ranges, Premultiplied alpha for hyper-saturated colors (#134)
* Externalized SparkRenderer.maxPixelRadius and .minAlpha settings. Added to documentation. Added to examples/editor under Debug folder, moved sort32 there. * Added SplatEncoding concept, applied individually to SparkRenderer (rgbMin/Max, lnScaleMinMax), SplatMesh (additionally sh1Min/Max, sh2, sh3). Updated examples/editor to control settings via gui. Updated all related code to pipe along settings where needed. Added SparkRenderer.premultipliedAlpha to control blend mode and enable hyper-saturated color contributions by default. * Fix SplatLoader not passing along encoding. Complete renaming to splat encoding.
This commit is contained in:
committed by
GitHub
parent
f4b4522c56
commit
f60c2b0cf9
+58
-2
@@ -1,6 +1,10 @@
|
||||
import * as THREE from "three";
|
||||
|
||||
import { PackedSplats } from "./PackedSplats";
|
||||
import {
|
||||
DEFAULT_SPLAT_ENCODING,
|
||||
PackedSplats,
|
||||
type SplatEncoding,
|
||||
} from "./PackedSplats";
|
||||
import { RgbaArray } from "./RgbaArray";
|
||||
import { SparkViewpoint, type SparkViewpointOptions } from "./SparkViewpoint";
|
||||
import { type GeneratorMapping, SplatAccumulator } from "./SplatAccumulator";
|
||||
@@ -8,6 +12,7 @@ import { SplatEdit } from "./SplatEdit";
|
||||
import { SplatGenerator, SplatModifier } from "./SplatGenerator";
|
||||
import { SplatGeometry } from "./SplatGeometry";
|
||||
import { SplatMesh } from "./SplatMesh";
|
||||
import { LN_SCALE_MAX, LN_SCALE_MIN } from "./defines";
|
||||
import {
|
||||
DynoVec3,
|
||||
DynoVec4,
|
||||
@@ -85,6 +90,11 @@ export type SparkRendererOptions = {
|
||||
* rendering and significantly reduces performance.
|
||||
*/
|
||||
renderer: THREE.WebGLRenderer;
|
||||
/**
|
||||
* Whether to use premultiplied alpha when accumulating splat RGB
|
||||
* @default true
|
||||
*/
|
||||
premultipliedAlpha?: boolean;
|
||||
/**
|
||||
* Pass in a THREE.Clock to synchronize time-based effects across different
|
||||
* systems. Alternatively, you can set the SparkRenderer properties time and
|
||||
@@ -184,15 +194,22 @@ export type SparkRendererOptions = {
|
||||
* radial distance or Z-depth)
|
||||
*/
|
||||
view?: SparkViewpointOptions;
|
||||
/**
|
||||
* Override the default splat encoding ranges for the PackedSplats.
|
||||
* (default: undefined)
|
||||
*/
|
||||
splatEncoding?: SplatEncoding;
|
||||
};
|
||||
|
||||
export class SparkRenderer extends THREE.Mesh {
|
||||
renderer: THREE.WebGLRenderer;
|
||||
premultipliedAlpha: boolean;
|
||||
material: THREE.ShaderMaterial;
|
||||
uniforms: ReturnType<typeof SparkRenderer.makeUniforms>;
|
||||
|
||||
autoUpdate: boolean;
|
||||
preUpdate: boolean;
|
||||
needsUpdate: boolean;
|
||||
originDistance: number;
|
||||
maxStdDev: number;
|
||||
maxPixelRadius: number;
|
||||
@@ -205,6 +222,7 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
falloff: number;
|
||||
clipXY: number;
|
||||
focalAdjustment: number;
|
||||
splatEncoding: SplatEncoding;
|
||||
|
||||
splatTexture: null | {
|
||||
enable?: boolean;
|
||||
@@ -270,13 +288,20 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
constructor(options: SparkRendererOptions) {
|
||||
const uniforms = SparkRenderer.makeUniforms();
|
||||
const shaders = getShaders();
|
||||
const premultipliedAlpha = options.premultipliedAlpha ?? true;
|
||||
const material = new THREE.ShaderMaterial({
|
||||
glslVersion: THREE.GLSL3,
|
||||
vertexShader: shaders.splatVertex,
|
||||
fragmentShader: shaders.splatFragment,
|
||||
uniforms,
|
||||
transparent: true,
|
||||
blending: THREE.NormalBlending,
|
||||
blending: premultipliedAlpha
|
||||
? THREE.CustomBlending
|
||||
: THREE.NormalBlending,
|
||||
blendSrc: premultipliedAlpha ? THREE.OneFactor : THREE.SrcAlphaFactor,
|
||||
blendDst: premultipliedAlpha
|
||||
? THREE.OneMinusSrcAlphaFactor
|
||||
: THREE.OneFactor,
|
||||
depthTest: true,
|
||||
depthWrite: false,
|
||||
side: THREE.DoubleSide,
|
||||
@@ -309,8 +334,10 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
);
|
||||
this.modifier = new SplatModifier(modifier);
|
||||
|
||||
this.premultipliedAlpha = premultipliedAlpha;
|
||||
this.autoUpdate = options.autoUpdate ?? true;
|
||||
this.preUpdate = options.preUpdate ?? false;
|
||||
this.needsUpdate = false;
|
||||
this.originDistance = options.originDistance ?? 1;
|
||||
this.maxStdDev = options.maxStdDev ?? Math.sqrt(8.0);
|
||||
this.maxPixelRadius = options.maxPixelRadius ?? 512.0;
|
||||
@@ -323,6 +350,7 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
this.falloff = options.falloff ?? 1.0;
|
||||
this.clipXY = options.clipXY ?? 1.4;
|
||||
this.focalAdjustment = options.focalAdjustment ?? 1.0;
|
||||
this.splatEncoding = options.splatEncoding ?? { ...DEFAULT_SPLAT_ENCODING };
|
||||
|
||||
this.active = new SplatAccumulator();
|
||||
this.accumulatorCount = 1;
|
||||
@@ -401,10 +429,14 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
splatTexMid: { value: 0.0 },
|
||||
// Gsplat collection to render
|
||||
packedSplats: { type: "t", value: PackedSplats.getEmpty() },
|
||||
// Splat encoding ranges
|
||||
rgbMinMaxLnScaleMinMax: { value: new THREE.Vector4() },
|
||||
// Time in seconds for time-based effects
|
||||
time: { value: 0 },
|
||||
// Delta time in seconds since last frame
|
||||
deltaTime: { value: 0 },
|
||||
// Whether to use premultiplied alpha when accumulating splat RGB
|
||||
premultipliedAlpha: { value: true },
|
||||
// Whether to encode Gsplat with linear RGB (for environment mapping)
|
||||
encodeLinear: { value: false },
|
||||
// Debug flag that alternates each frame
|
||||
@@ -499,6 +531,20 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
|
||||
if (isNewFrame) {
|
||||
// Keep these uniforms the same for both eyes if in WebXR
|
||||
const blending = this.premultipliedAlpha
|
||||
? THREE.CustomBlending
|
||||
: THREE.NormalBlending;
|
||||
if (blending !== this.material.blending) {
|
||||
this.material.blending = blending;
|
||||
this.material.blendSrc = this.premultipliedAlpha
|
||||
? THREE.OneFactor
|
||||
: THREE.SrcAlphaFactor;
|
||||
this.material.blendDst = this.premultipliedAlpha
|
||||
? THREE.OneMinusSrcAlphaFactor
|
||||
: THREE.OneFactor;
|
||||
this.material.needsUpdate = true;
|
||||
}
|
||||
this.uniforms.premultipliedAlpha.value = this.premultipliedAlpha;
|
||||
this.uniforms.time.value = time;
|
||||
this.uniforms.deltaTime.value = deltaTime;
|
||||
// Alternating debug flag that can aid in visual debugging
|
||||
@@ -598,6 +644,12 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
const { accumulator, geometry } = this.viewpoint.display;
|
||||
this.uniforms.numSplats.value = accumulator.splats.numSplats;
|
||||
this.uniforms.packedSplats.value = accumulator.splats.getTexture();
|
||||
this.uniforms.rgbMinMaxLnScaleMinMax.value.set(
|
||||
accumulator.splats.splatEncoding?.rgbMin ?? 0.0,
|
||||
accumulator.splats.splatEncoding?.rgbMax ?? 1.0,
|
||||
accumulator.splats.splatEncoding?.lnScaleMin ?? LN_SCALE_MIN,
|
||||
accumulator.splats.splatEncoding?.lnScaleMax ?? LN_SCALE_MAX,
|
||||
);
|
||||
this.geometry = geometry;
|
||||
} else {
|
||||
// No Gsplats to display for this viewpoint yet
|
||||
@@ -691,6 +743,7 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
const isVisible = object.generator && visibleGenHash.has(object.uuid);
|
||||
const numSplats = isVisible ? object.numSplats : 0;
|
||||
if (
|
||||
this.needsUpdate ||
|
||||
object.generator !== current?.generator ||
|
||||
numSplats !== current?.count
|
||||
) {
|
||||
@@ -708,9 +761,11 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
|
||||
// Check if we need any update at all
|
||||
const needsUpdate =
|
||||
this.needsUpdate ||
|
||||
originUpdate ||
|
||||
generators.length !== activeMapping.size ||
|
||||
generators.some((g) => g.version !== activeMapping.get(g)?.version);
|
||||
this.needsUpdate = false;
|
||||
|
||||
let accumulator: SplatAccumulator | null = null;
|
||||
if (needsUpdate) {
|
||||
@@ -782,6 +837,7 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
|
||||
// Generate the Gsplats according to the mapping that need updating
|
||||
accumulator.ensureGenerate(maxSplats);
|
||||
accumulator.splats.splatEncoding = { ...this.splatEncoding };
|
||||
const generated = accumulator.generateSplats({
|
||||
renderer: this.renderer,
|
||||
modifier: this.modifier,
|
||||
|
||||
Reference in New Issue
Block a user