rayzee 11.2.0 → 11.3.0
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 +62 -33
- package/dist/ArchiveImporter-D735xX-4.js +7860 -0
- package/dist/BidirectionalIntegrator-M89c0AlP.js +954 -0
- package/dist/{BufferLayout-D_IBo2ci.js → BufferLayout-xPRH9jYD.js} +41 -5
- package/dist/{NeuralRetouch-DFGbnmLb.js → NeuralRetouch-DiBH63kz.js} +1 -1
- package/dist/{NeuralSuperRes-BtXvyJQl.js → NeuralSuperRes-CZNQP13W.js} +1 -1
- package/dist/{PathTracer-BHXGJtPN.js → PathTracer-BXCCVrPK.js} +4371 -4612
- package/dist/{PhysicalSky-DN6u8UX4.js → PhysicalSky-DqmaS0lb.js} +2 -2
- package/dist/{RayzeeRenderer-Dw1x8mVL.js → RayzeeRenderer-DUSMFhmc.js} +426 -363
- package/dist/SolarLimb-bZE6y63S.js +762 -0
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/rayzee-core.es.js +7 -7
- package/dist/rayzee.es.js +472 -468
- package/dist/rayzee.umd.js +106 -105
- package/dist/sceneCachePolicy-BAib5fCp.js +1602 -0
- package/package.json +1 -1
- package/src/EngineDefaults.js +1 -1
- package/src/PathTracerApp.js +4 -3
- package/src/Processor/ArchiveImporter.js +173 -94
- package/src/Processor/ArchiveReader.js +20 -23
- package/src/Processor/AssetLoader.js +48 -1
- package/src/Processor/BVHRefitter.js +109 -13
- package/src/Processor/BufferLayout.js +115 -0
- package/src/Processor/ChunkedRecords.js +3 -2
- package/src/Processor/EmissiveTriangleBuilder.js +6 -6
- package/src/Processor/GeometryExtractor.js +189 -79
- package/src/Processor/HostMemory.js +39 -9
- package/src/Processor/InstanceTable.js +714 -42
- package/src/Processor/PBRT/PBRTCurves.js +224 -6
- package/src/Processor/PBRT/PBRTMaterials.js +3 -0
- package/src/Processor/PBRT/PBRTSceneBuilder.js +82 -25
- package/src/Processor/PackedRayBuffer.js +5 -2
- package/src/Processor/SceneProcessor.js +747 -141
- package/src/Processor/TLASBuilder.js +32 -17
- package/src/Processor/USD/USDLayer.js +1149 -0
- package/src/Processor/USD/USDScene.js +1655 -0
- package/src/Processor/USD/USDStage.js +682 -0
- package/src/Processor/USD/USDText.js +674 -0
- package/src/Processor/USD/index.js +138 -0
- package/src/Processor/Workers/BVHRefitWorker.js +3 -3
- package/src/RayzeeRenderer.js +168 -27
- package/src/Stages/NormalDepth.js +7 -12
- package/src/Stages/PathTracer.js +12 -17
- package/src/Stages/PathTracerStage.js +273 -96
- package/src/Storage/GeometrySpill.js +46 -14
- package/src/Storage/SceneGraphCodec.js +17 -3
- package/src/Storage/SpillStore.js +9 -0
- package/src/TSL/BVHTraversal.js +183 -7
- package/src/TSL/Common.js +57 -13
- package/src/TSL/ShadeKernel.js +3 -2
- package/src/TSL/TextureSampling.js +11 -8
- package/src/TSL/patches.js +84 -0
- package/src/integrators/BidirectionalIntegrator.js +20 -2
- package/src/managers/DenoisingManager.js +4 -4
- package/src/managers/InteractionManager.js +19 -6
- package/src/managers/MaterialDataManager.js +22 -5
- package/src/managers/UniformManager.js +3 -0
- package/dist/ArchiveImporter-eNlu0_6F.js +0 -5198
- package/dist/BidirectionalIntegrator-D1-6UySB.js +0 -946
- package/dist/SolarLimb-CPaeZVwz.js +0 -721
- package/dist/sceneCachePolicy-DA7iGoL4.js +0 -485
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { isFoldedRef, foldedFirst, foldedCount } from './BVHLeafFold.js';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
TRIANGLE_DATA_LAYOUT, packNormalOct, BVH_LEAF_MARKERS, BVH_EMPTY_BOX,
|
|
15
|
+
CLUSTER_FIRST_MASK, CLUSTER_COUNT_SHIFT, CLUSTER_ROOT_MASK, CLUSTER_SIZE, RECORD_VEC4, TLAS_LEAF_IDENTITY, encodeClusterBoxes,
|
|
16
|
+
} from './BufferLayout.js';
|
|
14
17
|
|
|
15
18
|
const FPT = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;
|
|
16
19
|
|
|
@@ -54,6 +57,7 @@ const triAccess = ( triangleData ) => {
|
|
|
54
57
|
const FLOATS_PER_NODE = 16; // 4 vec4s per BVH node
|
|
55
58
|
const LEAF_MARKER = BVH_LEAF_MARKERS.TRIANGLE_LEAF;
|
|
56
59
|
const BLAS_POINTER_MARKER = BVH_LEAF_MARKERS.BLAS_POINTER_LEAF;
|
|
60
|
+
const CLUSTER_MARKER = BVH_LEAF_MARKERS.CLUSTER_LEAF;
|
|
57
61
|
|
|
58
62
|
// Relative to the matrix's own magnitude, never an absolute floor — see transformBoundsToWorld.
|
|
59
63
|
const SINGULAR_REL_EPS = 1e-12;
|
|
@@ -64,6 +68,13 @@ const SINGULAR_REL_EPS = 1e-12;
|
|
|
64
68
|
*/
|
|
65
69
|
function transformBoundsToWorld( bvhData, nodeOff, src, srcOff, dst, dstOff ) {
|
|
66
70
|
|
|
71
|
+
if ( src[ srcOff ] > src[ srcOff + 3 ] ) {
|
|
72
|
+
|
|
73
|
+
for ( let i = 0; i < 6; i ++ ) dst[ dstOff + i ] = src[ srcOff + i ];
|
|
74
|
+
return;
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
67
78
|
const a0 = bvhData[ nodeOff + 4 ], a1 = bvhData[ nodeOff + 5 ], a2 = bvhData[ nodeOff + 6 ], tx = bvhData[ nodeOff + 7 ];
|
|
68
79
|
const a3 = bvhData[ nodeOff + 8 ], a4 = bvhData[ nodeOff + 9 ], a5 = bvhData[ nodeOff + 10 ], ty = bvhData[ nodeOff + 11 ];
|
|
69
80
|
const a6 = bvhData[ nodeOff + 12 ], a7 = bvhData[ nodeOff + 13 ], a8 = bvhData[ nodeOff + 14 ], tz = bvhData[ nodeOff + 15 ];
|
|
@@ -151,6 +162,19 @@ function triangleBounds( acc, first, count, out, off ) {
|
|
|
151
162
|
|
|
152
163
|
const folded = new Float32Array( 12 );
|
|
153
164
|
|
|
165
|
+
// Empty bounds (a hidden group member, an empty leaf) are min > max while computed — neutral in a union — and stored
|
|
166
|
+
// as the far point box no ray enters, read back as empty.
|
|
167
|
+
const isEmptyStored = ( f, o ) => f[ o ] >= BVH_EMPTY_BOX * 0.5;
|
|
168
|
+
|
|
169
|
+
/** A child's computed box into its slot at `o`: as it is, or the far point box when empty. */
|
|
170
|
+
function writeChildSlot( f, o, src, s ) {
|
|
171
|
+
|
|
172
|
+
const empty = src[ s ] > src[ s + 3 ];
|
|
173
|
+
f[ o ] = empty ? BVH_EMPTY_BOX : src[ s ]; f[ o + 1 ] = empty ? BVH_EMPTY_BOX : src[ s + 1 ]; f[ o + 2 ] = empty ? BVH_EMPTY_BOX : src[ s + 2 ];
|
|
174
|
+
f[ o + 4 ] = empty ? BVH_EMPTY_BOX : src[ s + 3 ]; f[ o + 5 ] = empty ? BVH_EMPTY_BOX : src[ s + 4 ]; f[ o + 6 ] = empty ? BVH_EMPTY_BOX : src[ s + 5 ];
|
|
175
|
+
|
|
176
|
+
}
|
|
177
|
+
|
|
154
178
|
/** A node's own box from what it stores — its two child boxes, or a leaf's triangles — into out[ 0 .. 6 ). */
|
|
155
179
|
function storedBounds( nodes, n, acc, out ) {
|
|
156
180
|
|
|
@@ -165,13 +189,56 @@ function storedBounds( nodes, n, acc, out ) {
|
|
|
165
189
|
|
|
166
190
|
}
|
|
167
191
|
|
|
192
|
+
const left = ! isEmptyStored( f, o ), right = ! isEmptyStored( f, o + 8 );
|
|
168
193
|
for ( let a = 0; a < 3; a ++ ) {
|
|
169
194
|
|
|
170
|
-
out[ a ] = Math.min( f[ o + a ], f[ o + 8 + a ] );
|
|
171
|
-
out[ 3 + a ] = Math.max( f[ o + 4 + a ], f[ o + 12 + a ] );
|
|
195
|
+
out[ a ] = Math.min( left ? f[ o + a ] : Infinity, right ? f[ o + 8 + a ] : Infinity );
|
|
196
|
+
out[ 3 + a ] = Math.max( left ? f[ o + 4 + a ] : - Infinity, right ? f[ o + 12 + a ] : - Infinity );
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const recordRows = new Float32Array( 16 );
|
|
203
|
+
const rootBox = new Float32Array( 6 );
|
|
204
|
+
const copyBoxes = new Float32Array( CLUSTER_SIZE * 6 );
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* A cluster leaf's boxes from its copies now: each copy's root's stored box carried to world through its record
|
|
208
|
+
* (behind the BLASes from node `recordNodeStart`), encoded into the leaf; the union into `out` at `outOff`.
|
|
209
|
+
*/
|
|
210
|
+
function refitClusterLeaf( nodes, i, acc, recordNodeStart, out, outOff ) {
|
|
211
|
+
|
|
212
|
+
const f = nodeF( nodes, i ), idx = nodeIdx( nodes, i ), o = nodeBase( nodes, i );
|
|
213
|
+
const first = idx[ o ] & CLUSTER_FIRST_MASK;
|
|
214
|
+
const count = ( ( idx[ o ] >>> CLUSTER_COUNT_SHIFT ) & 3 ) + 1;
|
|
215
|
+
|
|
216
|
+
for ( let k = 0; k < count; k ++ ) {
|
|
217
|
+
|
|
218
|
+
const word = idx[ o + 12 + k ];
|
|
219
|
+
storedBounds( nodes, word & CLUSTER_ROOT_MASK, acc, rootBox );
|
|
220
|
+
if ( word & TLAS_LEAF_IDENTITY ) {
|
|
221
|
+
|
|
222
|
+
for ( let a = 0; a < 6; a ++ ) copyBoxes[ k * 6 + a ] = rootBox[ a ];
|
|
223
|
+
continue;
|
|
224
|
+
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// A record's 12 floats may straddle a node, and so a chunk.
|
|
228
|
+
const lane = recordNodeStart * 16 + ( first + k ) * RECORD_VEC4 * 4;
|
|
229
|
+
for ( let l = 0; l < 12; l ++ ) {
|
|
230
|
+
|
|
231
|
+
const n = ( lane + l ) >> 4;
|
|
232
|
+
recordRows[ 4 + l ] = nodeF( nodes, n )[ nodeBase( nodes, n ) + ( ( lane + l ) & 15 ) ];
|
|
233
|
+
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
transformBoundsToWorld( recordRows, 0, rootBox, 0, copyBoxes, k * 6 );
|
|
172
237
|
|
|
173
238
|
}
|
|
174
239
|
|
|
240
|
+
encodeClusterBoxes( f, idx, o, copyBoxes, count, out, outOff );
|
|
241
|
+
|
|
175
242
|
}
|
|
176
243
|
|
|
177
244
|
/**
|
|
@@ -179,7 +246,7 @@ function storedBounds( nodes, n, acc, out ) {
|
|
|
179
246
|
* node's two child boxes rewritten from theirs. A leaf folded into its parent takes its box from
|
|
180
247
|
* its triangles.
|
|
181
248
|
*/
|
|
182
|
-
function refitNode( nodes, i, acc, bounds, base ) {
|
|
249
|
+
function refitNode( nodes, i, acc, bounds, base, recordNodeStart = - 1 ) {
|
|
183
250
|
|
|
184
251
|
const bvhF = nodeF( nodes, i );
|
|
185
252
|
const idx = nodeIdx( nodes, i );
|
|
@@ -194,6 +261,13 @@ function refitNode( nodes, i, acc, bounds, base ) {
|
|
|
194
261
|
|
|
195
262
|
}
|
|
196
263
|
|
|
264
|
+
if ( marker === CLUSTER_MARKER ) {
|
|
265
|
+
|
|
266
|
+
refitClusterLeaf( nodes, i, acc, recordNodeStart, bounds, b );
|
|
267
|
+
return;
|
|
268
|
+
|
|
269
|
+
}
|
|
270
|
+
|
|
197
271
|
if ( marker === BLAS_POINTER_MARKER ) {
|
|
198
272
|
|
|
199
273
|
// BLAS-pointer leaf (TLAS): the BLAS root's bounds are in the instance's own
|
|
@@ -222,10 +296,8 @@ function refitNode( nodes, i, acc, bounds, base ) {
|
|
|
222
296
|
|
|
223
297
|
}
|
|
224
298
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
bvhF[ o + 8 ] = rs[ rb ]; bvhF[ o + 9 ] = rs[ rb + 1 ]; bvhF[ o + 10 ] = rs[ rb + 2 ];
|
|
228
|
-
bvhF[ o + 12 ] = rs[ rb + 3 ]; bvhF[ o + 13 ] = rs[ rb + 4 ]; bvhF[ o + 14 ] = rs[ rb + 5 ];
|
|
299
|
+
writeChildSlot( bvhF, o, ls, lb );
|
|
300
|
+
writeChildSlot( bvhF, o + 8, rs, rb );
|
|
229
301
|
|
|
230
302
|
for ( let a = 0; a < 3; a ++ ) {
|
|
231
303
|
|
|
@@ -346,15 +418,15 @@ export class BVHRefitter {
|
|
|
346
418
|
* @param {ArrayLike<number>} blasRanges - flat [ startNode, nodeCount, ... ]
|
|
347
419
|
* @param {number} tlasNodeCount - the TLAS occupies nodes [0, tlasNodeCount)
|
|
348
420
|
*/
|
|
349
|
-
refitPartial( bvhData, triangleData, blasRanges, tlasNodeCount ) {
|
|
421
|
+
refitPartial( bvhData, triangleData, blasRanges, tlasNodeCount, groupRange = null, recordNodeStart = - 1 ) {
|
|
350
422
|
|
|
351
423
|
for ( let r = 0; r < blasRanges.length; r += 2 ) this.refitRange( bvhData, triangleData, blasRanges[ r ], blasRanges[ r + 1 ] );
|
|
352
424
|
|
|
353
425
|
const acc = triAccess( triangleData );
|
|
354
426
|
const nodes = nodeAccess( bvhData );
|
|
427
|
+
if ( groupRange ) this.refitGroups( nodes, acc, groupRange[ 0 ], groupRange[ 1 ] );
|
|
355
428
|
if ( ! this._tlasBounds || this._tlasBounds.length < tlasNodeCount * 6 ) this._tlasBounds = new Float32Array( tlasNodeCount * 6 );
|
|
356
429
|
const bounds = this._tlasBounds;
|
|
357
|
-
const rootBox = this._rootBox ||= new Float32Array( 6 );
|
|
358
430
|
|
|
359
431
|
for ( let i = tlasNodeCount - 1; i >= 0; i -- ) {
|
|
360
432
|
|
|
@@ -368,7 +440,7 @@ export class BVHRefitter {
|
|
|
368
440
|
|
|
369
441
|
} else {
|
|
370
442
|
|
|
371
|
-
refitNode( nodes, i, acc, bounds, 0 );
|
|
443
|
+
refitNode( nodes, i, acc, bounds, 0, recordNodeStart );
|
|
372
444
|
|
|
373
445
|
}
|
|
374
446
|
|
|
@@ -376,6 +448,30 @@ export class BVHRefitter {
|
|
|
376
448
|
|
|
377
449
|
}
|
|
378
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Group trees between the TLAS and the BLASes: each inner node's child boxes from what its children store, last
|
|
453
|
+
* node first, so a child group node is current before its parent reads it. The empty leaf is left alone.
|
|
454
|
+
* @private
|
|
455
|
+
*/
|
|
456
|
+
refitGroups( nodes, acc, start, count ) {
|
|
457
|
+
|
|
458
|
+
const box = this._groupBox ||= new Float32Array( 6 );
|
|
459
|
+
for ( let i = start + count - 1; i >= start; i -- ) {
|
|
460
|
+
|
|
461
|
+
const f = nodeF( nodes, i );
|
|
462
|
+
const idx = nodeIdx( nodes, i );
|
|
463
|
+
const o = nodeBase( nodes, i );
|
|
464
|
+
if ( idx[ o + 3 ] === LEAF_MARKER ) continue;
|
|
465
|
+
|
|
466
|
+
storedBounds( nodes, idx[ o + 3 ], acc, box );
|
|
467
|
+
writeChildSlot( f, o, box, 0 );
|
|
468
|
+
storedBounds( nodes, idx[ o + 7 ], acc, box );
|
|
469
|
+
writeChildSlot( f, o + 8, box, 0 );
|
|
470
|
+
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
}
|
|
474
|
+
|
|
379
475
|
/**
|
|
380
476
|
* Bottom-up refit of all BVH node AABBs.
|
|
381
477
|
* Reverse pre-order iteration gives valid bottom-up order (children have higher
|
|
@@ -385,7 +481,7 @@ export class BVHRefitter {
|
|
|
385
481
|
* @param {Uint32Array} triangleData - Updated triangle records
|
|
386
482
|
* @param {number} nodeCount - Total number of BVH nodes
|
|
387
483
|
*/
|
|
388
|
-
refit( bvhData, triangleData, nodeCount ) {
|
|
484
|
+
refit( bvhData, triangleData, nodeCount, recordNodeStart = - 1 ) {
|
|
389
485
|
|
|
390
486
|
const acc = triAccess( triangleData );
|
|
391
487
|
|
|
@@ -398,7 +494,7 @@ export class BVHRefitter {
|
|
|
398
494
|
}
|
|
399
495
|
|
|
400
496
|
const nodes = nodeAccess( bvhData );
|
|
401
|
-
for ( let i = nodeCount - 1; i >= 0; i -- ) refitNode( nodes, i, acc, this._bounds, 0 );
|
|
497
|
+
for ( let i = nodeCount - 1; i >= 0; i -- ) refitNode( nodes, i, acc, this._bounds, 0, recordNodeStart );
|
|
402
498
|
|
|
403
499
|
}
|
|
404
500
|
|
|
@@ -244,8 +244,116 @@ export const BVH_LEAF_MARKERS = {
|
|
|
244
244
|
TRIANGLE_LEAF: 0x40000000, // leaf containing triangle references
|
|
245
245
|
BLAS_POINTER_LEAF: 0x40000001, // TLAS leaf pointing to a BLAS root node
|
|
246
246
|
FRONTIER: 0x40000002, // parallel-build placeholder, overwritten during assembly
|
|
247
|
+
CLUSTER_LEAF: 0x40000003, // TLAS leaf over up to CLUSTER_SIZE copies of one object (copy clusters)
|
|
247
248
|
};
|
|
248
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Copy clusters, for a TLAS over millions of copies: a leaf holds up to CLUSTER_SIZE neighbouring copies, and each
|
|
252
|
+
* copy's world-to-object rows are a record of RECORD_VEC4 vec4s behind the BLASes (48 B, against 64 B of leaf node and
|
|
253
|
+
* 64 B of inner node a copy without them). Leaf slots:
|
|
254
|
+
* 0 first record | ( count − 1 ) << CLUSTER_COUNT_SHIFT; copy k's record is first + k
|
|
255
|
+
* 1 byte a: the f32 exponent field of axis a's step, a power of two
|
|
256
|
+
* 2, 4, 5 the union box's min (x, y, z)
|
|
257
|
+
* 3 CLUSTER_LEAF
|
|
258
|
+
* 6–8 copy k's box min on axis 0–2, in steps from the union's min, byte k
|
|
259
|
+
* 9–11 the same for its max
|
|
260
|
+
* 12 + k copy k's object root | TLAS_LEAF_IDENTITY (its record is identity) | CLUSTER_COPY_HIDDEN
|
|
261
|
+
* A traversal stack entry `COPY_ENTRY | leaf << 2 | k` enters copy k: node indices stay below it, folded leaves start at
|
|
262
|
+
* 2^31, so a leaf index and a record index must each stay below 2^28.
|
|
263
|
+
*/
|
|
264
|
+
export const CLUSTER_SIZE = 4;
|
|
265
|
+
export const CLUSTER_FIRST_MASK = 0x0fffffff;
|
|
266
|
+
export const CLUSTER_COUNT_SHIFT = 28;
|
|
267
|
+
export const CLUSTER_ROOT_MASK = 0x3fffffff;
|
|
268
|
+
export const CLUSTER_COPY_HIDDEN = 0x80000000;
|
|
269
|
+
export const COPY_ENTRY = 0x40000000;
|
|
270
|
+
export const RECORD_VEC4 = 3;
|
|
271
|
+
|
|
272
|
+
const _f32 = new Float32Array( 1 );
|
|
273
|
+
const _u32 = new Uint32Array( _f32.buffer );
|
|
274
|
+
|
|
275
|
+
/** The f32 nearest `x` that is not above it. */
|
|
276
|
+
function f32Down( x ) {
|
|
277
|
+
|
|
278
|
+
const f = Math.fround( x );
|
|
279
|
+
if ( f <= x ) return f;
|
|
280
|
+
_f32[ 0 ] = f;
|
|
281
|
+
_u32[ 0 ] += f > 0 ? - 1 : 1;
|
|
282
|
+
return _f32[ 0 ];
|
|
283
|
+
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Four f32 steps at the larger magnitude of `a` and `b`: what an f32 sum near them can be off by, with room. */
|
|
287
|
+
function f32Pad( a, b ) {
|
|
288
|
+
|
|
289
|
+
return Math.max( Math.abs( a ), Math.abs( b ), 1e-30 ) * 2 ** - 21;
|
|
290
|
+
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const BASE_SLOT = [ 2, 4, 5 ];
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* A cluster leaf's boxes (slots 1, 2 and 4–11 at float offset `o`): the union of `count` copies' world boxes (`boxes`,
|
|
297
|
+
* 6 a copy), then each as bytes of it. Everything is rounded outward by a few f32 steps of its own size, so the GPU's
|
|
298
|
+
* f32 decode never culls a copy from a ray that reaches it — far from the origin a byte step can be finer than one f32
|
|
299
|
+
* step. The exact union goes to `out` at `outOff`.
|
|
300
|
+
*/
|
|
301
|
+
export function encodeClusterBoxes( data, idx, o, boxes, count, out, outOff ) {
|
|
302
|
+
|
|
303
|
+
let exponents = 0;
|
|
304
|
+
for ( let a = 0; a < 3; a ++ ) {
|
|
305
|
+
|
|
306
|
+
let lo = Infinity, hi = - Infinity;
|
|
307
|
+
for ( let k = 0; k < count; k ++ ) {
|
|
308
|
+
|
|
309
|
+
lo = Math.min( lo, boxes[ k * 6 + a ] );
|
|
310
|
+
hi = Math.max( hi, boxes[ k * 6 + 3 + a ] );
|
|
311
|
+
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if ( out ) {
|
|
315
|
+
|
|
316
|
+
out[ outOff + a ] = lo;
|
|
317
|
+
out[ outOff + 3 + a ] = hi;
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Every copy hidden (a group's members all are): the far point box no ray enters.
|
|
322
|
+
if ( ! ( lo <= hi ) ) {
|
|
323
|
+
|
|
324
|
+
data[ o + BASE_SLOT[ a ] ] = BVH_EMPTY_BOX;
|
|
325
|
+
idx[ o + 6 + a ] = 0;
|
|
326
|
+
idx[ o + 9 + a ] = 0;
|
|
327
|
+
continue;
|
|
328
|
+
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const pad = f32Pad( lo, hi );
|
|
332
|
+
const base = f32Down( lo - pad );
|
|
333
|
+
let e = Math.max( - 126, Math.ceil( Math.log2( ( hi + pad - base ) / 255 ) ) );
|
|
334
|
+
while ( Math.fround( base + 255 * 2 ** e ) < hi + pad ) e ++;
|
|
335
|
+
const step = 2 ** e;
|
|
336
|
+
data[ o + BASE_SLOT[ a ] ] = base;
|
|
337
|
+
exponents |= ( e + 127 ) << ( a * 8 );
|
|
338
|
+
let qLo = 0, qHi = 0;
|
|
339
|
+
for ( let k = 0; k < count; k ++ ) {
|
|
340
|
+
|
|
341
|
+
const kLo = boxes[ k * 6 + a ], kHi = boxes[ k * 6 + 3 + a ];
|
|
342
|
+
const kPad = f32Pad( kLo, kHi );
|
|
343
|
+
qLo |= Math.max( 0, Math.min( 255, Math.floor( ( kLo - kPad - base ) / step ) ) ) << ( k * 8 );
|
|
344
|
+
qHi |= Math.max( 0, Math.min( 255, Math.ceil( ( kHi + kPad - base ) / step ) ) ) << ( k * 8 );
|
|
345
|
+
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
idx[ o + 6 + a ] = qLo >>> 0;
|
|
349
|
+
idx[ o + 9 + a ] = qHi >>> 0;
|
|
350
|
+
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
idx[ o + 1 ] = exponents >>> 0;
|
|
354
|
+
|
|
355
|
+
}
|
|
356
|
+
|
|
249
357
|
/**
|
|
250
358
|
* A triangle leaf of at most this many triangles is folded into its parent: the child's slot ([3]
|
|
251
359
|
* or [7]) holds `~( first << 4 | count )`, the very value traversal pushes, so an inner node is
|
|
@@ -256,6 +364,13 @@ export const BVH_LEAF_MARKERS = {
|
|
|
256
364
|
export const BVH_FOLDED_LEAF_MAX = 15;
|
|
257
365
|
export const BVH_FOLDED_FIRST_LIMIT = 1 << 27;
|
|
258
366
|
|
|
367
|
+
/**
|
|
368
|
+
* A child no ray may enter — a hidden part of a grouped object — is stored as a point box this far out: its distance
|
|
369
|
+
* along any ray exceeds every hit, so it is never pushed. Refits read it back as empty. An inverted box (min > max)
|
|
370
|
+
* will not do: the slab test swaps the planes and the ray enters it everywhere.
|
|
371
|
+
*/
|
|
372
|
+
export const BVH_EMPTY_BOX = 1e30;
|
|
373
|
+
|
|
259
374
|
/** A u32 view over a float buffer, for writing index fields as exact bit patterns. */
|
|
260
375
|
export function bvhIndexView( f32 ) {
|
|
261
376
|
|
|
@@ -241,9 +241,10 @@ export class ChunkedRecords {
|
|
|
241
241
|
|
|
242
242
|
if ( ! this._spilled.has( k ) ) continue;
|
|
243
243
|
const lanes = Math.min( this.recordsPerChunk, this.recordCount - k * this.recordsPerChunk ) * this.lanesPerRecord;
|
|
244
|
-
const bytes = await this._store.read( k, lanes * this.LaneType.BYTES_PER_ELEMENT );
|
|
245
244
|
const chunk = allocChunk( this.LaneType, lanes, this.shared );
|
|
246
|
-
new Uint8Array( chunk.buffer, chunk.byteOffset, chunk.byteLength )
|
|
245
|
+
const target = new Uint8Array( chunk.buffer, chunk.byteOffset, chunk.byteLength );
|
|
246
|
+
if ( this._store.readInto ) await this._store.readInto( k, target );
|
|
247
|
+
else target.set( new Uint8Array( await this._store.read( k, target.byteLength ) ) );
|
|
247
248
|
this.chunks[ k ] = chunk;
|
|
248
249
|
for ( const v of this._liveViews() ) v.chunks[ k ] = new v.LaneType( chunk.buffer, chunk.byteOffset, chunk.byteLength / v.LaneType.BYTES_PER_ELEMENT );
|
|
249
250
|
this._spilled.delete( k );
|
|
@@ -132,8 +132,8 @@ export class EmissiveTriangleBuilder {
|
|
|
132
132
|
// another object's light as soon as any instanced mesh came earlier.
|
|
133
133
|
const placement = table?.placementRunOf?.( meshIndex )?.start ?? - 1;
|
|
134
134
|
const hasInstance = placement >= 0 && table.isSet[ placement ];
|
|
135
|
-
const
|
|
136
|
-
const
|
|
135
|
+
const mo = hasInstance ? table.matrixRow( placement ) : 0;
|
|
136
|
+
const m = hasInstance ? table.rowArray : null;
|
|
137
137
|
const px = ( x, y, z ) => ( m ? m[ mo ] * x + m[ mo + 4 ] * y + m[ mo + 8 ] * z + m[ mo + 12 ] : x );
|
|
138
138
|
const py = ( x, y, z ) => ( m ? m[ mo + 1 ] * x + m[ mo + 5 ] * y + m[ mo + 9 ] * z + m[ mo + 13 ] : y );
|
|
139
139
|
const pz = ( x, y, z ) => ( m ? m[ mo + 2 ] * x + m[ mo + 6 ] * y + m[ mo + 10 ] * z + m[ mo + 14 ] : z );
|
|
@@ -231,8 +231,6 @@ export class EmissiveTriangleBuilder {
|
|
|
231
231
|
const chunkedF = chunked ? chunked.viewAs( Float32Array ) : null;
|
|
232
232
|
const flatF = chunked ? null : new Float32Array( triangleData.buffer, triangleData.byteOffset, triangleData.length );
|
|
233
233
|
const FLOATS_PER_TRIANGLE = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;
|
|
234
|
-
const m = table.world;
|
|
235
|
-
|
|
236
234
|
this.totalEmissivePower = 0;
|
|
237
235
|
|
|
238
236
|
for ( const tri of this.emissiveTriangles ) {
|
|
@@ -245,7 +243,7 @@ export class EmissiveTriangleBuilder {
|
|
|
245
243
|
const placement = table.placementRunOf?.( tri.meshIndex )?.start ?? - 1;
|
|
246
244
|
if ( placement >= 0 && table.isSet[ placement ] ) {
|
|
247
245
|
|
|
248
|
-
const o = placement
|
|
246
|
+
const o = table.matrixRow( placement ), m = table.rowArray;
|
|
249
247
|
for ( let k = 0; k < 9; k += 3 ) {
|
|
250
248
|
|
|
251
249
|
const x = v[ k ], y = v[ k + 1 ], z = v[ k + 2 ];
|
|
@@ -671,7 +669,9 @@ export class EmissiveTriangleBuilder {
|
|
|
671
669
|
this.lightBVHNodeData[ 14 ] = 1.0; // cone axis z
|
|
672
670
|
this.lightBVHNodeData[ 15 ] = - 1.0; // cosThetaO = whole sphere
|
|
673
671
|
this.lightBVHNodeCount = 1;
|
|
674
|
-
|
|
672
|
+
// Shade reads the map only while emissiveTriangleCount > 0: one over every triangle was 446 MB on the Moana
|
|
673
|
+
// island, in the tab, on the GPU and in Chrome's upload pool.
|
|
674
|
+
this.emissiveBitTrailMap = new Float32Array( 4 ).fill( - 1 );
|
|
675
675
|
this.emissiveIndicesArray = new Int32Array( 0 );
|
|
676
676
|
this.emissivePowerArray = new Float32Array( 0 );
|
|
677
677
|
this._buildCDF();
|