|
75 | 75 | </script> |
76 | 76 | <script type="module"> |
77 | 77 | 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"; |
88 | 79 | import { getAssetFileURL } from "/examples/js/get-asset-url.js"; |
| 80 | + import { FpsMovement, PointerControls } from "/examples/js/controls.js"; |
89 | 81 |
|
90 | 82 | const canvas = document.getElementById("canvas"); |
91 | 83 | const scene = new THREE.Scene(); |
|
104 | 96 | renderer.setSize(window.innerWidth, window.innerHeight); |
105 | 97 | } |
106 | 98 |
|
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(); |
111 | 100 | const splatURL = await getAssetFileURL("fireplace.spz"); |
112 | | - const fireplace = new SplatMesh({ url: splatURL }); |
| 101 | + const fireplace = await loader.loadAsync(splatURL); |
113 | 102 | fireplace.quaternion.set(1, 0, 0, 0); |
114 | 103 | fireplace.position.set(0, -1, -10); |
115 | 104 | scene.add(fireplace); |
|
122 | 111 | // Debug SDF spheres |
123 | 112 | const helpers = []; |
124 | 113 |
|
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 | + }; |
133 | 125 |
|
134 | 126 | // create a wireframe helper |
135 | 127 | const helper = new THREE.Mesh( |
|
141 | 133 | scene.add(helper); |
142 | 134 | helpers.push(helper); |
143 | 135 |
|
144 | | - lightingLayer.add(light); |
145 | 136 | lights.push(light); |
146 | 137 | } |
147 | 138 |
|
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 | | - |
170 | 139 | // glowing embers at the base of the fire |
171 | 140 | createLight( |
172 | 141 | scene, |
173 | | - emberLayer, |
174 | 142 | new THREE.Vector3(0.5, -1.0, -10.5), |
175 | 143 | new THREE.Color(1, 0.6, 0.4), |
176 | 144 | 0.75, |
177 | 145 | 1, |
| 146 | + 0.1, |
| 147 | + 0.8, |
| 148 | + false |
178 | 149 | ); |
179 | 150 |
|
180 | 151 | // light in the fireplace |
181 | 152 | createLight( |
182 | 153 | scene, |
183 | | - lightingLayer, |
184 | 154 | new THREE.Vector3(0.3, -1.1, -10.6), |
185 | 155 | new THREE.Color(1, 0.95, 0.2), |
186 | 156 | 1.6, |
187 | 157 | 0, |
| 158 | + 0.1, |
| 159 | + 1.4, |
| 160 | + false |
188 | 161 | ); |
189 | 162 |
|
190 | 163 | // ambient light throughout the full room |
191 | 164 | createLight( |
192 | 165 | scene, |
193 | | - ambientLayer, |
194 | 166 | new THREE.Vector3(0, 1, -11), |
195 | 167 | new THREE.Color(1, 0.8, 0.6), |
196 | 168 | 6, |
197 | 169 | 0.8, |
| 170 | + 0.1, |
| 171 | + 0.05, |
| 172 | + true |
198 | 173 | ); |
199 | 174 | console.log(helpers); |
200 | 175 |
|
| 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 | + |
201 | 216 | let lastTime; |
202 | 217 |
|
203 | 218 | renderer.setAnimationLoop(time => { |
|
0 commit comments