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
package/src/TSL/patches.js
CHANGED
|
@@ -147,6 +147,90 @@ export function uploadStorageChunks( renderer, attr, chunks ) {
|
|
|
147
147
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/**
|
|
151
|
+
* GPU-only storage attributes standing for one array too large for a single binding: lane l lives in part
|
|
152
|
+
* ⌊l / lanesPerPart⌋, and a part holds whole records, so no record straddles two. Read through `splitStorage`
|
|
153
|
+
* (TSL/Common.js).
|
|
154
|
+
*/
|
|
155
|
+
export class StorageParts {
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {number} totalLanes
|
|
159
|
+
* @param {number} lanesPerRecord
|
|
160
|
+
* @param {number} maxPartBytes - at most maxStorageBufferBindingSize
|
|
161
|
+
* @param {Function} [LaneType]
|
|
162
|
+
* @param {number} [itemSize] - lanes an element: 4 for vec4 storage, 2 for vec2
|
|
163
|
+
*/
|
|
164
|
+
constructor( totalLanes, lanesPerRecord, maxPartBytes, LaneType = Float32Array, itemSize = 4 ) {
|
|
165
|
+
|
|
166
|
+
this.LaneType = LaneType;
|
|
167
|
+
this.itemSize = itemSize;
|
|
168
|
+
this.lanesPerPart = Math.max( 1, Math.floor( maxPartBytes / ( lanesPerRecord * LaneType.BYTES_PER_ELEMENT ) ) ) * lanesPerRecord;
|
|
169
|
+
this.totalLanes = totalLanes;
|
|
170
|
+
this.attrs = [];
|
|
171
|
+
for ( let at = 0; at < totalLanes || this.attrs.length === 0; at += this.lanesPerPart ) {
|
|
172
|
+
|
|
173
|
+
const lanes = Math.max( lanesPerRecord, Math.min( this.lanesPerPart, totalLanes - at ) );
|
|
174
|
+
this.attrs.push( gpuOnlyStorageAttribute( lanes / itemSize, itemSize, LaneType ) );
|
|
175
|
+
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Elements a full part holds. */
|
|
181
|
+
get elementsPerPart() {
|
|
182
|
+
|
|
183
|
+
return this.lanesPerPart / this.itemSize;
|
|
184
|
+
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Creates every part's GPU buffer now. */
|
|
188
|
+
create( backend ) {
|
|
189
|
+
|
|
190
|
+
for ( const attr of this.attrs ) backend.createStorageAttribute( attr );
|
|
191
|
+
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Writes `count` lanes of `src` from `srcOffset` to lane `lane`, across a part boundary if need be. */
|
|
195
|
+
write( backend, lane, src, srcOffset, count ) {
|
|
196
|
+
|
|
197
|
+
const bytes = this.LaneType.BYTES_PER_ELEMENT;
|
|
198
|
+
while ( count > 0 ) {
|
|
199
|
+
|
|
200
|
+
const p = Math.floor( lane / this.lanesPerPart );
|
|
201
|
+
const local = lane - p * this.lanesPerPart;
|
|
202
|
+
const n = Math.min( count, this.lanesPerPart - local );
|
|
203
|
+
const attr = this.attrs[ p ];
|
|
204
|
+
backend.createStorageAttribute( attr );
|
|
205
|
+
backend.device.queue.writeBuffer( backend.get( attr ).buffer, local * bytes, src, srcOffset, n );
|
|
206
|
+
lane += n;
|
|
207
|
+
srcOffset += n;
|
|
208
|
+
count -= n;
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Writes lanes [startLane, startLane + laneCount) of chunked `records`. */
|
|
215
|
+
writeRecords( backend, records, startLane, laneCount ) {
|
|
216
|
+
|
|
217
|
+
const lanesPerChunk = records.recordsPerChunk * records.lanesPerRecord;
|
|
218
|
+
for ( let lane = startLane, end = startLane + laneCount; lane < end; ) {
|
|
219
|
+
|
|
220
|
+
const ci = Math.floor( lane / lanesPerChunk );
|
|
221
|
+
const chunk = records.chunks[ ci ];
|
|
222
|
+
if ( ! chunk ) throw new Error( `chunk ${ci} is on disk and was never uploaded` );
|
|
223
|
+
const local = lane - ci * lanesPerChunk;
|
|
224
|
+
const n = Math.min( chunk.length - local, end - lane );
|
|
225
|
+
this.write( backend, lane, chunk, local, n );
|
|
226
|
+
lane += n;
|
|
227
|
+
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
|
|
150
234
|
/**
|
|
151
235
|
* Re-upload one lane range of a chunked storage attribute, spanning chunks as needed.
|
|
152
236
|
*
|
|
@@ -13,7 +13,7 @@ import { LIGHT_FLOATS } from '../managers/UniformManager.js';
|
|
|
13
13
|
import { HIT_STRIDE_BIDIRECTIONAL, LIGHT_VERTEX_STRIDE, freeStorageAttribute } from '../Processor/PackedRayBuffer.js';
|
|
14
14
|
import { COUNTER, ENERGY_SCALE } from '../Processor/QueueManager.js';
|
|
15
15
|
import { CAMERA_PROJECTION_IDS } from '../EngineDefaults.js';
|
|
16
|
-
import { BVH_MAX_INDEX } from '../Processor/BufferLayout.js';
|
|
16
|
+
import { BVH_MAX_INDEX, BVH_LEAF_MARKERS, CLUSTER_COUNT_SHIFT, CLUSTER_COPY_HIDDEN } from '../Processor/BufferLayout.js';
|
|
17
17
|
import { BOUNCE_KERNELS, LIST_WG_SIZE } from '../Stages/PathTracer.js';
|
|
18
18
|
import { buildLightGenerateKernel, LIGHT_GENERATE_WG_SIZE } from '../TSL/LightGenerateKernel.js';
|
|
19
19
|
import { buildGuideKernel, buildGuideClearKernel, guidedDiscPdf, recordEscape, GUIDE_BINS, GUIDE_ROW_STRIDE, GUIDE_TEXTURE_WIDTH } from '../TSL/LightGuide.js';
|
|
@@ -652,6 +652,14 @@ export class BidirectionalIntegrator {
|
|
|
652
652
|
const records = this.pt._bvhRecords;
|
|
653
653
|
const flat = records ? null : this.pt.bvhStorageAttr?.array;
|
|
654
654
|
if ( ! records && ! ( flat?.length >= 16 ) ) return null;
|
|
655
|
+
// A spilled TLAS comes back for the next frame's disc.
|
|
656
|
+
const blasBase = this.pt._instanceTable?.blasBase ?? 1;
|
|
657
|
+
if ( records && ! records.isResident( 0, blasBase ) ) {
|
|
658
|
+
|
|
659
|
+
this.pt._whenTLASResident( () => {} );
|
|
660
|
+
return null;
|
|
661
|
+
|
|
662
|
+
}
|
|
655
663
|
|
|
656
664
|
const view = ( f ) => {
|
|
657
665
|
|
|
@@ -668,6 +676,16 @@ export class BidirectionalIntegrator {
|
|
|
668
676
|
|
|
669
677
|
};
|
|
670
678
|
|
|
679
|
+
// A copy cluster is visible while any of its copies is (BufferLayout CLUSTER_LEAF).
|
|
680
|
+
const leafVisible = ( n ) => {
|
|
681
|
+
|
|
682
|
+
if ( n.u[ n.o + 3 ] !== BVH_LEAF_MARKERS.CLUSTER_LEAF ) return n.f[ n.o + 2 ] !== 0;
|
|
683
|
+
const count = ( ( n.u[ n.o ] >>> CLUSTER_COUNT_SHIFT ) & 3 ) + 1;
|
|
684
|
+
for ( let k = 0; k < count; k ++ ) if ( ! ( n.u[ n.o + 12 + k ] & CLUSTER_COPY_HIDDEN ) ) return true;
|
|
685
|
+
return false;
|
|
686
|
+
|
|
687
|
+
};
|
|
688
|
+
|
|
671
689
|
const root = node( 0 );
|
|
672
690
|
if ( root.u[ root.o + 3 ] >= BVH_MAX_INDEX ) return null;
|
|
673
691
|
// A tree that reaches a node twice is malformed; no search visits more nodes than the tree holds.
|
|
@@ -699,7 +717,7 @@ export class BidirectionalIntegrator {
|
|
|
699
717
|
const n = node( index );
|
|
700
718
|
if ( n.u[ n.o + 3 ] >= BVH_MAX_INDEX ) {
|
|
701
719
|
|
|
702
|
-
if (
|
|
720
|
+
if ( leafVisible( n ) ) best = s;
|
|
703
721
|
|
|
704
722
|
} else push( n );
|
|
705
723
|
|
|
@@ -988,10 +988,10 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
988
988
|
* @param {Float32Array} world - the placement matrix pool
|
|
989
989
|
* @param {number} offset - where this placement's matrix starts in it
|
|
990
990
|
*/
|
|
991
|
-
notePlacementMoving( leaf,
|
|
991
|
+
notePlacementMoving( leaf, matrixOf ) {
|
|
992
992
|
|
|
993
993
|
if ( leaf < 0 || ! this.historyActive || this._movedPlacements.has( leaf ) ) return;
|
|
994
|
-
this._movedPlacements.set( leaf, {
|
|
994
|
+
this._movedPlacements.set( leaf, { matrixOf, prev: matrixOf().slice() } );
|
|
995
995
|
|
|
996
996
|
}
|
|
997
997
|
|
|
@@ -1013,8 +1013,8 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1013
1013
|
const leaves = [ ...moved.keys() ].sort( ( a, b ) => a - b );
|
|
1014
1014
|
for ( let i = 0; i < leaves.length; i ++ ) {
|
|
1015
1015
|
|
|
1016
|
-
const {
|
|
1017
|
-
this._matB.fromArray(
|
|
1016
|
+
const { matrixOf, prev } = moved.get( leaves[ i ] );
|
|
1017
|
+
this._matB.fromArray( matrixOf() ).invert();
|
|
1018
1018
|
this._matA.fromArray( prev ).multiply( this._matB ).toArray( upload.toPrev, i * 16 );
|
|
1019
1019
|
upload.leaves[ i ] = leaves[ i ];
|
|
1020
1020
|
|
|
@@ -38,12 +38,13 @@ export function focusDistanceOfHit( hit, camera, panorama = false ) {
|
|
|
38
38
|
*/
|
|
39
39
|
export class InteractionManager extends EventDispatcher {
|
|
40
40
|
|
|
41
|
-
constructor( { scene, camera, canvas, assetLoader, pathTracer, floorPlane } ) {
|
|
41
|
+
constructor( { scene, camera, canvas, assetLoader, pathTracer, floorPlane, isGeometryOnDisk = null } ) {
|
|
42
42
|
|
|
43
43
|
super();
|
|
44
44
|
|
|
45
45
|
// Core dependencies
|
|
46
46
|
this.scene = scene;
|
|
47
|
+
this.isGeometryOnDisk = isGeometryOnDisk;
|
|
47
48
|
this.camera = camera;
|
|
48
49
|
this.canvas = canvas;
|
|
49
50
|
this.assetLoader = assetLoader;
|
|
@@ -125,7 +126,8 @@ export class InteractionManager extends EventDispatcher {
|
|
|
125
126
|
select( object ) {
|
|
126
127
|
|
|
127
128
|
const outline = this._overlayManager?.getHelper( 'outline' );
|
|
128
|
-
|
|
129
|
+
// A spilled model's geometry is on disk: there is nothing to draw the outline from.
|
|
130
|
+
if ( outline ) outline.setSelectedObjects( object && ! this.isGeometryOnDisk?.() ? [ object ] : [] );
|
|
129
131
|
|
|
130
132
|
this.selectedObject = object || null;
|
|
131
133
|
|
|
@@ -221,7 +223,7 @@ export class InteractionManager extends EventDispatcher {
|
|
|
221
223
|
const mouseCoords = this.getMouseCoordinates( event );
|
|
222
224
|
this.raycaster.setFromCamera( mouseCoords, this.camera );
|
|
223
225
|
|
|
224
|
-
const intersects = this.
|
|
226
|
+
const intersects = this._intersectScene();
|
|
225
227
|
|
|
226
228
|
if ( intersects.length > 0 ) {
|
|
227
229
|
|
|
@@ -490,7 +492,7 @@ export class InteractionManager extends EventDispatcher {
|
|
|
490
492
|
const mouseCoords = this.getMouseCoordinates( event );
|
|
491
493
|
this.raycaster.setFromCamera( mouseCoords, this.camera );
|
|
492
494
|
|
|
493
|
-
const intersects = this.
|
|
495
|
+
const intersects = this._intersectScene();
|
|
494
496
|
const validIntersects = this.filterValidIntersects( intersects );
|
|
495
497
|
const lightHit = this._pickLightHelper();
|
|
496
498
|
|
|
@@ -576,7 +578,7 @@ export class InteractionManager extends EventDispatcher {
|
|
|
576
578
|
const mouseCoords = this.getMouseCoordinates( event );
|
|
577
579
|
this.raycaster.setFromCamera( mouseCoords, this.camera );
|
|
578
580
|
|
|
579
|
-
const intersects = this.
|
|
581
|
+
const intersects = this._intersectScene();
|
|
580
582
|
const validIntersects = this.filterValidIntersects( intersects );
|
|
581
583
|
|
|
582
584
|
if ( validIntersects.length > 0 ) {
|
|
@@ -695,11 +697,22 @@ export class InteractionManager extends EventDispatcher {
|
|
|
695
697
|
|
|
696
698
|
}
|
|
697
699
|
|
|
700
|
+
/**
|
|
701
|
+
* The scene under the raycaster. Nothing while a spilled model's geometry is on disk: its arrays are empty, and
|
|
702
|
+
* testing tens of millions of empty copies held the page for seconds a pick.
|
|
703
|
+
* @private
|
|
704
|
+
*/
|
|
705
|
+
_intersectScene() {
|
|
706
|
+
|
|
707
|
+
return this.isGeometryOnDisk?.() ? [] : this.raycaster.intersectObjects( this.scene.children, true );
|
|
708
|
+
|
|
709
|
+
}
|
|
710
|
+
|
|
698
711
|
/** The nearest scene surface under an NDC point, skipping the floor and helpers. */
|
|
699
712
|
pickSurface( ndcX, ndcY ) {
|
|
700
713
|
|
|
701
714
|
this.raycaster.setFromCamera( { x: ndcX, y: ndcY }, this.camera );
|
|
702
|
-
return this.filterValidIntersects( this.
|
|
715
|
+
return this.filterValidIntersects( this._intersectScene() )[ 0 ] ?? null;
|
|
703
716
|
|
|
704
717
|
}
|
|
705
718
|
|
|
@@ -173,8 +173,17 @@ export class MaterialDataManager {
|
|
|
173
173
|
this._convertColorsToWorkingSpace( matImageData );
|
|
174
174
|
|
|
175
175
|
const vec4Count = matImageData.length / 4;
|
|
176
|
+
this.materialCount = Math.floor( vec4Count / PIXELS_PER_MATERIAL );
|
|
177
|
+
|
|
178
|
+
// The owning stage keeps materials at the front of its scene data buffer, the light data behind them: one
|
|
179
|
+
// binding for both, which the shading kernel's ten could not spare.
|
|
180
|
+
const shared = this.callbacks.adoptMaterials?.( matImageData );
|
|
181
|
+
if ( shared ) {
|
|
176
182
|
|
|
177
|
-
|
|
183
|
+
this.materialStorageAttr = shared.attr;
|
|
184
|
+
this.materialStorageNode = shared.node;
|
|
185
|
+
|
|
186
|
+
} else if ( this.materialStorageNode ) {
|
|
178
187
|
|
|
179
188
|
this.materialStorageAttr = new StorageInstancedBufferAttribute( matImageData, 4 );
|
|
180
189
|
this.materialStorageNode.value = this.materialStorageAttr;
|
|
@@ -187,7 +196,6 @@ export class MaterialDataManager {
|
|
|
187
196
|
|
|
188
197
|
}
|
|
189
198
|
|
|
190
|
-
this.materialCount = Math.floor( vec4Count / PIXELS_PER_MATERIAL );
|
|
191
199
|
this._sources = [ ...sources ];
|
|
192
200
|
this._hostSet = [];
|
|
193
201
|
this.callbacks.onMaterialFeaturesChanged?.();
|
|
@@ -195,6 +203,15 @@ export class MaterialDataManager {
|
|
|
195
203
|
|
|
196
204
|
}
|
|
197
205
|
|
|
206
|
+
/** Sends the materials to the GPU: only their region when the buffer carries the light data too. @private */
|
|
207
|
+
_uploadMaterials() {
|
|
208
|
+
|
|
209
|
+
const attr = this.materialStorageAttr;
|
|
210
|
+
if ( this.callbacks.adoptMaterials ) attr.addUpdateRange( 0, this.materialCount * PIXELS_PER_MATERIAL * 4 );
|
|
211
|
+
attr.needsUpdate = true;
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
|
|
198
215
|
/**
|
|
199
216
|
* Get the material storage attribute (for dependent stages).
|
|
200
217
|
* @returns {StorageInstancedBufferAttribute|null}
|
|
@@ -538,7 +555,7 @@ export class MaterialDataManager {
|
|
|
538
555
|
|
|
539
556
|
}
|
|
540
557
|
|
|
541
|
-
this.
|
|
558
|
+
this._uploadMaterials();
|
|
542
559
|
( this._hostSet[ materialIndex ] ??= new Map() ).set( property, toPortable( value ) );
|
|
543
560
|
|
|
544
561
|
// Recompute triangle-data opaque-blocker flag when any input to it changes.
|
|
@@ -588,7 +605,7 @@ export class MaterialDataManager {
|
|
|
588
605
|
this._recomputeOpaqueBlockerForMaterial( materialIndex );
|
|
589
606
|
this.callbacks.onMaterialFeaturesChanged?.();
|
|
590
607
|
|
|
591
|
-
this.
|
|
608
|
+
this._uploadMaterials();
|
|
592
609
|
this._notifyReset();
|
|
593
610
|
|
|
594
611
|
}
|
|
@@ -668,7 +685,7 @@ export class MaterialDataManager {
|
|
|
668
685
|
|
|
669
686
|
}
|
|
670
687
|
|
|
671
|
-
this.
|
|
688
|
+
this._uploadMaterials();
|
|
672
689
|
this._notifyReset();
|
|
673
690
|
|
|
674
691
|
}
|
|
@@ -333,6 +333,9 @@ export class UniformManager {
|
|
|
333
333
|
u( 'emissiveTriangleCount', 0, 'int' );
|
|
334
334
|
u( 'emissiveTotalPower', 0.0, 'float' );
|
|
335
335
|
u( 'lightBVHNodeCount', 0, 'int' );
|
|
336
|
+
// Where the light data starts in the scene data buffer, after the materials (vec4 elements). Every light read
|
|
337
|
+
// adds it; the offsets below are within the light data.
|
|
338
|
+
u( 'lightVec4Offset', 0, 'int' );
|
|
336
339
|
// Offset (in vec4 elements) within the packed light buffer where emissive
|
|
337
340
|
// triangle data starts. Equals lightBVHNodeCount * LBVH_STRIDE; computed on upload.
|
|
338
341
|
u( 'emissiveVec4Offset', 0, 'int' );
|