rayzee 8.8.2 → 9.1.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 +210 -20
- package/dist/{NeuralRetouch-CYOJ21Wn.js → NeuralRetouch-BRquqfmZ.js} +1 -1
- package/dist/{NeuralSuperRes-Dc6jRYke.js → NeuralSuperRes-CbNvQjCw.js} +5 -2
- package/dist/{ToneMapGPU-BVE852iN.js → ToneMapGPU-DpUZDMbQ.js} +2 -1
- package/dist/rayzee.es.js +10795 -5324
- package/dist/rayzee.umd.js +279 -268
- package/package.json +1 -1
- package/src/AssetConfig.js +4 -0
- package/src/EngineDefaults.js +60 -76
- package/src/EngineEvents.js +7 -0
- package/src/EngineIssues.js +6 -0
- package/src/Headless.js +3 -1
- package/src/Passes/AIUpscaler.js +2 -1
- package/src/Passes/OIDNDenoiser.js +3 -6
- package/src/Passes/OIDNTemporalHistory.js +25 -7
- package/src/PathTracerApp.js +313 -25
- package/src/Processor/ArchiveCache.js +120 -0
- package/src/Processor/ArchiveReader.js +179 -32
- package/src/Processor/AssetLoader.js +523 -76
- package/src/Processor/AtmosphereModel.js +465 -0
- package/src/Processor/BVHBuilder.js +4 -1
- package/src/Processor/BVHLeafFold.js +100 -0
- package/src/Processor/BVHRefitter.js +98 -214
- package/src/Processor/ChunkedRecords.js +189 -4
- package/src/Processor/EmissiveTriangleBuilder.js +8 -0
- package/src/Processor/GeometryExtractor.js +238 -40
- package/src/Processor/InstanceTable.js +55 -37
- package/src/Processor/PBRT/PBRTCurves.js +104 -27
- package/src/Processor/PBRT/PBRTParser.js +132 -22
- package/src/Processor/PBRT/PBRTSceneBuilder.js +210 -55
- package/src/Processor/PBRT/buffers.js +27 -0
- package/src/Processor/PBRT/index.js +55 -15
- package/src/Processor/PackedRayBuffer.js +10 -13
- package/src/Processor/ParallelBVHBuilder.js +36 -15
- package/src/Processor/PhysicalSky.js +308 -0
- package/src/Processor/QueueManager.js +1 -0
- package/src/Processor/SceneProcessor.js +1132 -181
- package/src/Processor/SplitBLAS.js +249 -0
- package/src/Processor/SunPosition.js +41 -0
- package/src/Processor/TextureCreator.js +21 -0
- package/src/Processor/Workers/AIUpscalerWorker.js +79 -52
- package/src/Processor/Workers/BVHWorker.js +17 -13
- package/src/Processor/ZipReader.js +207 -0
- package/src/RenderSettings.js +49 -8
- package/src/SceneState/SceneState.js +367 -0
- package/src/SceneState/portable.js +52 -0
- package/src/Stages/MotionVector.js +8 -42
- package/src/Stages/NRD.js +30 -21
- package/src/Stages/NormalDepth.js +12 -23
- package/src/Stages/PathTracer.js +114 -6
- package/src/Stages/PathTracerStage.js +251 -104
- package/src/Storage/BLASCache.js +190 -0
- package/src/Storage/CDFCache.js +77 -0
- package/src/Storage/DownloadCache.js +266 -0
- package/src/Storage/GeometrySpill.js +217 -0
- package/src/Storage/SceneGraphCodec.js +503 -0
- package/src/Storage/SpillStore.js +112 -0
- package/src/Storage/StorageManager.js +934 -0
- package/src/Storage/StorageOps.js +137 -0
- package/src/Storage/StorageWorker.js +37 -0
- package/src/Storage/events.js +29 -0
- package/src/Storage/identity.js +77 -0
- package/src/Storage/index.js +9 -0
- package/src/Storage/inlineTransport.js +24 -0
- package/src/Storage/locks.js +99 -0
- package/src/Storage/openStorage.js +87 -0
- package/src/Storage/sceneCachePolicy.js +12 -0
- package/src/Storage/shared.js +9 -0
- package/src/Storage/transport.js +62 -0
- package/src/TSL/Atmosphere.js +684 -0
- package/src/TSL/BVHTraversal.js +288 -239
- package/src/TSL/CameraRay.js +99 -14
- package/src/TSL/Common.js +11 -2
- package/src/TSL/DebugKernel.js +2 -2
- package/src/TSL/Displacement.js +6 -6
- package/src/TSL/EmissiveSampling.js +5 -5
- package/src/TSL/EnvironmentCDF.js +176 -0
- package/src/TSL/GenerateKernel.js +5 -5
- package/src/TSL/HitFacet.js +4 -4
- package/src/TSL/LightsDirect.js +8 -8
- package/src/TSL/LightsSampling.js +55 -37
- package/src/TSL/Random.js +1 -0
- package/src/TSL/ShadeKernel.js +75 -19
- package/src/TSL/Sun.js +40 -0
- package/src/TSL/TextureSampling.js +6 -7
- package/src/index.js +22 -0
- package/src/managers/CameraManager.js +412 -56
- package/src/managers/DenoisingManager.js +4 -3
- package/src/managers/EnvironmentManager.js +263 -33
- package/src/managers/GoboManager.js +19 -8
- package/src/managers/IESManager.js +3 -3
- package/src/managers/InteractionManager.js +30 -2
- package/src/managers/LightManager.js +98 -5
- package/src/managers/MaterialDataManager.js +22 -2
- package/src/managers/TransformManager.js +26 -0
- package/src/managers/UniformManager.js +11 -7
- package/src/managers/VideoRenderManager.js +38 -17
- package/src/managers/ViewCamera.js +77 -0
- package/src/managers/WalkControls.js +262 -0
- package/src/managers/helpers/OutlineHelper.js +22 -2
- package/src/managers/timeline/CameraTrack.js +235 -0
- package/src/managers/timeline/TimelineManager.js +146 -0
- package/src/version.js +3 -0
- package/src/Processor/ProceduralSky.js +0 -291
package/README.md
CHANGED
|
@@ -22,9 +22,10 @@ A real-time WebGPU path tracing engine built on Three.js. Framework-agnostic —
|
|
|
22
22
|
- [Configuring Assets (CDN URLs & cache namespace)](#configuring-assets-cdn-urls--cache-namespace)
|
|
23
23
|
- [PathTracerApp](#pathtracerapp)
|
|
24
24
|
- [engine.cameraManager](#enginecameramanager)
|
|
25
|
-
- [Camera Projection (360° Panorama)](#camera-projection-360-panorama)
|
|
25
|
+
- [Camera Projection (Orthographic, 360° Panorama)](#camera-projection-orthographic-360-panorama)
|
|
26
26
|
- [engine.lightManager](#enginelightmanager)
|
|
27
27
|
- [engine.animationManager](#engineanimationmanager)
|
|
28
|
+
- [engine.timeline](#enginetimeline)
|
|
28
29
|
- [Materials](#materials)
|
|
29
30
|
- [Colour Management](#colour-management)
|
|
30
31
|
- [engine.environmentManager](#engineenvironmentmanager)
|
|
@@ -293,9 +294,12 @@ configureAssets({
|
|
|
293
294
|
ocioRuntimeFactory: () => import('@bb-studio/ocio'), // or: ocioRuntimeUrl: '/vendor/ocio/index.js'
|
|
294
295
|
ocioWasmUrl: '/vendor/ocio/ocio-wasm.wasm', // optional override for the .wasm
|
|
295
296
|
|
|
296
|
-
//
|
|
297
|
-
// apps embed the engine on the same origin
|
|
297
|
+
// Names the engine's on-disk storage (an OPFS directory). Set a unique value if several
|
|
298
|
+
// apps embed the engine on the same origin, so their caches stay apart.
|
|
298
299
|
cacheNamespace: 'my-app',
|
|
300
|
+
|
|
301
|
+
// On-disk storage: 'auto' (default) where the browser has it, false for none.
|
|
302
|
+
storage: 'auto',
|
|
299
303
|
});
|
|
300
304
|
|
|
301
305
|
const engine = new PathTracerApp(canvas);
|
|
@@ -320,6 +324,8 @@ const engine = new PathTracerApp(canvas, options?)
|
|
|
320
324
|
| `options.strict` | `boolean` | Throw an `EngineIssueError` where the engine would otherwise degrade and carry on (default: `false`). See [Degradation contract](#degradation-contract). |
|
|
321
325
|
| `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. |
|
|
322
326
|
| `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). |
|
|
327
|
+
| `options.storage` | `false \| 'auto' \| StorageManager` | On-disk storage (default: `configureAssets( { storage } )`). A manager you pass stays yours to dispose. See [On-disk storage](#on-disk-storage-opfs). |
|
|
328
|
+
| `options.memorySpill` | `boolean` | Experimental, default `false`: build a large static scene through disk — triangle records, BLAS nodes and the three.js geometry are written out as the build finishes with them — and raise the pbrt triangle and placement caps to 60M / 8M. See [On-disk storage](#on-disk-storage-opfs). |
|
|
323
329
|
|
|
324
330
|
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.
|
|
325
331
|
|
|
@@ -391,10 +397,20 @@ try {
|
|
|
391
397
|
}
|
|
392
398
|
```
|
|
393
399
|
|
|
394
|
-
Per-load options for pbrt archives:
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
400
|
+
Per-load options for pbrt archives:
|
|
401
|
+
|
|
402
|
+
| Option | Default | Effect |
|
|
403
|
+
|---|---|---|
|
|
404
|
+
| `promptBytes` | 4 GB | moves the line past which a multi-element archive asks for elements |
|
|
405
|
+
| `maxTriangles` | 45M (60M with `memorySpill`) | past it, placements are skipped and the build reports itself truncated |
|
|
406
|
+
| `maxPlacements` | 6M (8M with `memorySpill`) | the same, for placements |
|
|
407
|
+
| `curveTolerance` | 0.05 | how far a curve segment may stray, × the curve's half-width; curves become strips with adaptive segments. `0` gives the old uniform strips bit for bit |
|
|
408
|
+
| `instanceIncludes` | `true` | a file included again under the same material, with no side effects, is placed as an instance of its first reading instead of being read and stored again |
|
|
409
|
+
|
|
410
|
+
45M is the highest rung measured to survive without the spill; raising either cap is a deliberate
|
|
411
|
+
act on a fresh browser tab. With `memorySpill`, 80M stored triangles (4.35M placements) have loaded
|
|
412
|
+
and rendered; an 89M load ran out of memory while parsing, and WebGPU's 4 GB buffer limit stops the
|
|
413
|
+
triangle data at 89.5M in any case.
|
|
398
414
|
|
|
399
415
|
#### Settings
|
|
400
416
|
|
|
@@ -428,16 +444,19 @@ Key settings:
|
|
|
428
444
|
| `maxSubsurfaceSteps` | `number` | 8 | Max random-walk steps for subsurface scattering (raised to 64 by `configureForMode('production')`) |
|
|
429
445
|
| `enableAlphaShadows` | `boolean` | false | Alpha-tested shadow rays (enabled by `configureForMode('production')`) |
|
|
430
446
|
| `enableDOF` | `boolean` | false | Enable depth of field |
|
|
431
|
-
| `
|
|
432
|
-
| `
|
|
433
|
-
| `
|
|
447
|
+
| `dofMode` | `string` | 'look' | `'look'`: the blur is set by `dofBlur`, the same at any scene scale; `'physical'`: a real lens set by `aperture`, `focalLength` and `unitsPerMetre`. The `physical` render profile defaults to `'physical'` |
|
|
448
|
+
| `dofBlur` | `number` | 0.05 | Look mode: how far a distant background blurs, as a fraction of the image height |
|
|
449
|
+
| `focusDistance` | `number` | 0.8 | DOF focus distance in scene units — depth along the view axis (the focal plane is flat) |
|
|
450
|
+
| `aperture` | `number` | 5.6 | Physical mode: f-stop |
|
|
451
|
+
| `focalLength` | `number` | 50 | Physical mode: focal length (mm) |
|
|
452
|
+
| `unitsPerMetre` | `number` | 1 | Physical mode: scene units per real metre, for files whose units are not metres. It carries over between model loads — reset it when the new file's units differ |
|
|
434
453
|
| `transparentBackground` | `boolean` | false | Transparent canvas background |
|
|
435
454
|
| `interactionModeEnabled` | `boolean` | true | Render at lower resolution while the camera moves, keeping the full bounce budget ("Fast Navigation" in the app) |
|
|
436
455
|
| `interactionRenderScale` | `number` | 0.5 | Per-axis render scale while the camera moves (0.5 = a quarter of the pixels); `1` turns the drop off. Ignored while OIDN is the live denoiser |
|
|
437
456
|
| `renderMode` | `number` | 0 | Internal preview(0)/production(1) flag driving accumulation & ASVGF behavior — normally set via `configureForMode()`, not written directly |
|
|
438
457
|
| `visMode` | `number` | 0 | Debug visualization mode (0 = off) |
|
|
439
458
|
| `environmentMode` | `string` | 'hdri' | Sky mode: `'hdri'` \| `'procedural'` \| `'gradient'` \| `'color'` — not routed through `engine.settings`; use `engine.environmentManager.setMode()` instead |
|
|
440
|
-
| `cameraProjection` | `string` | 'perspective' | `'perspective'` \| `'equirectangular'` — see [Camera Projection](#camera-projection-360-panorama) |
|
|
459
|
+
| `cameraProjection` | `string` | 'perspective' | `'perspective'` \| `'orthographic'` \| `'equirectangular'` — see [Camera Projection](#camera-projection-orthographic-360-panorama) |
|
|
441
460
|
| `panoramaLonRange` | `[number, number]` | `[-180, 180]` | Panorama longitude sweep, degrees, left→right |
|
|
442
461
|
| `panoramaLatRange` | `[number, number]` | `[-90, 90]` | Panorama latitude sweep, degrees, bottom→top |
|
|
443
462
|
| `panoramaLevelHorizon` | `boolean` | true | Yaw-only panorama basis, so orbit pitch/roll can't tilt the horizon |
|
|
@@ -467,21 +486,45 @@ To pause rendering for image-viewing UI, set `engine.pauseRendering = true` and
|
|
|
467
486
|
Camera switching, auto-focus, DOF, and direct Three.js access.
|
|
468
487
|
|
|
469
488
|
```js
|
|
470
|
-
engine.cameraManager.active // The active PerspectiveCamera
|
|
489
|
+
engine.cameraManager.active // The active camera: a PerspectiveCamera that can turn orthographic
|
|
471
490
|
engine.cameraManager.controls // The OrbitControls instance
|
|
472
491
|
engine.cameraManager.switchCamera(index) // Switch between scene cameras
|
|
473
492
|
engine.cameraManager.getNames() // List available cameras
|
|
474
493
|
engine.cameraManager.focusOn(center) // Focus orbit camera on a world-space point
|
|
475
494
|
engine.cameraManager.setAutoFocusMode(mode) // 'auto' | 'manual'
|
|
476
495
|
engine.cameraManager.setAFScreenPoint(x, y) // Set normalized AF screen point (0-1)
|
|
496
|
+
engine.cameraManager.setNavigationMode(mode) // 'orbit' | 'walk'
|
|
497
|
+
engine.cameraManager.walkControls.speed // Walk speed, scene units per second
|
|
498
|
+
engine.cameraManager.orthoHeight // Orthographic view height, scene units, wheel zoom included
|
|
499
|
+
engine.cameraManager.setOrthoHeight(height) // Set it
|
|
477
500
|
```
|
|
478
501
|
|
|
479
|
-
|
|
502
|
+
**Walk mode** is first-person navigation: drag to look, W A S D or the arrow keys to walk level, E and Q to
|
|
503
|
+
rise and sink, Shift faster, Alt slower. A new model resets `speed` so the walk crosses it in about eight
|
|
504
|
+
seconds. Keys are ignored while focus is in a text field, list or menu, and when a focused control has
|
|
505
|
+
already used the key. The mode obeys `controls.enabled`, so anything that locks the orbit camera locks walking
|
|
506
|
+
too. Switching back to `'orbit'` circles the surface at the centre of the view.
|
|
480
507
|
|
|
481
|
-
|
|
508
|
+
### Camera Projection (Orthographic, 360° Panorama)
|
|
509
|
+
|
|
510
|
+
Three camera models live behind the `cameraProjection` setting. All are compiled into the same kernel, so switching writes a uniform and resets accumulation — it never recompiles WGSL.
|
|
511
|
+
|
|
512
|
+
#### Orthographic
|
|
482
513
|
|
|
483
514
|
```js
|
|
484
|
-
engine.settings.set('cameraProjection', '
|
|
515
|
+
engine.settings.set('cameraProjection', 'orthographic');
|
|
516
|
+
engine.cameraManager.setOrthoHeight(12); // the view's height in scene units
|
|
517
|
+
engine.addEventListener(EngineEvents.ORTHO_HEIGHT_UPDATED, ({ height }) => {}); // the wheel changed it
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Rays are parallel and start on the camera's image plane, so nothing behind the camera is seen and nothing shrinks with distance. Switching keeps what the view shows at the orbit target: turning orthographic sizes the view from the orbit distance and field of view, and turning back moves the camera to match. The wheel then zooms by changing the view's size rather than moving the camera. A new model is framed the same way.
|
|
521
|
+
|
|
522
|
+
`engine.cameraManager.active` stays the same object — it switches its own projection and reports `isOrthographicCamera` — so picking, the transform gizmo and the overlays follow without anything being re-pointed. Imported orthographic cameras (glTF, and pbrt's `Camera "orthographic"`) switch the projection to orthographic at their own size, and a camera left orthographic comes back so; any other camera switches it back to perspective. Every denoiser, auto-focus and depth of field keep working. An environment at infinity is seen from a single direction, so the background is one colour.
|
|
523
|
+
|
|
524
|
+
#### 360° Panorama
|
|
525
|
+
|
|
526
|
+
```js
|
|
527
|
+
engine.settings.set('cameraProjection', 'equirectangular');
|
|
485
528
|
|
|
486
529
|
// Optional: crop the sweep. Degrees, [min, max].
|
|
487
530
|
engine.settings.set('panoramaLonRange', [-90, 90]); // VR180
|
|
@@ -496,9 +539,9 @@ Depth of field still works: the lens plane is built from each ray's own frame, n
|
|
|
496
539
|
Two features are incompatible with a non-frustum camera and the engine switches them off for you when panorama is enabled:
|
|
497
540
|
|
|
498
541
|
- **ASVGF** falls back to the `edgeaware` denoiser — ASVGF's motion vectors unproject through the projection matrix, which is meaningless when every pixel is its own direction.
|
|
499
|
-
- **Auto-focus**
|
|
542
|
+
- **Auto-focus** pauses and focus holds its last distance — it raycasts via `Raycaster.setFromCamera`, which only understands a frustum. It resumes when you leave the panorama.
|
|
500
543
|
|
|
501
|
-
Read the outcome back rather than duplicating the rule (`engine.denoisingManager.denoiserStrategy
|
|
544
|
+
Read the denoiser outcome back rather than duplicating the rule (`engine.denoisingManager.denoiserStrategy`); it is not restored automatically when you leave the panorama.
|
|
502
545
|
|
|
503
546
|
### engine.lightManager
|
|
504
547
|
|
|
@@ -529,6 +572,34 @@ engine.animationManager.setLoop(true) // Enable/disable looping
|
|
|
529
572
|
engine.animationManager.clips // Get available animation clips
|
|
530
573
|
```
|
|
531
574
|
|
|
575
|
+
### engine.timeline
|
|
576
|
+
|
|
577
|
+
Authored animation: keyframed tracks on one time axis, in seconds. The camera's is the first track; the timeline is where lights and objects will join it. A model's own clips stay with `engine.animationManager`, and saved cameras (`engine.addCamera()`) stay cameras — a keyframe is not a camera.
|
|
578
|
+
|
|
579
|
+
```js
|
|
580
|
+
const camera = engine.timeline.camera // the camera's track
|
|
581
|
+
const key = camera.addKey() // key the current view, 2 s after the last key
|
|
582
|
+
camera.addKey(5) // …or at a time
|
|
583
|
+
camera.updateKey(key.id) // give a key the current view
|
|
584
|
+
camera.setTime(key.id, 3.5) // retime it; keys stay in time order
|
|
585
|
+
camera.remove(key.id)
|
|
586
|
+
camera.keys // [{ id, time, position, target, fov, orthoHeight }]
|
|
587
|
+
|
|
588
|
+
engine.timeline.duration // seconds to the last key of any track
|
|
589
|
+
engine.timeline.animates // a track has two keys or more
|
|
590
|
+
engine.timeline.seek(2.5) // put the scene where the timeline has it at 2.5 s
|
|
591
|
+
await engine.timeline.play() // run it in the viewport, controls locked
|
|
592
|
+
engine.timeline.stop() // or stop early
|
|
593
|
+
|
|
594
|
+
// Keys changed ({ track: 'camera' }) or playback started or stopped ({ track: undefined })
|
|
595
|
+
engine.addEventListener(EngineEvents.TIMELINE_CHANGED, ({ track }) => {})
|
|
596
|
+
|
|
597
|
+
// A video of the move through a still scene; pass clipIndex too to move the camera during a clip
|
|
598
|
+
await new VideoRenderManager(engine).renderAnimation({ timeline: engine.timeline, fps: 30, onFrame });
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
Between keys the camera glides along a smooth curve through their positions while looking along another through their targets, so a subject every key looks at stays in frame. It leaves the first key and reaches the last at rest, runs straight through the keys between, and holds still outside them. FOV, or an orthographic view's height, blends too, in the projection in use. The curves are three.js keyframe tracks (`InterpolateSmooth`). With auto-focus on, focus is measured again on every video frame, and the view is put back when the render ends. A new model clears the keys, as it clears saved cameras.
|
|
602
|
+
|
|
532
603
|
### Materials
|
|
533
604
|
|
|
534
605
|
Material property updates and texture transforms — accessed as direct methods on the engine.
|
|
@@ -603,12 +674,29 @@ engine.environmentManager.texture // The loaded environment texture
|
|
|
603
674
|
await engine.loadEnvironment(url) // Load HDR/EXR environment map (method on engine)
|
|
604
675
|
await engine.environmentManager.setEnvironmentMap(tex) // Set a custom environment texture
|
|
605
676
|
await engine.environmentManager.setMode(mode) // 'hdri' | 'procedural' | 'gradient' | 'color'
|
|
606
|
-
await engine.environmentManager.generateProcedural() //
|
|
677
|
+
await engine.environmentManager.generateProcedural() // Physical sky: spectral, multiple scattering, analytic sun
|
|
607
678
|
await engine.environmentManager.generateGradient() // Gradient sky
|
|
608
679
|
await engine.environmentManager.generateSolid() // Solid color sky
|
|
609
680
|
engine.environmentManager.markDirty() // Flag environment for GPU re-upload
|
|
610
681
|
```
|
|
611
682
|
|
|
683
|
+
The physical sky (mode `'procedural'`) is set through `params`, then baked. It is baked and
|
|
684
|
+
importance-sampled entirely on the GPU (~1.6 ms per sun move), so bake on every slider event rather
|
|
685
|
+
than debouncing: requests made in one task become one bake, and the promise resolves once the
|
|
686
|
+
environment has caught up. The sun is drawn and sampled as a light of its own, not from the texture.
|
|
687
|
+
|
|
688
|
+
```js
|
|
689
|
+
import { sunPosition, dayOfYearForMonth } from 'rayzee';
|
|
690
|
+
|
|
691
|
+
const p = engine.environmentManager.params;
|
|
692
|
+
const { azimuth, elevation } = sunPosition( { hours: 17.5, dayOfYear: dayOfYearForMonth( 6 ), latitude: 40 } );
|
|
693
|
+
const az = ( 180 - azimuth ) * Math.PI / 180, el = elevation * Math.PI / 180; // north along −Z, east along +X
|
|
694
|
+
p.skySunDirection.set( Math.cos( el ) * Math.sin( az ), Math.sin( el ), Math.cos( el ) * Math.cos( az ) );
|
|
695
|
+
p.skyTurbidity = 3; // haze, 1–10; also skyOzone (Dobson units), skyAirDensity (× Earth's),
|
|
696
|
+
// skyGroundAlbedo (Color), skyAltitude (m), skySunSize (°), skySunStrength
|
|
697
|
+
await engine.environmentManager.generateProcedural();
|
|
698
|
+
```
|
|
699
|
+
|
|
612
700
|
### engine.denoisingManager
|
|
613
701
|
|
|
614
702
|
Denoiser strategy, ASVGF, OIDN, upscaler, and auto-exposure.
|
|
@@ -841,7 +929,8 @@ Before extraction the engine prices the scene and applies two lines, both record
|
|
|
841
929
|
|
|
842
930
|
Raise or lower the hard line with `new PathTracerApp(canvas, { maxSceneBytes })`. The estimate runs
|
|
843
931
|
low at the very top of its range, so the per-load `maxTriangles` cap (45M) is the more reliable
|
|
844
|
-
guard on a scene of that size.
|
|
932
|
+
guard on a scene of that size. With `memorySpill` the estimate leaves out what the build keeps on
|
|
933
|
+
disk (the BVH, and triangle records past what a streamed build holds at once).
|
|
845
934
|
|
|
846
935
|
---
|
|
847
936
|
|
|
@@ -912,6 +1001,87 @@ precision, the model, kernel capabilities, tile state and resource counts — wh
|
|
|
912
1001
|
confirm FP16 actually engaged on a given GPU rather than inferring it from the device's feature
|
|
913
1002
|
list. Both need `timestamp-query`, and `getDenoiseProfile()` returns `null` when OIDN is not set up.
|
|
914
1003
|
|
|
1004
|
+
### On-disk Storage (OPFS)
|
|
1005
|
+
|
|
1006
|
+
`engine.storage` is a `StorageManager` over the browser's origin private file system, or `null` where
|
|
1007
|
+
there is none (a private window, Node) — everything works without it, only slower. The engine keeps
|
|
1008
|
+
its caches there: downloads (models, skies, OIDN weights, STBN atlases — revalidated with `HEAD`),
|
|
1009
|
+
unpacked `.tar.gz` archives and archive indexes, built scenes (reopened without rebuilding BVHs when
|
|
1010
|
+
the first build took ≥ 10 s), and environment sampling tables. Caches share a budget of 30 % of the
|
|
1011
|
+
quota and are evicted least-recently-used; `engine.storage.usage()` reports each area.
|
|
1012
|
+
|
|
1013
|
+
A host keeps its own data beside them:
|
|
1014
|
+
|
|
1015
|
+
```js
|
|
1016
|
+
import { STORAGE_KIND } from 'rayzee';
|
|
1017
|
+
|
|
1018
|
+
engine.storage.defineArea( 'renders', { kind: STORAGE_KIND.USER } ); // never evicted
|
|
1019
|
+
const writer = await engine.storage.area( 'renders' ).create( 'render:42', { label: 'Render 42' } );
|
|
1020
|
+
await writer.writeFile( 'image.png', blob );
|
|
1021
|
+
await writer.commit(); // meta.json last: all or nothing
|
|
1022
|
+
|
|
1023
|
+
const entry = await engine.storage.area( 'renders' ).open( 'render:42' );
|
|
1024
|
+
const file = await entry.file( 'image.png' );
|
|
1025
|
+
entry.release();
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
`loadFile( url )` and `loadModel( url, { cacheKey } )` download through the cache — pass `cacheKey` for
|
|
1029
|
+
a link that expires (a signed URL). `fetchFile( url, storage )` / `cachedObjectURL( url )` do the same
|
|
1030
|
+
for a host's own assets. Failures record `storage.*` issues and fall back to memory; storage never
|
|
1031
|
+
throws for being absent or full.
|
|
1032
|
+
|
|
1033
|
+
**Memory spill (experimental).** With `memorySpill: true`, a static scene of more than one 64 MB chunk
|
|
1034
|
+
is built through disk, so its large arrays are never all in memory at once:
|
|
1035
|
+
|
|
1036
|
+
- Extraction and BVH building run together. Each stored range of triangles goes to a BLAS worker as
|
|
1037
|
+
soon as it is written, and extraction waits while more than 1.5 GB of triangle records are held.
|
|
1038
|
+
On the 55.7M-triangle Moana subset the build peak fell from 6.6 to 4.1 GB.
|
|
1039
|
+
- The three.js geometry goes to disk after the build last reads it, and comes back when the build
|
|
1040
|
+
ends. On a 70M-triangle scene the page held 0.84 GB after extraction instead of 4.07 GB.
|
|
1041
|
+
- Each triangle chunk and BLAS goes to the GPU, then to disk. At 50M triangles the page settles at
|
|
1042
|
+
7.2 GB instead of 9.1 GB.
|
|
1043
|
+
|
|
1044
|
+
With or without the spill, a mesh past 2M triangles is built as spatial pieces of ≤ 512k triangles
|
|
1045
|
+
on a worker pool and joined under the tree that split them, so no build holds a second copy of it.
|
|
1046
|
+
|
|
1047
|
+
The render is unchanged. Visibility and rigid moves need nothing read back. Material edits that
|
|
1048
|
+
rewrite triangles (side, transparency, emission), and refits, read it back first. `refitBLASes`
|
|
1049
|
+
throws on a spilled scene until `await engine.ensureSceneResident()`.
|
|
1050
|
+
|
|
1051
|
+
### Saving Scene State
|
|
1052
|
+
|
|
1053
|
+
```js
|
|
1054
|
+
const state = engine.exportSceneState(); // JSON-safe: settings, sky, colour, lights, cameras,
|
|
1055
|
+
// timeline keys, material edits, hidden and moved objects
|
|
1056
|
+
const source = engine.sceneSource; // { kind: 'url', url, cacheKey } | { kind: 'local-file', file } | ...
|
|
1057
|
+
|
|
1058
|
+
// later, after loading the same model again:
|
|
1059
|
+
const { skipped } = await engine.importSceneState( state, {
|
|
1060
|
+
resolve: async ( request ) => {
|
|
1061
|
+
// { kind: 'environment', source } → a File, a URL or null (the engine loads URL skies itself)
|
|
1062
|
+
// { kind: 'colorConfig', config } → true once you have loaded it (built-in configs load themselves)
|
|
1063
|
+
return null;
|
|
1064
|
+
},
|
|
1065
|
+
} );
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
Objects, materials and cameras are matched by position in the scene plus a name check — UUIDs
|
|
1069
|
+
change on every load — so a different model keeps its own objects and materials and the rest still
|
|
1070
|
+
applies; `skipped` lists what could not be put back. Texture swaps on materials are not included yet.
|
|
1071
|
+
|
|
1072
|
+
### Render Checkpoints
|
|
1073
|
+
|
|
1074
|
+
```js
|
|
1075
|
+
const checkpoint = await engine.captureRenderCheckpoint(); // accumulation + the counters that pick the next sample
|
|
1076
|
+
// ...a reload later, the same scene state restored and one frame rendered at the same size:
|
|
1077
|
+
engine.restoreRenderCheckpoint( checkpoint );
|
|
1078
|
+
engine.wake(); // or await engine.renderFrames( n, { reset: false } )
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
In deterministic mode the continued render is bit-identical to one that never stopped. A checkpoint
|
|
1082
|
+
is ~60 bytes a pixel (252 MB at 2048²): the accumulated colour and aux buffers as RGBA32F plus three
|
|
1083
|
+
per-pixel convergence buffers.
|
|
1084
|
+
|
|
915
1085
|
---
|
|
916
1086
|
|
|
917
1087
|
### Events
|
|
@@ -941,7 +1111,8 @@ engine.addEventListener(EngineEvents.RENDER_COMPLETE, (e) => {
|
|
|
941
1111
|
| `TRANSFORM_MODE_CHANGED` | Gizmo mode changed |
|
|
942
1112
|
| `SELECT_MODE_CHANGED` | Selection mode toggled |
|
|
943
1113
|
| `SETTING_CHANGED` | A render setting is modified |
|
|
944
|
-
| `AUTO_FOCUS_UPDATED` | Auto-focus recalculated |
|
|
1114
|
+
| `AUTO_FOCUS_UPDATED` | Auto-focus recalculated — `worldDistance` in scene units, `distance` divided by the model's size |
|
|
1115
|
+
| `ORTHO_HEIGHT_UPDATED` | An orthographic view's height changed — `height` in scene units |
|
|
945
1116
|
| `AUTO_EXPOSURE_UPDATED` | Auto-exposure recalculated |
|
|
946
1117
|
| `AF_POINT_PLACED` | Focus point placed on screen |
|
|
947
1118
|
| `ANIMATION_STARTED` / `ANIMATION_PAUSED` / `ANIMATION_STOPPED` / `ANIMATION_FINISHED` | Animation lifecycle |
|
|
@@ -982,6 +1153,7 @@ import {
|
|
|
982
1153
|
CAMERA_PRESETS,
|
|
983
1154
|
CAMERA_RANGES,
|
|
984
1155
|
SKY_PRESETS,
|
|
1156
|
+
DEFAULT_SUN_PATH,
|
|
985
1157
|
AUTO_FOCUS_MODES,
|
|
986
1158
|
AF_DEFAULTS,
|
|
987
1159
|
TRIANGLE_DATA_LAYOUT,
|
|
@@ -1006,12 +1178,28 @@ import {
|
|
|
1006
1178
|
displayCanvasFit,
|
|
1007
1179
|
} from 'rayzee';
|
|
1008
1180
|
|
|
1181
|
+
// Where the sun stands for a solar time, date and latitude (degrees; azimuth clockwise from north)
|
|
1182
|
+
import { sunPosition, timeForSunElevation, dayOfYearForMonth } from 'rayzee';
|
|
1183
|
+
|
|
1009
1184
|
// Leveled/namespaced logging, shared with the workers
|
|
1010
1185
|
import { Logger, createLogger, fmt, LOG_LEVELS } from 'rayzee';
|
|
1011
1186
|
|
|
1012
1187
|
// Asset URL / cache namespace overrides
|
|
1013
1188
|
import { configureAssets, getAssetConfig } from 'rayzee';
|
|
1014
1189
|
|
|
1190
|
+
// On-disk storage (OPFS): caches, host areas, downloads, file identity
|
|
1191
|
+
import {
|
|
1192
|
+
openStorage, StorageManager, STORAGE_KIND, ENGINE_AREAS, acquireLock, heldLockNames,
|
|
1193
|
+
fileIdentity, identityKey, sameIdentity,
|
|
1194
|
+
DownloadCache, DOWNLOAD_POLICY, fetchFile, nameFromUrl, cachedObjectURL,
|
|
1195
|
+
} from 'rayzee';
|
|
1196
|
+
|
|
1197
|
+
// Archives read in place, one entry at a time
|
|
1198
|
+
import { openZip, readZipDirectory } from 'rayzee';
|
|
1199
|
+
|
|
1200
|
+
// Scene state as plain data (engine.exportSceneState), and the engine version as built
|
|
1201
|
+
import { SCENE_STATE_VERSION, toPortable, fromPortable, VERSION } from 'rayzee';
|
|
1202
|
+
|
|
1015
1203
|
// Advanced: managers & pipeline
|
|
1016
1204
|
import {
|
|
1017
1205
|
RenderSettings,
|
|
@@ -1024,6 +1212,8 @@ import {
|
|
|
1024
1212
|
AnimationManager,
|
|
1025
1213
|
TransformManager,
|
|
1026
1214
|
VideoRenderManager,
|
|
1215
|
+
TimelineManager,
|
|
1216
|
+
CameraTrack,
|
|
1027
1217
|
InteractionManager,
|
|
1028
1218
|
RenderPipeline,
|
|
1029
1219
|
RenderStage,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as e } from "./rolldown-runtime-CAFD8bLK.js";
|
|
2
|
-
import { S as t, t as n, y as r } from "./ToneMapGPU-
|
|
2
|
+
import { S as t, t as n, y as r } from "./ToneMapGPU-DpUZDMbQ.js";
|
|
3
3
|
//#region src/neural/NeuralRetouch.js
|
|
4
4
|
var i = /* @__PURE__ */ e({
|
|
5
5
|
NeuralRetouch: () => m,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as e } from "./rolldown-runtime-CAFD8bLK.js";
|
|
2
|
-
import { S as t, t as n, y as r } from "./ToneMapGPU-
|
|
2
|
+
import { S as t, t as n, y as r } from "./ToneMapGPU-DpUZDMbQ.js";
|
|
3
3
|
//#region src/EngineEvents.js
|
|
4
4
|
var i = {
|
|
5
5
|
RENDER_COMPLETE: "engine:renderComplete",
|
|
@@ -21,6 +21,7 @@ var i = {
|
|
|
21
21
|
OBJECT_TRANSFORM_END: "engine:objectTransformEnd",
|
|
22
22
|
TRANSFORM_MODE_CHANGED: "engine:transformModeChanged",
|
|
23
23
|
AUTO_FOCUS_UPDATED: "engine:autoFocusUpdated",
|
|
24
|
+
ORTHO_HEIGHT_UPDATED: "engine:orthoHeightUpdated",
|
|
24
25
|
AUTO_EXPOSURE_UPDATED: "engine:autoExposureUpdated",
|
|
25
26
|
AF_POINT_PLACED: "engine:afPointPlaced",
|
|
26
27
|
SETTING_CHANGED: "engine:settingChanged",
|
|
@@ -28,12 +29,14 @@ var i = {
|
|
|
28
29
|
ANIMATION_PAUSED: "engine:animationPaused",
|
|
29
30
|
ANIMATION_STOPPED: "engine:animationStopped",
|
|
30
31
|
ANIMATION_FINISHED: "engine:animationFinished",
|
|
32
|
+
TIMELINE_CHANGED: "engine:timelineChanged",
|
|
31
33
|
VIDEO_RENDER_PROGRESS: "engine:videoRenderProgress",
|
|
32
34
|
VIDEO_RENDER_COMPLETE: "engine:videoRenderComplete",
|
|
33
35
|
DISPOSE: "engine:dispose",
|
|
34
36
|
DEVICE_LOST: "engine:deviceLost",
|
|
35
37
|
ISSUE: "engine:issue",
|
|
36
|
-
SCENE_METADATA_APPLIED: "engine:sceneMetadataApplied"
|
|
38
|
+
SCENE_METADATA_APPLIED: "engine:sceneMetadataApplied",
|
|
39
|
+
STORAGE_CHANGED: "engine:storageChanged"
|
|
37
40
|
}, a = /* @__PURE__ */ e({
|
|
38
41
|
NeuralSuperRes: () => g,
|
|
39
42
|
SR_MAX_INPUT: () => c,
|
|
@@ -110,7 +110,8 @@ var D = {
|
|
|
110
110
|
upscalerModelBaseUrl: "https://huggingface.co/notaneimu/onnx-image-models/resolve/main/",
|
|
111
111
|
neuralRuntimeUrl: "/neural/neural-runtime.js",
|
|
112
112
|
neuralAssetBaseUrl: null,
|
|
113
|
-
cacheNamespace: "rayzee"
|
|
113
|
+
cacheNamespace: "rayzee",
|
|
114
|
+
storage: "auto"
|
|
114
115
|
};
|
|
115
116
|
function O(e) {
|
|
116
117
|
e && Object.assign(D, e);
|