three-gpu-pathtracer 0.0.22 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/build/index.module.js +465 -57
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +464 -56
- package/build/index.umd.cjs.map +1 -1
- package/package.json +13 -13
- package/src/core/PathTracingRenderer.js +48 -1
- package/src/core/PathTracingSceneGenerator.js +21 -2
- package/src/core/WebGLPathTracer.js +68 -13
- package/src/core/utils/BakedGeometry.js +23 -0
- package/src/core/utils/BufferAttributeUtils.js +11 -3
- package/src/core/utils/MeshDiff.js +6 -13
- package/src/core/utils/StaticGeometryGenerator.js +18 -3
- package/src/core/utils/mergeGeometries.js +14 -1
- package/src/index.d.ts +2 -1
- package/src/materials/MaterialBase.js +11 -0
- package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +2 -1
- package/src/materials/pathtracing/glsl/attenuate_hit_function.glsl.js +2 -1
- package/src/materials/pathtracing/glsl/camera_util_functions.glsl.js +1 -1
- package/src/materials/pathtracing/glsl/get_surface_record_function.glsl.js +2 -1
- package/src/materials/surface/AmbientOcclusionMaterial.js +2 -0
- package/src/shader/bvh/inside_fog_volume_function.glsl.js +1 -1
- package/src/shader/structs/material_struct.glsl.js +3 -1
- package/src/uniforms/MaterialsTexture.js +4 -1
- package/src/uniforms/StratifiedSamplesTexture.js +55 -3
- package/src/uniforms/stratified/StratifiedSampler.js +19 -7
- package/src/uniforms/stratified/StratifiedSamplerCombined.js +14 -4
- package/src/utils/CubeToEquirectGenerator.js +159 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "three-gpu-pathtracer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"description": "Path tracing renderer and utilities for three.js built on top of three-mesh-bvh.",
|
|
5
5
|
"module": "src/index.js",
|
|
6
6
|
"main": "build/index.umd.cjs",
|
|
@@ -30,35 +30,35 @@
|
|
|
30
30
|
"tracer"
|
|
31
31
|
],
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@lookingglass/webxr": "^0.
|
|
33
|
+
"@lookingglass/webxr": "^0.6.0",
|
|
34
34
|
"@monogrid/gainmap-js": "^3.0.5",
|
|
35
|
-
"@types/node": "^
|
|
36
|
-
"@types/three": "^0.
|
|
37
|
-
"@typescript-eslint/
|
|
35
|
+
"@types/node": "^24.3.1",
|
|
36
|
+
"@types/three": "^0.181.0",
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^8.40.0",
|
|
38
38
|
"canvas-capture": "^2.0.5",
|
|
39
|
-
"eslint": "^
|
|
39
|
+
"eslint": "^8.56.0",
|
|
40
40
|
"eslint-config-mdcs": "^5.0.0",
|
|
41
41
|
"node-fetch": "^3.2.9",
|
|
42
|
-
"parcel": "^2.12.0",
|
|
43
42
|
"pixelmatch": "^5.3.0",
|
|
44
43
|
"pngjs": "^6.0.0",
|
|
45
44
|
"process": "^0.11.10",
|
|
46
45
|
"puppeteer": "^15.4.0",
|
|
47
46
|
"rollup": "^2.70.0",
|
|
48
47
|
"simple-git": "^3.10.0",
|
|
49
|
-
"three": "^0.
|
|
50
|
-
"three-mesh-bvh": "^0.
|
|
51
|
-
"typescript": "5.
|
|
48
|
+
"three": "^0.181.1",
|
|
49
|
+
"three-mesh-bvh": "^0.9.5",
|
|
50
|
+
"typescript": "^5.9.2",
|
|
51
|
+
"vite": "^6.2.2",
|
|
52
52
|
"yargs": "^17.5.1"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"three": ">=0.
|
|
55
|
+
"three": ">=0.180.0",
|
|
56
56
|
"three-mesh-bvh": ">=0.7.4",
|
|
57
57
|
"xatlas-web": "^0.1.0"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
|
-
"start": "
|
|
61
|
-
"build-examples": "
|
|
60
|
+
"start": "vite --config ./vite.config.js",
|
|
61
|
+
"build-examples": "vite build --config ./vite.config.js",
|
|
62
62
|
"update-screenshots": "node ./scripts/push-screenshots.js",
|
|
63
63
|
"screenshot-diff": "node ./scripts/regression-test.js",
|
|
64
64
|
"build": "rollup -c",
|
|
@@ -159,6 +159,9 @@ export class PathTracingRenderer {
|
|
|
159
159
|
|
|
160
160
|
set material( v ) {
|
|
161
161
|
|
|
162
|
+
this._fsQuad.material.removeEventListener( 'recompilation', this._compileFunction );
|
|
163
|
+
v.addEventListener( 'recompilation', this._compileFunction );
|
|
164
|
+
|
|
162
165
|
this._fsQuad.material = v;
|
|
163
166
|
|
|
164
167
|
}
|
|
@@ -195,6 +198,12 @@ export class PathTracingRenderer {
|
|
|
195
198
|
|
|
196
199
|
}
|
|
197
200
|
|
|
201
|
+
get isCompiling() {
|
|
202
|
+
|
|
203
|
+
return Boolean( this._compilePromise );
|
|
204
|
+
|
|
205
|
+
}
|
|
206
|
+
|
|
198
207
|
constructor( renderer ) {
|
|
199
208
|
|
|
200
209
|
this.camera = null;
|
|
@@ -212,6 +221,7 @@ export class PathTracingRenderer {
|
|
|
212
221
|
this._blendQuad = new FullScreenQuad( new BlendMaterial() );
|
|
213
222
|
this._task = null;
|
|
214
223
|
this._currentTile = 0;
|
|
224
|
+
this._compilePromise = null;
|
|
215
225
|
|
|
216
226
|
this._sobolTarget = new SobolNumberMapGenerator().generate( renderer );
|
|
217
227
|
|
|
@@ -236,6 +246,33 @@ export class PathTracingRenderer {
|
|
|
236
246
|
} ),
|
|
237
247
|
];
|
|
238
248
|
|
|
249
|
+
// function for listening to for triggered compilation so we can wait for compilation to finish
|
|
250
|
+
// before starting to render
|
|
251
|
+
this._compileFunction = () => {
|
|
252
|
+
|
|
253
|
+
const promise = this.compileMaterial( this._fsQuad._mesh );
|
|
254
|
+
promise.then( () => {
|
|
255
|
+
|
|
256
|
+
if ( this._compilePromise === promise ) {
|
|
257
|
+
|
|
258
|
+
this._compilePromise = null;
|
|
259
|
+
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
} );
|
|
263
|
+
|
|
264
|
+
this._compilePromise = promise;
|
|
265
|
+
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
this.material.addEventListener( 'recompilation', this._compileFunction );
|
|
269
|
+
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
compileMaterial() {
|
|
273
|
+
|
|
274
|
+
return this._renderer.compileAsync( this._fsQuad._mesh );
|
|
275
|
+
|
|
239
276
|
}
|
|
240
277
|
|
|
241
278
|
setCamera( camera ) {
|
|
@@ -267,7 +304,6 @@ export class PathTracingRenderer {
|
|
|
267
304
|
material.setDefine( 'CAMERA_TYPE', cameraType );
|
|
268
305
|
|
|
269
306
|
this.camera = camera;
|
|
270
|
-
// this.reset();
|
|
271
307
|
|
|
272
308
|
}
|
|
273
309
|
|
|
@@ -334,9 +370,11 @@ export class PathTracingRenderer {
|
|
|
334
370
|
this.samples = 0;
|
|
335
371
|
this._task = null;
|
|
336
372
|
|
|
373
|
+
this.material.stratifiedTexture.stableNoise = this.stableNoise;
|
|
337
374
|
if ( this.stableNoise ) {
|
|
338
375
|
|
|
339
376
|
this.material.seed = 0;
|
|
377
|
+
this.material.stratifiedTexture.reset();
|
|
340
378
|
|
|
341
379
|
}
|
|
342
380
|
|
|
@@ -344,6 +382,15 @@ export class PathTracingRenderer {
|
|
|
344
382
|
|
|
345
383
|
update() {
|
|
346
384
|
|
|
385
|
+
// ensure we've updated our defines before rendering so we can ensure we
|
|
386
|
+
// can wait for compilation to finish
|
|
387
|
+
this.material.onBeforeRender();
|
|
388
|
+
if ( this.isCompiling ) {
|
|
389
|
+
|
|
390
|
+
return;
|
|
391
|
+
|
|
392
|
+
}
|
|
393
|
+
|
|
347
394
|
if ( ! this._task ) {
|
|
348
395
|
|
|
349
396
|
this._task = renderTask.call( this );
|
|
@@ -95,6 +95,7 @@ export class PathTracingSceneGenerator {
|
|
|
95
95
|
this._bvhWorker = null;
|
|
96
96
|
this._pendingGenerate = null;
|
|
97
97
|
this._buildAsync = false;
|
|
98
|
+
this._materialUuids = null;
|
|
98
99
|
|
|
99
100
|
}
|
|
100
101
|
|
|
@@ -176,12 +177,29 @@ export class PathTracingSceneGenerator {
|
|
|
176
177
|
// generate the geometry
|
|
177
178
|
const result = staticGeometryGenerator.generate( geometry );
|
|
178
179
|
const materials = result.materials;
|
|
180
|
+
let needsMaterialIndexUpdate = result.changeType !== NO_CHANGE || this._materialUuids === null || this._materialUuids.length !== length;
|
|
181
|
+
if ( ! needsMaterialIndexUpdate ) {
|
|
182
|
+
|
|
183
|
+
for ( let i = 0, length = materials.length; i < length; i ++ ) {
|
|
184
|
+
|
|
185
|
+
const material = materials[ i ];
|
|
186
|
+
if ( material.uuid !== this._materialUuids[ i ] ) {
|
|
187
|
+
|
|
188
|
+
needsMaterialIndexUpdate = true;
|
|
189
|
+
break;
|
|
190
|
+
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
179
197
|
const textures = getTextures( materials );
|
|
180
198
|
const { lights, iesTextures } = getLights( objects );
|
|
181
|
-
|
|
182
|
-
if ( result.changeType !== NO_CHANGE ) {
|
|
199
|
+
if ( needsMaterialIndexUpdate ) {
|
|
183
200
|
|
|
184
201
|
updateMaterialIndexAttribute( geometry, materials, materials );
|
|
202
|
+
this._materialUuids = materials.map( material => material.uuid );
|
|
185
203
|
|
|
186
204
|
}
|
|
187
205
|
|
|
@@ -225,6 +243,7 @@ export class PathTracingSceneGenerator {
|
|
|
225
243
|
return {
|
|
226
244
|
bvhChanged: result.changeType !== NO_CHANGE,
|
|
227
245
|
bvh: this.bvh,
|
|
246
|
+
needsMaterialIndexUpdate,
|
|
228
247
|
lights,
|
|
229
248
|
iesTextures,
|
|
230
249
|
geometry,
|
|
@@ -5,6 +5,7 @@ import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
|
|
|
5
5
|
import { GradientEquirectTexture } from '../textures/GradientEquirectTexture.js';
|
|
6
6
|
import { getIesTextures, getLights, getTextures } from './utils/sceneUpdateUtils.js';
|
|
7
7
|
import { ClampedInterpolationMaterial } from '../materials/fullscreen/ClampedInterpolationMaterial.js';
|
|
8
|
+
import { CubeToEquirectGenerator } from '../utils/CubeToEquirectGenerator.js';
|
|
8
9
|
|
|
9
10
|
function supportsFloatBlending( renderer ) {
|
|
10
11
|
|
|
@@ -81,6 +82,24 @@ export class WebGLPathTracer {
|
|
|
81
82
|
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
get stableNoise() {
|
|
86
|
+
|
|
87
|
+
return this._pathTracer.stableNoise;
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
set stableNoise( v ) {
|
|
92
|
+
|
|
93
|
+
this._pathTracer.stableNoise = v;
|
|
94
|
+
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get isCompiling() {
|
|
98
|
+
|
|
99
|
+
return Boolean( this._pathTracer.isCompiling );
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
constructor( renderer ) {
|
|
85
104
|
|
|
86
105
|
// members
|
|
@@ -89,6 +108,7 @@ export class WebGLPathTracer {
|
|
|
89
108
|
this._pathTracer = new PathTracingRenderer( renderer );
|
|
90
109
|
this._queueReset = false;
|
|
91
110
|
this._clock = new Clock();
|
|
111
|
+
this._compilePromise = null;
|
|
92
112
|
|
|
93
113
|
this._lowResPathTracer = new PathTracingRenderer( renderer );
|
|
94
114
|
this._lowResPathTracer.tiles.set( 1, 1 );
|
|
@@ -101,6 +121,10 @@ export class WebGLPathTracer {
|
|
|
101
121
|
} ) );
|
|
102
122
|
this._materials = null;
|
|
103
123
|
|
|
124
|
+
this._previousEnvironment = null;
|
|
125
|
+
this._previousBackground = null;
|
|
126
|
+
this._internalBackground = null;
|
|
127
|
+
|
|
104
128
|
// options
|
|
105
129
|
this.renderDelay = 100;
|
|
106
130
|
this.minSamples = 5;
|
|
@@ -229,6 +253,13 @@ export class WebGLPathTracer {
|
|
|
229
253
|
const scene = this.scene;
|
|
230
254
|
const material = this._pathTracer.material;
|
|
231
255
|
|
|
256
|
+
if ( this._internalBackground ) {
|
|
257
|
+
|
|
258
|
+
this._internalBackground.dispose();
|
|
259
|
+
this._internalBackground = null;
|
|
260
|
+
|
|
261
|
+
}
|
|
262
|
+
|
|
232
263
|
// update scene background
|
|
233
264
|
material.backgroundBlur = scene.backgroundBlurriness;
|
|
234
265
|
material.backgroundIntensity = scene.backgroundIntensity ?? 1;
|
|
@@ -256,6 +287,17 @@ export class WebGLPathTracer {
|
|
|
256
287
|
material.backgroundMap = colorBackground;
|
|
257
288
|
material.backgroundAlpha = 1;
|
|
258
289
|
|
|
290
|
+
} else if ( scene.background.isCubeTexture ) {
|
|
291
|
+
|
|
292
|
+
if ( scene.background !== this._previousBackground ) {
|
|
293
|
+
|
|
294
|
+
const background = new CubeToEquirectGenerator( this._renderer ).generate( scene.background );
|
|
295
|
+
this._internalBackground = background;
|
|
296
|
+
material.backgroundMap = background;
|
|
297
|
+
material.backgroundAlpha = 1;
|
|
298
|
+
|
|
299
|
+
}
|
|
300
|
+
|
|
259
301
|
} else {
|
|
260
302
|
|
|
261
303
|
material.backgroundMap = scene.background;
|
|
@@ -264,26 +306,32 @@ export class WebGLPathTracer {
|
|
|
264
306
|
}
|
|
265
307
|
|
|
266
308
|
// update scene environment
|
|
267
|
-
material.environmentIntensity = scene.environmentIntensity ?? 1;
|
|
309
|
+
material.environmentIntensity = scene.environment !== null ? ( scene.environmentIntensity ?? 1 ) : 0;
|
|
268
310
|
material.environmentRotation.makeRotationFromEuler( scene.environmentRotation ).invert();
|
|
269
311
|
if ( this._previousEnvironment !== scene.environment ) {
|
|
270
312
|
|
|
271
|
-
if ( scene.environment ) {
|
|
313
|
+
if ( scene.environment !== null ) {
|
|
272
314
|
|
|
273
|
-
|
|
274
|
-
// OES_texture_float_linear or OES_texture_half_float_linear. Requires changes to
|
|
275
|
-
// the equirect uniform
|
|
276
|
-
material.envMapInfo.updateFrom( scene.environment );
|
|
315
|
+
if ( scene.environment.isCubeTexture ) {
|
|
277
316
|
|
|
278
|
-
|
|
317
|
+
const environment = new CubeToEquirectGenerator( this._renderer ).generate( scene.environment );
|
|
318
|
+
material.envMapInfo.updateFrom( environment );
|
|
319
|
+
|
|
320
|
+
} else {
|
|
279
321
|
|
|
280
|
-
|
|
322
|
+
// TODO: Consider setting this to the highest supported bit depth by checking for
|
|
323
|
+
// OES_texture_float_linear or OES_texture_half_float_linear. Requires changes to
|
|
324
|
+
// the equirect uniform
|
|
325
|
+
material.envMapInfo.updateFrom( scene.environment );
|
|
326
|
+
|
|
327
|
+
}
|
|
281
328
|
|
|
282
329
|
}
|
|
283
330
|
|
|
284
331
|
}
|
|
285
332
|
|
|
286
333
|
this._previousEnvironment = scene.environment;
|
|
334
|
+
this._previousBackground = scene.background;
|
|
287
335
|
this.reset();
|
|
288
336
|
|
|
289
337
|
}
|
|
@@ -295,6 +343,7 @@ export class WebGLPathTracer {
|
|
|
295
343
|
geometry,
|
|
296
344
|
bvh,
|
|
297
345
|
bvhChanged,
|
|
346
|
+
needsMaterialIndexUpdate,
|
|
298
347
|
} = results;
|
|
299
348
|
|
|
300
349
|
this._materials = materials;
|
|
@@ -312,6 +361,10 @@ export class WebGLPathTracer {
|
|
|
312
361
|
geometry.attributes.color,
|
|
313
362
|
);
|
|
314
363
|
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if ( needsMaterialIndexUpdate ) {
|
|
367
|
+
|
|
315
368
|
material.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
|
|
316
369
|
|
|
317
370
|
}
|
|
@@ -354,7 +407,7 @@ export class WebGLPathTracer {
|
|
|
354
407
|
// render the path tracing sample after enough time has passed
|
|
355
408
|
const delta = clock.getDelta() * 1e3;
|
|
356
409
|
const elapsedTime = clock.getElapsedTime() * 1e3;
|
|
357
|
-
if ( ! this.pausePathTracing && this.enablePathTracing && this.renderDelay <= elapsedTime ) {
|
|
410
|
+
if ( ! this.pausePathTracing && this.enablePathTracing && this.renderDelay <= elapsedTime && ! this.isCompiling ) {
|
|
358
411
|
|
|
359
412
|
pathTracer.update();
|
|
360
413
|
|
|
@@ -387,7 +440,7 @@ export class WebGLPathTracer {
|
|
|
387
440
|
// render the fallback if we haven't rendered enough samples, are paused, or are occluded
|
|
388
441
|
if ( ! this.enablePathTracing || this.samples < minSamples || quad.material.opacity < 1 ) {
|
|
389
442
|
|
|
390
|
-
if ( this.dynamicLowRes ) {
|
|
443
|
+
if ( this.dynamicLowRes && ! this.isCompiling ) {
|
|
391
444
|
|
|
392
445
|
if ( lowResPathTracer.samples < 1 ) {
|
|
393
446
|
|
|
@@ -402,7 +455,9 @@ export class WebGLPathTracer {
|
|
|
402
455
|
quad.render( renderer );
|
|
403
456
|
quad.material.opacity = currentOpacity;
|
|
404
457
|
|
|
405
|
-
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if ( ! this.dynamicLowRes && this.rasterizeScene || this.dynamicLowRes && this.isCompiling ) {
|
|
406
461
|
|
|
407
462
|
this.rasterizeSceneCallback( this.scene, this.camera );
|
|
408
463
|
|
|
@@ -440,8 +495,8 @@ export class WebGLPathTracer {
|
|
|
440
495
|
|
|
441
496
|
dispose() {
|
|
442
497
|
|
|
443
|
-
this.
|
|
444
|
-
this.
|
|
498
|
+
this._quad.dispose();
|
|
499
|
+
this._quad.material.dispose();
|
|
445
500
|
this._pathTracer.dispose();
|
|
446
501
|
|
|
447
502
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BufferGeometry } from 'three';
|
|
2
2
|
import { MeshDiff } from './MeshDiff.js';
|
|
3
3
|
import { convertToStaticGeometry } from './convertToStaticGeometry.js';
|
|
4
|
+
import { validateAttributes } from './BufferAttributeUtils.js';
|
|
4
5
|
|
|
5
6
|
export class BakedGeometry extends BufferGeometry {
|
|
6
7
|
|
|
@@ -13,6 +14,28 @@ export class BakedGeometry extends BufferGeometry {
|
|
|
13
14
|
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
// returns whether the passed mesh is compatible with this baked geometry
|
|
18
|
+
// such that it can be updated without resizing attributes
|
|
19
|
+
isCompatible( mesh, attributes ) {
|
|
20
|
+
|
|
21
|
+
const geometry = mesh.geometry;
|
|
22
|
+
for ( let i = 0; i < attributes.length; i ++ ) {
|
|
23
|
+
|
|
24
|
+
const key = attributes[ i ];
|
|
25
|
+
const attr1 = geometry.attributes[ key ];
|
|
26
|
+
const attr2 = this.attributes[ key ];
|
|
27
|
+
if ( attr1 && ! validateAttributes( attr1, attr2 ) ) {
|
|
28
|
+
|
|
29
|
+
return false;
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return true;
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
16
39
|
updateFrom( mesh, options ) {
|
|
17
40
|
|
|
18
41
|
const diff = this._diff;
|
|
@@ -41,12 +41,18 @@ export function createAttributeClone( attr, countOverride = null ) {
|
|
|
41
41
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// Confirms that the two provided attributes are compatible
|
|
44
|
+
// Confirms that the two provided attributes are compatible. Returns false if they are not.
|
|
45
45
|
export function validateAttributes( attr1, attr2 ) {
|
|
46
46
|
|
|
47
47
|
if ( ! attr1 && ! attr2 ) {
|
|
48
48
|
|
|
49
|
-
return;
|
|
49
|
+
return true;
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if ( Boolean( attr1 ) !== Boolean( attr2 ) ) {
|
|
54
|
+
|
|
55
|
+
return false;
|
|
50
56
|
|
|
51
57
|
}
|
|
52
58
|
|
|
@@ -57,8 +63,10 @@ export function validateAttributes( attr1, attr2 ) {
|
|
|
57
63
|
|
|
58
64
|
if ( ! sameCount || ! sameNormalized || ! sameType || ! sameItemSize ) {
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
return false;
|
|
61
67
|
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
return true;
|
|
71
|
+
|
|
64
72
|
}
|
|
@@ -1,29 +1,22 @@
|
|
|
1
1
|
import { Matrix4 } from 'three';
|
|
2
2
|
import { bufferToHash } from '../../utils/bufferToHash.js';
|
|
3
3
|
|
|
4
|
-
function attributeSort( a, b ) {
|
|
5
|
-
|
|
6
|
-
if ( a.uuid > b.uuid ) return 1;
|
|
7
|
-
if ( a.uuid < b.uuid ) return - 1;
|
|
8
|
-
return 0;
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
4
|
function getGeometryHash( geometry ) {
|
|
13
5
|
|
|
14
|
-
let hash =
|
|
6
|
+
let hash = geometry.uuid;
|
|
15
7
|
const attributes = Object.values( geometry.attributes );
|
|
16
8
|
if ( geometry.index ) {
|
|
17
9
|
|
|
18
10
|
attributes.push( geometry.index );
|
|
11
|
+
hash += `index|${ geometry.index.version }`;
|
|
19
12
|
|
|
20
13
|
}
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
for ( const attr of attributes ) {
|
|
15
|
+
const keys = Object.keys( attributes ).sort();
|
|
16
|
+
for ( const key of keys ) {
|
|
25
17
|
|
|
26
|
-
|
|
18
|
+
const attr = attributes[ key ];
|
|
19
|
+
hash += `${ key }_${ attr.version }|`;
|
|
27
20
|
|
|
28
21
|
}
|
|
29
22
|
|
|
@@ -171,15 +171,24 @@ export class StaticGeometryGenerator {
|
|
|
171
171
|
unusedMeshKeys.delete( meshKey );
|
|
172
172
|
|
|
173
173
|
// initialize the intermediate geometry
|
|
174
|
-
if
|
|
174
|
+
// if the mesh and source geometry have changed in such a way that they are no longer
|
|
175
|
+
// compatible then regenerate the baked geometry from scratch
|
|
176
|
+
let geom = _intermediateGeometry.get( meshKey );
|
|
177
|
+
if ( ! geom || ! geom.isCompatible( mesh, this.attributes ) ) {
|
|
175
178
|
|
|
176
|
-
|
|
179
|
+
if ( geom ) {
|
|
180
|
+
|
|
181
|
+
geom.dispose();
|
|
182
|
+
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
geom = new BakedGeometry();
|
|
186
|
+
_intermediateGeometry.set( meshKey, geom );
|
|
177
187
|
|
|
178
188
|
}
|
|
179
189
|
|
|
180
190
|
// transform the geometry into the intermediate buffer geometry, saving whether
|
|
181
191
|
// or not it changed.
|
|
182
|
-
const geom = _intermediateGeometry.get( meshKey );
|
|
183
192
|
if ( geom.updateFrom( mesh, convertOptions ) ) {
|
|
184
193
|
|
|
185
194
|
// TODO: provide option for only generating the set of attributes that are present
|
|
@@ -231,6 +240,12 @@ export class StaticGeometryGenerator {
|
|
|
231
240
|
|
|
232
241
|
// get the list of geometries to merge
|
|
233
242
|
let forceUpdate = false;
|
|
243
|
+
if ( meshes.length !== previousMergeInfo.length ) {
|
|
244
|
+
|
|
245
|
+
forceUpdate = true;
|
|
246
|
+
|
|
247
|
+
}
|
|
248
|
+
|
|
234
249
|
for ( let i = 0, l = meshes.length; i < l; i ++ ) {
|
|
235
250
|
|
|
236
251
|
const mesh = meshes[ i ];
|
|
@@ -205,7 +205,20 @@ export function mergeGeometries( geometries, options = {}, targetGeometry = new
|
|
|
205
205
|
const attr = geometry.getAttribute( key );
|
|
206
206
|
if ( ! skip ) {
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
if ( key === 'color' && targetAttribute.itemSize !== attr.itemSize ) {
|
|
209
|
+
|
|
210
|
+
// make sure the color attribute is aligned with itemSize 3 to 4
|
|
211
|
+
for ( let index = offset, l = attr.count; index < l; index ++ ) {
|
|
212
|
+
|
|
213
|
+
attr.setXYZW( index, targetAttribute.getX( index ), targetAttribute.getY( index ), targetAttribute.getZ( index ), 1.0 );
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
} else {
|
|
218
|
+
|
|
219
|
+
copyAttributeContents( attr, targetAttribute, offset );
|
|
220
|
+
|
|
221
|
+
}
|
|
209
222
|
|
|
210
223
|
}
|
|
211
224
|
|
package/src/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
Scene,
|
|
22
22
|
PMREMGenerator
|
|
23
23
|
} from 'three';
|
|
24
|
-
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass';
|
|
24
|
+
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
|
|
25
25
|
import { MeshBVH, MeshBVHOptions } from 'three-mesh-bvh';
|
|
26
26
|
|
|
27
27
|
// three.js type augmentation
|
|
@@ -57,6 +57,7 @@ export interface PathTracingSceneGeneratorResult {
|
|
|
57
57
|
lights: Array<Light>;
|
|
58
58
|
iesTextures: Array<DataTexture>;
|
|
59
59
|
geometry: BufferGeometry;
|
|
60
|
+
needsMaterialIndexUpdate: boolean;
|
|
60
61
|
materials: Array<Material>;
|
|
61
62
|
textures: Array<Texture>;
|
|
62
63
|
objects: Array<Object3D>;
|
|
@@ -2,6 +2,17 @@ import { ShaderMaterial } from 'three';
|
|
|
2
2
|
|
|
3
3
|
export class MaterialBase extends ShaderMaterial {
|
|
4
4
|
|
|
5
|
+
set needsUpdate( v ) {
|
|
6
|
+
|
|
7
|
+
super.needsUpdate = true;
|
|
8
|
+
this.dispatchEvent( {
|
|
9
|
+
|
|
10
|
+
type: 'recompilation',
|
|
11
|
+
|
|
12
|
+
} );
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
5
16
|
constructor( shader ) {
|
|
6
17
|
|
|
7
18
|
super( shader );
|
|
@@ -10,7 +10,7 @@ import { PhysicalCameraUniform } from '../../uniforms/PhysicalCameraUniform.js';
|
|
|
10
10
|
import { EquirectHdrInfoUniform } from '../../uniforms/EquirectHdrInfoUniform.js';
|
|
11
11
|
import { LightsInfoUniformStruct } from '../../uniforms/LightsInfoUniformStruct.js';
|
|
12
12
|
import { AttributesTextureArray } from '../../uniforms/AttributesTextureArray.js';
|
|
13
|
-
import { MaterialsTexture } from '../../uniforms/MaterialsTexture.js';
|
|
13
|
+
import { MaterialsTexture, MATERIAL_PIXELS } from '../../uniforms/MaterialsTexture.js';
|
|
14
14
|
import { RenderTarget2DArray } from '../../uniforms/RenderTarget2DArray.js';
|
|
15
15
|
import { StratifiedSamplesTexture } from '../../uniforms/StratifiedSamplesTexture.js';
|
|
16
16
|
import { BlueNoiseTexture } from '../../textures/BlueNoiseTexture.js';
|
|
@@ -66,6 +66,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
66
66
|
ATTR_TANGENT: 1,
|
|
67
67
|
ATTR_UV: 2,
|
|
68
68
|
ATTR_COLOR: 3,
|
|
69
|
+
MATERIAL_PIXELS: MATERIAL_PIXELS,
|
|
69
70
|
},
|
|
70
71
|
|
|
71
72
|
uniforms: {
|
|
@@ -97,7 +97,8 @@ export const attenuate_hit_function = /* glsl */`
|
|
|
97
97
|
// alphaMap
|
|
98
98
|
if ( material.alphaMap != - 1 ) {
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
|
|
101
|
+
albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
|
|
101
102
|
|
|
102
103
|
}
|
|
103
104
|
|
|
@@ -60,7 +60,7 @@ export const camera_util_functions = /* glsl */`
|
|
|
60
60
|
vec3 shapeUVW= rand3( 1 );
|
|
61
61
|
int blades = physicalCamera.apertureBlades;
|
|
62
62
|
float anamorphicRatio = physicalCamera.anamorphicRatio;
|
|
63
|
-
vec2 apertureSample =
|
|
63
|
+
vec2 apertureSample = sampleAperture( blades, shapeUVW );
|
|
64
64
|
apertureSample *= physicalCamera.bokehSize * 0.5 * 1e-3;
|
|
65
65
|
|
|
66
66
|
// rotate the aperture shape
|
|
@@ -48,7 +48,8 @@ export const get_surface_record_function = /* glsl */`
|
|
|
48
48
|
// alphaMap
|
|
49
49
|
if ( material.alphaMap != - 1 ) {
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
|
|
52
|
+
albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
|
|
52
53
|
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TangentSpaceNormalMap, Vector2 } from 'three';
|
|
2
2
|
import { MaterialBase } from '../MaterialBase.js';
|
|
3
3
|
import { MeshBVHUniformStruct, BVHShaderGLSL } from 'three-mesh-bvh';
|
|
4
|
+
import { MATERIAL_PIXELS } from '../../uniforms/MaterialsTexture.js';
|
|
4
5
|
|
|
5
6
|
import * as StructsGLSL from '../../shader/structs/index.js';
|
|
6
7
|
import * as SamplingGLSL from '../../shader/sampling/index.js';
|
|
@@ -43,6 +44,7 @@ export class AmbientOcclusionMaterial extends MaterialBase {
|
|
|
43
44
|
|
|
44
45
|
defines: {
|
|
45
46
|
SAMPLES: 10,
|
|
47
|
+
MATERIAL_PIXELS: MATERIAL_PIXELS,
|
|
46
48
|
},
|
|
47
49
|
|
|
48
50
|
uniforms: {
|
|
@@ -7,7 +7,7 @@ export const inside_fog_volume_function = /* glsl */`
|
|
|
7
7
|
// returns whether the given material is a fog material or not
|
|
8
8
|
bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
|
|
9
9
|
|
|
10
|
-
uint i = materialIndex *
|
|
10
|
+
uint i = materialIndex * uint( MATERIAL_PIXELS );
|
|
11
11
|
vec4 s14 = texelFetch1D( materials, i + 14u );
|
|
12
12
|
return bool( int( s14.b ) & 4 );
|
|
13
13
|
|