From 690e09ac9566d173956111c2f3dd6e7a6520d8d1 Mon Sep 17 00:00:00 2001 From: Andreas Sundquist Date: Fri, 20 Jun 2025 14:23:45 -0700 Subject: [PATCH] Add modifiers normalColor and depthColor. Add examples/debug-color to show how to use them on SplatMeshes. (#58) --- examples.html | 1 + examples/debug-color/index.html | 61 +++++++++++++++++++++++++++++++++ index.html | 1 + src/dyno/util.ts | 35 +++++++++++++++++++ src/index.ts | 1 + src/modifiers.ts | 2 ++ src/modifiers/depthColor.ts | 60 ++++++++++++++++++++++++++++++++ src/modifiers/normalColor.ts | 46 +++++++++++++++++++++++++ 8 files changed, 207 insertions(+) create mode 100644 examples/debug-color/index.html create mode 100644 src/modifiers.ts create mode 100644 src/modifiers/depthColor.ts create mode 100644 src/modifiers/normalColor.ts diff --git a/examples.html b/examples.html index 4896327..db6b64a 100644 --- a/examples.html +++ b/examples.html @@ -372,6 +372,7 @@ Particle Simulation WebXR GLSL Shaders + Debug Coloring Editor
diff --git a/examples/debug-color/index.html b/examples/debug-color/index.html new file mode 100644 index 0000000..dd6ffe6 --- /dev/null +++ b/examples/debug-color/index.html @@ -0,0 +1,61 @@ + + + + + + + Spark • Debug Coloring + + + + + + + + + diff --git a/index.html b/index.html index d294b91..908f5ca 100644 --- a/index.html +++ b/index.html @@ -144,6 +144,7 @@
  • Particle Simulation
  • WebXR
  • GLSL Shaders
  • +
  • Debug Coloring
  • Editor
  • Viewer
  • diff --git a/src/dyno/util.ts b/src/dyno/util.ts index 85a3ba5..df1c778 100644 --- a/src/dyno/util.ts +++ b/src/dyno/util.ts @@ -70,6 +70,13 @@ export const hashVec4 = ( ): DynoVal<"vec4"> => { return new HashVec4({ value }); }; +export const normalizedDepth = ( + z: DynoVal<"float">, + zNear: DynoVal<"float">, + zFar: DynoVal<"float">, +): DynoVal<"float"> => { + return new NormalizedDepth({ z, zNear, zFar }).outputs.depth; +}; export class DynoRemapIndex extends Dyno<{ from: "int"; to: "int"; index: "int" }, { index: "int" }> @@ -404,3 +411,31 @@ export class HashVec4 return new DynoOutput(this, "hash"); } } + +export class NormalizedDepth + extends Dyno< + { z: "float"; zNear: "float"; zFar: "float" }, + { depth: "float" } + > + implements HasDynoOut<"float"> +{ + constructor({ + z, + zNear, + zFar, + }: { z: DynoVal<"float">; zNear: DynoVal<"float">; zFar: DynoVal<"float"> }) { + super({ + inTypes: { z: "float", zNear: "float", zFar: "float" }, + outTypes: { depth: "float" }, + inputs: { z, zNear, zFar }, + statements: ({ inputs, outputs }) => [ + `float clamped = clamp(${inputs.z}, ${inputs.zNear}, ${inputs.zFar});`, + `${outputs.depth} = (log2(clamped + 1.0) - log2(${inputs.zNear} + 1.0)) / (log2(${inputs.zFar} + 1.0) - log2(${inputs.zNear} + 1.0));`, + ], + }); + } + + dynoOut(): DynoValue<"float"> { + return new DynoOutput(this, "depth"); + } +} diff --git a/src/index.ts b/src/index.ts index d82d370..9a3f614 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,6 +48,7 @@ export { } from "./splatConstructors"; export * as generators from "./generators"; +export * as modifiers from "./modifiers"; export { VRButton } from "./vrButton"; export { diff --git a/src/modifiers.ts b/src/modifiers.ts new file mode 100644 index 0000000..cb325e3 --- /dev/null +++ b/src/modifiers.ts @@ -0,0 +1,2 @@ +export * from "./modifiers/normalColor"; +export * from "./modifiers/depthColor"; diff --git a/src/modifiers/depthColor.ts b/src/modifiers/depthColor.ts new file mode 100644 index 0000000..9d788dd --- /dev/null +++ b/src/modifiers/depthColor.ts @@ -0,0 +1,60 @@ +import type { SplatTransformer } from "../SplatGenerator"; +import type { SplatMesh } from "../SplatMesh"; +import { + type DynoVal, + Gsplat, + combineGsplat, + dynoBlock, + dynoConst, + neg, + normalizedDepth, + select, + split, + splitGsplat, + sub, +} from "../dyno"; + +export function makeDepthColorModifier( + splatToView: SplatTransformer, + minDepth: DynoVal<"float">, + maxDepth: DynoVal<"float">, + reverse: DynoVal<"bool">, +) { + return dynoBlock({ gsplat: Gsplat }, { gsplat: Gsplat }, ({ gsplat }) => { + if (!gsplat) { + throw new Error("No gsplat input"); + } + let { center } = splitGsplat(gsplat).outputs; + center = splatToView.apply(center); + const { z } = split(center).outputs; + let depth = normalizedDepth(neg(z), minDepth, maxDepth); + depth = select(reverse, sub(dynoConst("float", 1), depth), depth); + + gsplat = combineGsplat({ gsplat, r: depth, g: depth, b: depth }); + return { gsplat }; + }); +} + +export function setDepthColor( + splats: SplatMesh, + minDepth: number, + maxDepth: number, + reverse?: boolean, +) { + splats.enableWorldToView = true; + const dynoMinDepth = dynoConst("float", minDepth); + const dynoMaxDepth = dynoConst("float", maxDepth); + const dynoReverse = dynoConst("bool", reverse ?? false); + splats.worldModifier = makeDepthColorModifier( + splats.context.worldToView, + dynoMinDepth, + dynoMaxDepth, + dynoReverse, + ); + splats.updateGenerator(); + return { + minDepth: dynoMinDepth, + maxDepth: dynoMaxDepth, + reverse: dynoReverse, + }; +} diff --git a/src/modifiers/normalColor.ts b/src/modifiers/normalColor.ts new file mode 100644 index 0000000..ac09541 --- /dev/null +++ b/src/modifiers/normalColor.ts @@ -0,0 +1,46 @@ +import type { SplatTransformer } from "../SplatGenerator"; +import type { SplatMesh } from "../SplatMesh"; +import { + Gsplat, + add, + combineGsplat, + dot, + dynoBlock, + dynoConst, + greaterThanEqual, + gsplatNormal, + mul, + neg, + select, + splitGsplat, +} from "../dyno"; + +export function makeNormalColorModifier(splatToView: SplatTransformer) { + return dynoBlock({ gsplat: Gsplat }, { gsplat: Gsplat }, ({ gsplat }) => { + if (!gsplat) { + throw new Error("No gsplat input"); + } + let normal = gsplatNormal(gsplat); + + const viewGsplat = splatToView.applyGsplat(gsplat); + const viewCenter = splitGsplat(viewGsplat).outputs.center; + const viewNormal = gsplatNormal(viewGsplat); + const splatDot = dot(viewCenter, viewNormal); + + const sameDir = greaterThanEqual(splatDot, dynoConst("float", 0)); + normal = select(sameDir, neg(normal), normal); + const rgb = add( + mul(normal, dynoConst("float", 0.5)), + dynoConst("float", 0.5), + ); + + gsplat = combineGsplat({ gsplat, rgb }); + return { gsplat }; + }); +} + +export function setWorldNormalColor(splats: SplatMesh) { + splats.enableWorldToView = true; + splats.worldModifier = makeNormalColorModifier(splats.context.worldToView); + splats.updateGenerator(); +}