mirror of
https://github.com/storytold/spark.git
synced 2026-10-14 23:52:59 +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
+47
-6
@@ -1,7 +1,11 @@
|
||||
import * as THREE from "three";
|
||||
|
||||
import init_wasm, { raycast_splats } from "spark-internal-rs";
|
||||
import { PackedSplats } from "./PackedSplats";
|
||||
import {
|
||||
DEFAULT_SPLAT_ENCODING,
|
||||
PackedSplats,
|
||||
type SplatEncoding,
|
||||
} from "./PackedSplats";
|
||||
import { type RgbaArray, readRgbaArray } from "./RgbaArray";
|
||||
import { SplatEdit, SplatEditSdf, SplatEdits } from "./SplatEdit";
|
||||
import {
|
||||
@@ -11,6 +15,7 @@ import {
|
||||
} from "./SplatGenerator";
|
||||
import type { SplatFileType } from "./SplatLoader";
|
||||
import type { SplatSkinning } from "./SplatSkinning";
|
||||
import { LN_SCALE_MAX, LN_SCALE_MIN } from "./defines";
|
||||
import {
|
||||
DynoFloat,
|
||||
DynoUsampler2DArray,
|
||||
@@ -27,6 +32,7 @@ import {
|
||||
mul,
|
||||
normalize,
|
||||
readPackedSplat,
|
||||
split,
|
||||
splitGsplat,
|
||||
sub,
|
||||
unindent,
|
||||
@@ -80,6 +86,9 @@ export type SplatMeshOptions = {
|
||||
// Gsplat modifier to apply in world-space after transformations.
|
||||
// (default: undefined)
|
||||
worldModifier?: GsplatModifier;
|
||||
// Override the default splat encoding ranges for the PackedSplats.
|
||||
// (default: undefined)
|
||||
splatEncoding?: SplatEncoding;
|
||||
};
|
||||
|
||||
export type SplatMeshContext = {
|
||||
@@ -183,6 +192,9 @@ export class SplatMesh extends SplatGenerator {
|
||||
});
|
||||
|
||||
this.packedSplats = options.packedSplats ?? new PackedSplats();
|
||||
this.packedSplats.splatEncoding = options.splatEncoding ?? {
|
||||
...DEFAULT_SPLAT_ENCODING,
|
||||
};
|
||||
this.numSplats = this.packedSplats.numSplats;
|
||||
this.editable = options.editable ?? true;
|
||||
this.onFrame = options.onFrame;
|
||||
@@ -226,8 +238,15 @@ export class SplatMesh extends SplatGenerator {
|
||||
}
|
||||
|
||||
async asyncInitialize(options: SplatMeshOptions) {
|
||||
const { url, fileBytes, fileType, fileName, maxSplats, constructSplats } =
|
||||
options;
|
||||
const {
|
||||
url,
|
||||
fileBytes,
|
||||
fileType,
|
||||
fileName,
|
||||
maxSplats,
|
||||
constructSplats,
|
||||
splatEncoding,
|
||||
} = options;
|
||||
if (url || fileBytes || constructSplats) {
|
||||
const packedSplatsOptions = {
|
||||
url,
|
||||
@@ -236,6 +255,7 @@ export class SplatMesh extends SplatGenerator {
|
||||
fileName,
|
||||
maxSplats,
|
||||
construct: constructSplats,
|
||||
splatEncoding,
|
||||
};
|
||||
this.packedSplats.reinitialize(packedSplatsOptions);
|
||||
}
|
||||
@@ -321,13 +341,32 @@ export class SplatMesh extends SplatGenerator {
|
||||
const { center } = splitGsplat(gsplat).outputs;
|
||||
const viewDir = normalize(sub(center, viewCenterInObject));
|
||||
|
||||
function rescaleSh(
|
||||
sNorm: DynoVal<"vec3">,
|
||||
minMax: DynoVal<"vec2">,
|
||||
) {
|
||||
const { x: min, y: max } = split(minMax).outputs;
|
||||
const mid = mul(add(min, max), dynoConst("float", 0.5));
|
||||
const scale = mul(sub(max, min), dynoConst("float", 0.5));
|
||||
return add(mid, mul(sNorm, scale));
|
||||
}
|
||||
|
||||
// Evaluate Spherical Harmonics
|
||||
let rgb = evaluateSH1(gsplat, sh1Texture, viewDir);
|
||||
const sh1Snorm = evaluateSH1(gsplat, sh1Texture, viewDir);
|
||||
let rgb = rescaleSh(sh1Snorm, this.packedSplats.dynoSh1MinMax);
|
||||
if (this.maxSh >= 2 && sh2Texture) {
|
||||
rgb = add(rgb, evaluateSH2(gsplat, sh2Texture, viewDir));
|
||||
const sh2Snorm = evaluateSH2(gsplat, sh2Texture, viewDir);
|
||||
rgb = add(
|
||||
rgb,
|
||||
rescaleSh(sh2Snorm, this.packedSplats.dynoSh2MinMax),
|
||||
);
|
||||
}
|
||||
if (this.maxSh >= 3 && sh3Texture) {
|
||||
rgb = add(rgb, evaluateSH3(gsplat, sh3Texture, viewDir));
|
||||
const sh3Snorm = evaluateSH3(gsplat, sh3Texture, viewDir);
|
||||
rgb = add(
|
||||
rgb,
|
||||
rescaleSh(sh3Snorm, this.packedSplats.dynoSh3MinMax),
|
||||
);
|
||||
}
|
||||
|
||||
// Flash off for 0.3 / 1.0 sec for debugging
|
||||
@@ -539,6 +578,8 @@ export class SplatMesh extends SplatGenerator {
|
||||
this.packedSplats.numSplats,
|
||||
this.packedSplats.packedArray,
|
||||
RAYCAST_ELLIPSOID,
|
||||
this.packedSplats.splatEncoding?.lnScaleMin ?? LN_SCALE_MIN,
|
||||
this.packedSplats.splatEncoding?.lnScaleMax ?? LN_SCALE_MAX,
|
||||
);
|
||||
|
||||
for (const distance of distances) {
|
||||
|
||||
Reference in New Issue
Block a user