rayzee 7.31.0 → 8.0.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 +164 -0
- package/dist/rayzee.es.js +6547 -4334
- package/dist/rayzee.umd.js +122 -73
- package/package.json +1 -1
- package/src/EngineDefaults.js +168 -13
- package/src/EngineIssues.js +6 -0
- package/src/PathTracerApp.js +128 -20
- package/src/Processor/ArchiveReader.js +538 -0
- package/src/Processor/AssetLoader.js +357 -22
- package/src/Processor/BVHBuilder.js +48 -32
- package/src/Processor/BVHRefitter.js +244 -99
- package/src/Processor/ChunkedRecords.js +302 -0
- package/src/Processor/EmissiveTriangleBuilder.js +48 -21
- package/src/Processor/GeometryExtractor.js +577 -92
- package/src/Processor/HostMemory.js +222 -0
- package/src/Processor/InstanceTable.js +548 -97
- package/src/Processor/PBRT/EqualAreaOctahedral.js +173 -0
- package/src/Processor/PBRT/LoopSubdivision.js +240 -0
- package/src/Processor/PBRT/PBRTCurves.js +249 -0
- package/src/Processor/PBRT/PBRTMath.js +28 -0
- package/src/Processor/PBRT/PBRTParser.js +187 -41
- package/src/Processor/PBRT/PBRTSceneBuilder.js +659 -34
- package/src/Processor/PBRT/PBRTTokenizer.js +205 -65
- package/src/Processor/PBRT/index.js +306 -45
- package/src/Processor/PackedRayBuffer.js +29 -18
- package/src/Processor/ParallelBVHBuilder.js +9 -8
- package/src/Processor/QueueManager.js +5 -3
- package/src/Processor/SceneProcessor.js +1063 -366
- package/src/Processor/TLASBuilder.js +546 -201
- package/src/Processor/TextureCreator.js +11 -9
- package/src/Processor/VRAMTracker.js +12 -3
- package/src/Processor/Workers/BVHRefitWorker.js +16 -11
- package/src/Processor/Workers/BVHWorker.js +12 -12
- package/src/Processor/Workers/TLASWorker.js +37 -0
- package/src/Stages/NormalDepth.js +6 -2
- package/src/Stages/PathTracer.js +5 -5
- package/src/Stages/PathTracerStage.js +163 -41
- package/src/TSL/BVHTraversal.js +208 -90
- package/src/TSL/Common.js +135 -2
- package/src/TSL/Displacement.js +21 -11
- package/src/TSL/EmissiveSampling.js +49 -25
- package/src/TSL/ExtendKernel.js +3 -2
- package/src/TSL/LightBVHSampling.js +7 -4
- package/src/TSL/LightsDirect.js +30 -29
- package/src/TSL/ShadeKernel.js +8 -6
- package/src/TSL/Struct.js +3 -0
- package/src/TSL/TextureSampling.js +24 -9
- package/src/TSL/patches.js +128 -3
- package/src/index.js +3 -0
- package/src/managers/AnimationManager.js +86 -76
- package/src/managers/CameraManager.js +11 -0
- package/src/managers/MaterialDataManager.js +29 -23
- package/src/managers/TransformManager.js +9 -192
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@ A real-time WebGPU path tracing engine built on Three.js. Framework-agnostic —
|
|
|
30
30
|
- [engine.denoisingManager](#enginedenoisingmanager)
|
|
31
31
|
- [engine.interactionManager](#engineinteractionmanager)
|
|
32
32
|
- [engine.transformManager](#enginetransformmanager)
|
|
33
|
+
- [Moving and Deforming Objects](#moving-and-deforming-objects)
|
|
34
|
+
- [Degradation contract](#degradation-contract)
|
|
33
35
|
- [Output Methods](#output-methods)
|
|
34
36
|
- [Render Resolution Reserve](#render-resolution-reserve)
|
|
35
37
|
- [Memory Monitoring](#memory-monitoring)
|
|
@@ -309,6 +311,9 @@ const engine = new PathTracerApp(canvas, options?)
|
|
|
309
311
|
| `canvas` | `HTMLCanvasElement` | Rendering target |
|
|
310
312
|
| `options.autoResize` | `boolean` | Auto-resize on window resize (default: `true`) |
|
|
311
313
|
| `options.container` | `HTMLElement` | Single DOM parent the engine mounts auxiliary elements into — HUD overlay (tile borders, helpers) and denoiser canvas. Defaults to `canvas.parentNode`. |
|
|
314
|
+
| `options.strict` | `boolean` | Throw an `EngineIssueError` where the engine would otherwise degrade and carry on (default: `false`). See [Degradation contract](#degradation-contract). |
|
|
315
|
+
| `options.profile` | `string` | `'viewer'` (default) or `'physical'` — product tuning that is not a physical constant: area-light scale, environment rotation, tone mapping, saturation. An unknown name throws. |
|
|
316
|
+
| `options.maxSceneBytes` | `number` | Raise or lower the CPU memory ceiling a scene may need before the engine refuses it (default 9,216 MB). See [Memory monitoring](#memory-monitoring). |
|
|
312
317
|
|
|
313
318
|
The engine creates and mounts everything it needs (denoiser canvas, tile/HUD overlay) into a single parent on `init()`. Performance HUDs (e.g. `stats-gl`) are not bundled — listen to `EngineEvents.FRAME` and tick your own panel.
|
|
314
319
|
|
|
@@ -349,6 +354,41 @@ engine.setSceneObjectVisibility(id, visible) // Toggle visi
|
|
|
349
354
|
|
|
350
355
|
`id` is the appended root's `Object3D.uuid`, returned by `addModel`/`addModelFromObject3D`. For `addModelFromObject3D` the engine carries your object's uuid onto its copy, so the id matches the object you passed. The built-in ground plane is permanent and can't be removed.
|
|
351
356
|
|
|
357
|
+
##### Loading part of a scene archive
|
|
358
|
+
|
|
359
|
+
A pbrt-v4 scene archive (`.tar`, `.tar.gz`, `.zip`) is usually a root `.pbrt` file that includes one
|
|
360
|
+
subtree per element, and the whole thing rarely fits in a browser tab — Moana is 29 GB unpacked.
|
|
361
|
+
The archive can be inspected without retaining any of it, then loaded one element at a time:
|
|
362
|
+
|
|
363
|
+
```js
|
|
364
|
+
const { kind, root, elements, entryCount, totalBytes } = await engine.inspectArchive(file);
|
|
365
|
+
|
|
366
|
+
await engine.loadFile(file, { element: elements[0].path }); // one element
|
|
367
|
+
await engine.loadFile(file, { element: [ a.path, b.path ] }); // several together
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Everything above a chosen element comes along — the root scene file, the material library, an
|
|
371
|
+
ancestor's `textures` folder — and an `Include` pointing at an element you left out only warns,
|
|
372
|
+
which is what makes a partial load work. Selecting every element is a valid answer and loads the
|
|
373
|
+
whole scene.
|
|
374
|
+
|
|
375
|
+
Past 4 GB unpacked, a multi-element archive **throws** `ARCHIVE_NEEDS_ELEMENT` rather than taking
|
|
376
|
+
all of it. The error carries the element list, so a host can turn it into a picker:
|
|
377
|
+
|
|
378
|
+
```js
|
|
379
|
+
try {
|
|
380
|
+
await engine.loadFile(file);
|
|
381
|
+
} catch (err) {
|
|
382
|
+
if (err.code === 'ARCHIVE_NEEDS_ELEMENT') showPicker(err.elements, err.root, err.totalBytes);
|
|
383
|
+
else throw err;
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Per-load options for pbrt archives: `promptBytes` moves that 4 GB line, `maxTriangles` (default
|
|
388
|
+
45M) and `maxPlacements` (default 6M) cap the build — past either, placements are skipped and the
|
|
389
|
+
build reports itself truncated. 45M is the highest rung measured to survive; raising it is a
|
|
390
|
+
deliberate act on a fresh browser tab.
|
|
391
|
+
|
|
352
392
|
#### Settings
|
|
353
393
|
|
|
354
394
|
```js
|
|
@@ -575,6 +615,88 @@ engine.transformManager.setSpace('world') // 'world' | 'local'
|
|
|
575
615
|
engine.transformManager.controls // Access the underlying TransformControls
|
|
576
616
|
```
|
|
577
617
|
|
|
618
|
+
### Moving and Deforming Objects
|
|
619
|
+
|
|
620
|
+
Three calls update a loaded scene without rebuilding it. They differ in how much work they do, and
|
|
621
|
+
picking the wrong one is the usual source of trouble.
|
|
622
|
+
|
|
623
|
+
```js
|
|
624
|
+
engine.updateMeshTransforms(meshIndices) // an object moved, rotated or scaled
|
|
625
|
+
engine.refitBLASes(meshIndices, positions) // specific objects' vertices changed
|
|
626
|
+
await engine.refitBVH(positions) // the whole scene is posed anew, e.g. animation
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
**`updateMeshTransforms` is the one a gizmo drag wants.** Triangles are stored in each object's own
|
|
630
|
+
space, so a rigid move only rewrites a matrix — no vertex pass, no geometry upload. Using
|
|
631
|
+
`refitBLASes` for a move instead rewrites vertices needlessly, and drags along any other object
|
|
632
|
+
sharing the same geometry.
|
|
633
|
+
|
|
634
|
+
All three take **indices into `engine.sceneMeshes`**, which is a depth-first walk of the rendered
|
|
635
|
+
scene and includes the engine's own hidden ground disk. Build your index list from that array, never
|
|
636
|
+
from your own model root, or the two orders silently disagree.
|
|
637
|
+
|
|
638
|
+
Positions are **world space**, 9 floats per triangle (`ax,ay,az, bx,by,bz, cx,cy,cz`), triangles in
|
|
639
|
+
index order. Two shapes are accepted:
|
|
640
|
+
|
|
641
|
+
```js
|
|
642
|
+
// Preferred: a per-mesh callback, asked for one mesh at a time. You may hand back the same
|
|
643
|
+
// scratch buffer on every call.
|
|
644
|
+
await engine.refitBVH((meshIndex, triCount) => myPositionsFor(meshIndex));
|
|
645
|
+
|
|
646
|
+
// Also works: one array for every triangle in the scene, meshes in sceneMeshes order.
|
|
647
|
+
// 1,030 MB at 30M triangles, and will not allocate at that size — prefer the callback.
|
|
648
|
+
await engine.refitBVH(sceneWideFloat32Array);
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
Both shapes are length-checked and throw on a mismatch. Before that check existed, a short buffer
|
|
652
|
+
wrote NaN through every bounding box with no error and the scene simply vanished.
|
|
653
|
+
|
|
654
|
+
An object that shares its geometry with another cannot be deformed — writing its vertices would
|
|
655
|
+
move every copy. `refitBLASes` skips such a mesh and records a `refit.shared_geometry` issue.
|
|
656
|
+
Anything skinned or morphed is given triangles of its own at load, so this only fires when the
|
|
657
|
+
wrong mesh was handed over.
|
|
658
|
+
|
|
659
|
+
---
|
|
660
|
+
|
|
661
|
+
### Degradation contract
|
|
662
|
+
|
|
663
|
+
The engine degrades rather than fails, which is right for a viewer and backwards for a batch
|
|
664
|
+
renderer, so one option decides which you get:
|
|
665
|
+
|
|
666
|
+
```js
|
|
667
|
+
const engine = new PathTracerApp(canvas, { strict: true }); // throw at the point of degradation
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
Lenient hosts read the log instead:
|
|
671
|
+
|
|
672
|
+
```js
|
|
673
|
+
engine.issues // every recorded issue, newest last
|
|
674
|
+
engine.issueErrors // just the ones a strict host would have thrown on
|
|
675
|
+
engine.addEventListener(EngineEvents.ISSUE, ({ issue }) => report(issue));
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
Each issue carries `{ code, message, detail, severity, at }`. `ISSUE_CODES` is **add-only API
|
|
679
|
+
surface** — pin a version and branch on the strings; they are never renamed or repurposed.
|
|
680
|
+
|
|
681
|
+
| Code | Raised when |
|
|
682
|
+
|---|---|
|
|
683
|
+
| `adapter.software` | the GPU is a software rasteriser (SwiftShader, llvmpipe, lavapipe, WARP) |
|
|
684
|
+
| `asset.unreachable` / `asset.ambiguous_entry` | the asset could not be fetched, or an archive held several candidate models |
|
|
685
|
+
| `asset.archive_too_large` / `asset.entry_too_large` | an archive or one of its entries exceeded the byte budget |
|
|
686
|
+
| `texture.build_failed` / `texture.processing_fallback` / `texture.limit_exceeded` | a texture could not be built, fell back to a slower path, or exceeded the per-map-type cap |
|
|
687
|
+
| `environment.load_failed` | the environment map failed to load |
|
|
688
|
+
| `setting.unknown_key` | a setting name reached no stage — how a typo becomes a wrong image |
|
|
689
|
+
| `render.size_declined` / `render.reserve_capped` | the requested render size or reserve exceeded device limits |
|
|
690
|
+
| `stage.render_failed` | a pipeline stage threw (recorded once per stage and phase) |
|
|
691
|
+
| `scene.memory_budget` | the scene needs more CPU memory than is safe, or more than is possible |
|
|
692
|
+
| `emissive.instances_collapsed` | an emissive instanced mesh was too large to expand, so its copies light the scene as one |
|
|
693
|
+
| `refit.shared_geometry` | a deform was asked for on a mesh that shares its triangles, and was skipped |
|
|
694
|
+
|
|
695
|
+
`settings.getEffective()` is the companion for the `setting.unknown_key` case: it returns every live
|
|
696
|
+
setting as `{ value, source, routed }`, and `routed: false` means stored but reaching no stage.
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
578
700
|
### Output Methods
|
|
579
701
|
|
|
580
702
|
Canvas output, screenshots, and scene statistics — accessed as direct methods on the engine.
|
|
@@ -649,6 +771,33 @@ engine.vram.getReport(); // formatted one-line summary string
|
|
|
649
771
|
|
|
650
772
|
The React app surfaces this as a `Memory: … | Peak: …` readout in the on-canvas stats overlay.
|
|
651
773
|
|
|
774
|
+
#### CPU memory
|
|
775
|
+
|
|
776
|
+
The wall a large scene hits is not VRAM, it is contiguous `ArrayBuffer` address space on the CPU —
|
|
777
|
+
and how much of it a browser can still hand out falls as the tab stays up, so the same scene can
|
|
778
|
+
load after a restart and fail after a long session.
|
|
779
|
+
|
|
780
|
+
```js
|
|
781
|
+
const { preflight, allocatedBytes, peakLiveBytes, byPhase, samples } = engine.getHostMemoryInfo();
|
|
782
|
+
// null until a scene has been built
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
⚠️ Do not use `performance.memory.usedJSHeapSize` for this. It does not count `SharedArrayBuffer`,
|
|
786
|
+
and the triangle and node stores are SAB-backed, so the browser's own reading under-reports a large
|
|
787
|
+
scene by gigabytes.
|
|
788
|
+
|
|
789
|
+
Before extraction the engine prices the scene and applies two lines, both recording
|
|
790
|
+
`scene.memory_budget`:
|
|
791
|
+
|
|
792
|
+
| Estimate | What happens |
|
|
793
|
+
|---|---|
|
|
794
|
+
| above ~7,040 MB | warns, and builds anyway |
|
|
795
|
+
| above ~9,216 MB | **throws** — past this the renderer process is killed rather than throwing an error you could catch, so refusing early is the only useful answer |
|
|
796
|
+
|
|
797
|
+
Raise or lower the hard line with `new PathTracerApp(canvas, { maxSceneBytes })`. The estimate runs
|
|
798
|
+
low at the very top of its range, so the per-load `maxTriangles` cap (45M) is the more reliable
|
|
799
|
+
guard on a scene of that size.
|
|
800
|
+
|
|
652
801
|
---
|
|
653
802
|
|
|
654
803
|
### Logging
|
|
@@ -828,6 +977,21 @@ import {
|
|
|
828
977
|
// VRAM accounting (VRAMTracker is also reachable as engine.vram)
|
|
829
978
|
import { VRAMTracker, bufferBytes, textureBytes } from 'rayzee';
|
|
830
979
|
|
|
980
|
+
// Degradation contract — see above. ISSUE_CODES is add-only; pin a version and branch on it.
|
|
981
|
+
import { ISSUE_CODES, ISSUE_SEVERITY, IssueLog, EngineIssueError } from 'rayzee';
|
|
982
|
+
|
|
983
|
+
// CPU memory: price a scene before loading it, or measure what can still be placed
|
|
984
|
+
import {
|
|
985
|
+
MemoryLedger,
|
|
986
|
+
estimateSceneBytes,
|
|
987
|
+
probeAddressSpace,
|
|
988
|
+
SAFE_SCENE_BYTES,
|
|
989
|
+
MAX_SCENE_BYTES,
|
|
990
|
+
} from 'rayzee';
|
|
991
|
+
|
|
992
|
+
// Adapter description — flags software rasterisers (SwiftShader, llvmpipe, lavapipe, WARP)
|
|
993
|
+
import { describeAdapter } from 'rayzee';
|
|
994
|
+
|
|
831
995
|
// Dev-only: texture-binding aliasing guard. Two TextureNodes still holding the default
|
|
832
996
|
// EmptyTexture when a kernel is first compiled can share one GPU binding — nothing throws,
|
|
833
997
|
// the aliased node just reads someone else's texture. Off by default; costs a per-stage
|