Skip to content

Commit df1f998

Browse files
committed
Restructured Splat setup aimed at singular Splat object
1 parent 382ad19 commit df1f998

File tree

96 files changed

+6197
-19658
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+6197
-19658
lines changed

examples/debug-color/index.html

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
</script>
2727
<script type="module">
2828
import * as THREE from "three";
29-
import { SplatMesh, PackedSplats, dyno, modifiers } from "@sparkjsdev/spark";
29+
import { SplatLoader } from "@sparkjsdev/spark";
3030
import { getAssetFileURL } from "/examples/js/get-asset-url.js";
3131

3232
const scene = new THREE.Scene();
@@ -42,19 +42,58 @@
4242
renderer.setSize(window.innerWidth, window.innerHeight);
4343
}
4444

45+
const loader = new SplatLoader();
46+
4547
let splatURL = await getAssetFileURL("butterfly.spz");
46-
const butterfly = new SplatMesh({ url: splatURL });
48+
const butterfly = await loader.loadAsync(splatURL);
4749
butterfly.scale.setScalar(0.5);
4850
butterfly.quaternion.set(1, 0, 0, 0);
4951
butterfly.position.set(-0.5, 0, -1.5);
50-
modifiers.setWorldNormalColor(butterfly);
52+
butterfly.setShaderHooks({
53+
vertex: {
54+
global: /*glsl*/`
55+
vec3 gsplatNormal(vec3 scales, vec4 quaternion) {
56+
float minScale = min(scales.x, min(scales.y, scales.z));
57+
vec3 normal;
58+
if (scales.z == minScale) {
59+
normal = vec3(0.0, 0.0, 1.0);
60+
} else if (scales.y == minScale) {
61+
normal = vec3(0.0, 1.0, 0.0);
62+
} else {
63+
normal = vec3(1.0, 0.0, 0.0);
64+
}
65+
return quatVec(quaternion, normal);
66+
}
67+
68+
vec3 objectNormal;
69+
`,
70+
objectModifier: /*glsl*/`
71+
// Determine normal in object space
72+
objectNormal = gsplatNormal(scales, quaternion);
73+
`,
74+
splatColor: /*glsl*/`
75+
// Transform normal into world space
76+
vec3 worldNormal = normalize(mat3(modelMatrix) * objectNormal);
77+
// TODO: Handle flipped normals
78+
rgba.rgb = 0.5 + objectNormal * 0.5;
79+
`
80+
}
81+
})
5182
scene.add(butterfly);
5283

5384
splatURL = await getAssetFileURL("butterfly-ai.spz");
54-
const butterfly2 = new SplatMesh({ url: splatURL });
85+
const butterfly2 = await loader.loadAsync(splatURL);
5586
butterfly2.quaternion.set(1, 0, 0, 0);
5687
butterfly2.position.set(0.5, 0, -1.5);
57-
modifiers.setDepthColor(butterfly2, 1, 2, true);
88+
butterfly2.setShaderHooks({
89+
vertex: {
90+
splatColor: /*glsl*/`
91+
float clamped = clamp(-viewCenter.z, 1.0, 2.0);
92+
float depth = 1.0 - (log2(clamped + 1.0) - log2(1.0 + 1.0)) / (log2(2.0 + 1.0) - log2(1.0 + 1.0));
93+
rgba.rgb = vec3(depth);
94+
`
95+
}
96+
})
5897
scene.add(butterfly2);
5998

6099
let lastTime;

examples/depth-of-field/index.html

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@
2727
</script>
2828
<script type="module">
2929
import * as THREE from "three";
30-
import { SparkRenderer, SplatMesh, SparkControls } from "@sparkjsdev/spark";
30+
import { SplatLoader } from "@sparkjsdev/spark";
3131
import GUI from "lil-gui";
3232
import { getAssetFileURL } from "/examples/js/get-asset-url.js";
33+
import { SparkControls } from "/examples/js/controls.js";
3334

3435
const scene = new THREE.Scene();
3536
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
@@ -44,37 +45,62 @@
4445
renderer.setSize(window.innerWidth, window.innerHeight);
4546
}
4647

47-
const spark = new SparkRenderer({
48-
renderer,
49-
apertureAngle: 0.02,
50-
focalDistance: 5.0,
48+
const splatLoader = new SplatLoader();
49+
const splatURL = await getAssetFileURL("valley.spz");
50+
const background = await splatLoader.loadAsync(splatURL);
51+
background.quaternion.set(1, 0, 0, 0);
52+
background.scale.setScalar(0.5);
53+
scene.add(background);
54+
55+
const depthOfFieldUniforms = {
56+
// Depth-of-field distance to focal plane
57+
focalDistance: { type: 'float', value: 0.0 },
58+
// Full-width angle of aperture opening (in radians)
59+
apertureAngle: { type: 'float', value: 0.0 },
60+
};
61+
background.setShaderHooks({
62+
vertex: {
63+
uniforms: depthOfFieldUniforms
64+
},
65+
onBeforeCompile: (program, renderer) => {
66+
program.vertexShader = program.vertexShader.replace(/a \+= blurAmount;\n\s+d \+= blurAmount;/,
67+
/*glsl*/`
68+
float fullBlurAmount = blurAmount;
69+
if ((focalDistance > 0.0) && (apertureAngle > 0.0)) {
70+
float focusRadius = maxPixelRadius;
71+
if (viewCenter.z < 0.0) {
72+
float focusBlur = abs((-viewCenter.z - focalDistance) / viewCenter.z);
73+
float apertureRadius = focal.x * tan(0.5 * apertureAngle);
74+
focusRadius = focusBlur * apertureRadius;
75+
}
76+
fullBlurAmount = clamp(sqr(focusRadius), blurAmount, sqr(maxPixelRadius));
77+
}
78+
a += fullBlurAmount;
79+
d += fullBlurAmount;
80+
`);
81+
}
5182
});
52-
scene.add(spark);
5383

54-
const apertureSize = {
84+
const settings = {
85+
focalDistance: 5.0,
5586
apertureSize: 0.1,
5687
};
5788
function updateApertureAngle() {
58-
if (spark.focalDistance > 0) {
59-
spark.apertureAngle = 2 * Math.atan(0.5 * apertureSize.apertureSize / spark.focalDistance);
89+
depthOfFieldUniforms.focalDistance.value = settings.focalDistance;
90+
if (settings.focalDistance > 0) {
91+
depthOfFieldUniforms.apertureAngle.value = 2 * Math.atan(0.5 * settings.apertureSize / settings.focalDistance);
6092
} else {
61-
spark.apertureAngle = 0.0;
93+
depthOfFieldUniforms.apertureAngle.value = 0.0;
6294
}
6395
}
6496
updateApertureAngle();
6597

6698
const gui = new GUI({ title: "DoF settings" });
67-
gui.add(spark, "focalDistance", 0, 15, 0.01).name("Focal plane dist")
99+
gui.add(settings, "focalDistance", 0, 15, 0.01).name("Focal plane dist")
68100
.onChange(updateApertureAngle);
69-
gui.add(apertureSize, "apertureSize", 0, 0.4, 0.01).name("Aperture size")
101+
gui.add(settings, "apertureSize", 0, 0.4, 0.01).name("Aperture size")
70102
.onChange(updateApertureAngle);
71103

72-
const splatURL = await getAssetFileURL("valley.spz");
73-
const background = new SplatMesh({ url: splatURL });
74-
background.quaternion.set(1, 0, 0, 0);
75-
background.scale.setScalar(0.5);
76-
scene.add(background);
77-
78104
const controls = new SparkControls({ canvas: renderer.domElement });
79105
renderer.setAnimationLoop(function animate(time) {
80106
controls.update(camera);

examples/dynamic-lighting/index.html

Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,9 @@
7575
</script>
7676
<script type="module">
7777
import * as THREE from "three";
78-
import {
79-
SplatMesh,
80-
SplatEdit,
81-
SparkRenderer,
82-
SplatEditSdf,
83-
SplatEditSdfType,
84-
SplatEditRgbaBlendMode,
85-
FpsMovement,
86-
PointerControls,
87-
} from "@sparkjsdev/spark";
78+
import { SplatLoader } from "@sparkjsdev/spark";
8879
import { getAssetFileURL } from "/examples/js/get-asset-url.js";
80+
import { FpsMovement, PointerControls } from "/examples/js/controls.js";
8981

9082
const canvas = document.getElementById("canvas");
9183
const scene = new THREE.Scene();
@@ -104,12 +96,9 @@
10496
renderer.setSize(window.innerWidth, window.innerHeight);
10597
}
10698

107-
// Create a SparkRenderer and add it to the scene to render all the Gsplats.
108-
const spark = new SparkRenderer({ renderer });
109-
scene.add(spark);
110-
99+
const loader = new SplatLoader();
111100
const splatURL = await getAssetFileURL("fireplace.spz");
112-
const fireplace = new SplatMesh({ url: splatURL });
101+
const fireplace = await loader.loadAsync(splatURL);
113102
fireplace.quaternion.set(1, 0, 0, 0);
114103
fireplace.position.set(0, -1, -10);
115104
scene.add(fireplace);
@@ -122,14 +111,17 @@
122111
// Debug SDF spheres
123112
const helpers = [];
124113

125-
function createLight(scene, lightingLayer, position, color, radius, opacity) {
126-
const light = new SplatEditSdf({
127-
type: SplatEditSdfType.SPHERE,
128-
color: color,
129-
radius: radius,
130-
opacity: opacity,
131-
});
132-
light.position.copy(position);
114+
function createLight(scene, position, color, radius, opacity, sdfSmooth, sdfEdge, darken) {
115+
const light = {
116+
color,
117+
radius,
118+
opacity,
119+
position: position.clone(),
120+
visible: true,
121+
sdfSmooth,
122+
sdfEdge,
123+
darken,
124+
};
133125

134126
// create a wireframe helper
135127
const helper = new THREE.Mesh(
@@ -141,63 +133,86 @@
141133
scene.add(helper);
142134
helpers.push(helper);
143135

144-
lightingLayer.add(light);
145136
lights.push(light);
146137
}
147138

148-
// Create lighting layer
149-
const emberLayer = new SplatEdit({
150-
rgbaBlendMode: SplatEditRgbaBlendMode.ADD_RGBA,
151-
sdfSmooth: 0.1,
152-
softEdge: 0.8,
153-
});
154-
scene.add(emberLayer);
155-
156-
const lightingLayer = new SplatEdit({
157-
rgbaBlendMode: SplatEditRgbaBlendMode.ADD_RGBA,
158-
sdfSmooth: 0.1,
159-
softEdge: 1.4,
160-
});
161-
scene.add(lightingLayer);
162-
163-
const ambientLayer = new SplatEdit({
164-
rgbaBlendMode: SplatEditRgbaBlendMode.DARKEN,
165-
sdfSmooth: 0.1,
166-
softEdge: 0.05,
167-
});
168-
scene.add(ambientLayer);
169-
170139
// glowing embers at the base of the fire
171140
createLight(
172141
scene,
173-
emberLayer,
174142
new THREE.Vector3(0.5, -1.0, -10.5),
175143
new THREE.Color(1, 0.6, 0.4),
176144
0.75,
177145
1,
146+
0.1,
147+
0.8,
148+
false
178149
);
179150

180151
// light in the fireplace
181152
createLight(
182153
scene,
183-
lightingLayer,
184154
new THREE.Vector3(0.3, -1.1, -10.6),
185155
new THREE.Color(1, 0.95, 0.2),
186156
1.6,
187157
0,
158+
0.1,
159+
1.4,
160+
false
188161
);
189162

190163
// ambient light throughout the full room
191164
createLight(
192165
scene,
193-
ambientLayer,
194166
new THREE.Vector3(0, 1, -11),
195167
new THREE.Color(1, 0.8, 0.6),
196168
6,
197169
0.8,
170+
0.1,
171+
0.05,
172+
true
198173
);
199174
console.log(helpers);
200175

176+
fireplace.setShaderHooks({
177+
vertex: {
178+
uniforms: {
179+
'edits': {
180+
type: 'SplatEdit',
181+
value: lights
182+
},
183+
},
184+
global: /*glsl*/`
185+
struct SplatEdit {
186+
vec3 color;
187+
float radius;
188+
float opacity;
189+
vec3 position;
190+
bool visible;
191+
float sdfSmooth;
192+
float sdfEdge;
193+
bool darken;
194+
};
195+
`,
196+
splatColor: /*glsl*/`
197+
for(int i = 0; i < 3; i++) {
198+
if(!edits[i].visible) { continue; }
199+
200+
// TODO: Properly recreate original smoothing and modulation logic
201+
float dist = distance(edits[i].position, center) - edits[i].radius;
202+
203+
if(dist <= edits[i].radius) {
204+
float factor = clamp(-dist / edits[i].sdfEdge + 0.5, 0.0, 1.0);
205+
if(edits[i].darken) {
206+
rgba.rgb *= edits[i].color * edits[i].opacity * factor;
207+
} else {
208+
rgba.rgb += edits[i].color * edits[i].opacity * factor;
209+
}
210+
}
211+
}
212+
`
213+
}
214+
});
215+
201216
let lastTime;
202217

203218
renderer.setAnimationLoop(time => {

0 commit comments

Comments
 (0)