mirror of
https://github.com/storytold/spark.git
synced 2026-10-13 23:52:44 +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/webxr/index.html" class="example-link">WebXR</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>
|
||||
</div>
|
||||
<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/webxr/">WebXR</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/viewer/">Viewer</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -70,6 +70,13 @@ export const hashVec4 = <T extends ValueTypes>(
|
||||
): 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<T extends ValueTypes>
|
||||
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";
|
||||
|
||||
export * as generators from "./generators";
|
||||
export * as modifiers from "./modifiers";
|
||||
|
||||
export { VRButton } from "./vrButton";
|
||||
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