mirror of
https://github.com/storytold/spark.git
synced 2026-10-24 08:36:08 +00:00
Add modifiers normalColor and depthColor. Add examples/debug-color to show how to use them on SplatMeshes. (#58)
This commit is contained in:
committed by
GitHub
parent
a83c99eb86
commit
690e09ac95
@@ -372,6 +372,7 @@
|
|||||||
<a href="/examples/particle-simulation/index.html" class="example-link">Particle Simulation</a>
|
<a href="/examples/particle-simulation/index.html" class="example-link">Particle Simulation</a>
|
||||||
<a href="/examples/webxr/index.html" class="example-link">WebXR</a>
|
<a href="/examples/webxr/index.html" class="example-link">WebXR</a>
|
||||||
<a href="/examples/glsl/index.html" class="example-link">GLSL Shaders</a>
|
<a href="/examples/glsl/index.html" class="example-link">GLSL Shaders</a>
|
||||||
|
<a href="/examples/debug-color/index.html" class="example-link">Debug Coloring</a>
|
||||||
<a href="/examples/editor/index.html" class="example-link">Editor</a>
|
<a href="/examples/editor/index.html" class="example-link">Editor</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Spark • Debug Coloring</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"three": "/examples/js/vendor/three/build/three.module.js",
|
||||||
|
"@sparkjsdev/spark": "/dist/spark.module.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { SplatMesh, PackedSplats, dyno, modifiers } from "@sparkjsdev/spark";
|
||||||
|
import { getAssetFileURL } from "/examples/js/get-asset-url.js";
|
||||||
|
|
||||||
|
const scene = new THREE.Scene();
|
||||||
|
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||||
|
const renderer = new THREE.WebGLRenderer();
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
document.body.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
let splatURL = await getAssetFileURL("butterfly.spz");
|
||||||
|
const butterfly = new SplatMesh({ url: splatURL });
|
||||||
|
butterfly.scale.setScalar(0.5);
|
||||||
|
butterfly.quaternion.set(1, 0, 0, 0);
|
||||||
|
butterfly.position.set(-0.5, 0, -1.5);
|
||||||
|
modifiers.setWorldNormalColor(butterfly);
|
||||||
|
scene.add(butterfly);
|
||||||
|
|
||||||
|
splatURL = await getAssetFileURL("butterfly-ai.spz");
|
||||||
|
const butterfly2 = new SplatMesh({ url: splatURL });
|
||||||
|
butterfly2.quaternion.set(1, 0, 0, 0);
|
||||||
|
butterfly2.position.set(0.5, 0, -1.5);
|
||||||
|
modifiers.setDepthColor(butterfly2, 1, 2, true);
|
||||||
|
scene.add(butterfly2);
|
||||||
|
|
||||||
|
renderer.setAnimationLoop(function animate(time) {
|
||||||
|
butterfly.rotation.y += 0.01;
|
||||||
|
butterfly2.rotation.x += 0.01;
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -144,6 +144,7 @@
|
|||||||
<li><a href="/examples/particle-simulation/">Particle Simulation</a></li>
|
<li><a href="/examples/particle-simulation/">Particle Simulation</a></li>
|
||||||
<li><a href="/examples/webxr/">WebXR</a></li>
|
<li><a href="/examples/webxr/">WebXR</a></li>
|
||||||
<li><a href="/examples/glsl/">GLSL Shaders</a></li>
|
<li><a href="/examples/glsl/">GLSL Shaders</a></li>
|
||||||
|
<li><a href="/examples/debug-color/">Debug Coloring</a></li>
|
||||||
<li><a href="/examples/editor/">Editor</a></li>
|
<li><a href="/examples/editor/">Editor</a></li>
|
||||||
<li><a href="/examples/viewer/">Viewer</a></li>
|
<li><a href="/examples/viewer/">Viewer</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -70,6 +70,13 @@ export const hashVec4 = <T extends ValueTypes>(
|
|||||||
): DynoVal<"vec4"> => {
|
): DynoVal<"vec4"> => {
|
||||||
return new HashVec4({ value });
|
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
|
export class DynoRemapIndex
|
||||||
extends Dyno<{ from: "int"; to: "int"; index: "int" }, { index: "int" }>
|
extends Dyno<{ from: "int"; to: "int"; index: "int" }, { index: "int" }>
|
||||||
@@ -404,3 +411,31 @@ export class HashVec4<T extends ValueTypes>
|
|||||||
return new DynoOutput(this, "hash");
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export {
|
|||||||
} from "./splatConstructors";
|
} from "./splatConstructors";
|
||||||
|
|
||||||
export * as generators from "./generators";
|
export * as generators from "./generators";
|
||||||
|
export * as modifiers from "./modifiers";
|
||||||
|
|
||||||
export { VRButton } from "./vrButton";
|
export { VRButton } from "./vrButton";
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./modifiers/normalColor";
|
||||||
|
export * from "./modifiers/depthColor";
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user