rayzee 8.5.0 → 8.6.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.
Files changed (60) hide show
  1. package/README.md +60 -4
  2. package/dist/{DLSSNeural-BNSJ8V3z.js → DLSSNeural-BhBCBgKf.js} +5 -5
  3. package/dist/{DLSSSuperRes-BoEW89Cl.js → DLSSSuperRes-BCA7F_k5.js} +5 -5
  4. package/dist/ToneMapGPU-BzcLheoR.js +533 -0
  5. package/dist/rayzee.es.js +27198 -9047
  6. package/dist/rayzee.umd.js +255 -187
  7. package/package.json +1 -1
  8. package/src/AssetConfig.js +15 -2
  9. package/src/Color/BakedViews.js +134 -0
  10. package/src/Color/BuiltinViews.js +261 -0
  11. package/src/Color/ColorManagement.js +1130 -0
  12. package/src/Color/ColorSpaces.js +432 -0
  13. package/src/Color/Displays.js +19 -0
  14. package/src/Color/InputColorSpaces.js +243 -0
  15. package/src/Color/LutBake.js +371 -0
  16. package/src/Color/OcioRuntime.js +338 -0
  17. package/src/Color/OcioViews.js +419 -0
  18. package/src/Color/ViewTransforms.js +323 -0
  19. package/src/Color/WorkingMatrix.js +86 -0
  20. package/src/EngineDefaults.js +55 -3
  21. package/src/EngineIssues.js +3 -0
  22. package/src/Passes/AIUpscaler.js +16 -7
  23. package/src/PathTracerApp.js +227 -7
  24. package/src/Processor/AssetLoader.js +1 -2
  25. package/src/Processor/EmissiveTriangleBuilder.js +34 -2
  26. package/src/Processor/GeometryExtractor.js +151 -138
  27. package/src/Processor/LightSerializer.js +8 -3
  28. package/src/Processor/MaterialPacking.js +168 -0
  29. package/src/Processor/PackedRayBuffer.js +6 -2
  30. package/src/Processor/ProceduralSky.js +4 -0
  31. package/src/Processor/SceneProcessor.js +25 -1
  32. package/src/Processor/SimpleSky.js +8 -0
  33. package/src/Processor/TextureCreator.js +137 -91
  34. package/src/Processor/TextureReadback.js +92 -0
  35. package/src/Processor/ToneMapCPU.js +80 -165
  36. package/src/Processor/ToneMapGPU.js +122 -151
  37. package/src/RenderSettings.js +1 -0
  38. package/src/Stages/Compositor.js +11 -2
  39. package/src/Stages/PathTracer.js +2 -0
  40. package/src/Stages/PathTracerStage.js +1 -1
  41. package/src/TSL/Common.js +15 -0
  42. package/src/TSL/Debugger.js +2 -3
  43. package/src/TSL/EmissiveSampling.js +6 -8
  44. package/src/TSL/ExtendKernel.js +9 -0
  45. package/src/TSL/Fresnel.js +23 -1
  46. package/src/TSL/HitFacet.js +102 -0
  47. package/src/TSL/LightsDirect.js +12 -33
  48. package/src/TSL/LightsSampling.js +15 -7
  49. package/src/TSL/MaterialEvaluation.js +26 -19
  50. package/src/TSL/MaterialProperties.js +74 -37
  51. package/src/TSL/MaterialTransmission.js +2 -3
  52. package/src/TSL/ShadeKernel.js +34 -21
  53. package/src/TSL/ShadowTerminator.js +125 -0
  54. package/src/TSL/Struct.js +9 -0
  55. package/src/TSL/Subsurface.js +2 -3
  56. package/src/index.js +29 -1
  57. package/src/managers/EnvironmentManager.js +20 -0
  58. package/src/managers/MaterialDataManager.js +118 -208
  59. package/src/managers/UniformManager.js +1 -0
  60. package/dist/ToneMapGPU-DzqSyzTx.js +0 -341
package/README.md CHANGED
@@ -26,6 +26,7 @@ A real-time WebGPU path tracing engine built on Three.js. Framework-agnostic —
26
26
  - [engine.lightManager](#enginelightmanager)
27
27
  - [engine.animationManager](#engineanimationmanager)
28
28
  - [Materials](#materials)
29
+ - [Colour Management](#colour-management)
29
30
  - [engine.environmentManager](#engineenvironmentmanager)
30
31
  - [engine.denoisingManager](#enginedenoisingmanager)
31
32
  - [engine.interactionManager](#engineinteractionmanager)
@@ -292,6 +293,11 @@ configureAssets({
292
293
  dlssRuntimeUrl: '/dlss/dlss-runtime.js',
293
294
  dlssAssetBaseUrl: '/dlss/',
294
295
 
296
+ // OpenColorIO runtime (~6 MB of WebAssembly) — the engine never names the package, so a host
297
+ // that loads colour configs supplies it, bundled or served. Unset, colour management stays inert.
298
+ ocioRuntimeFactory: () => import('@bb-studio/ocio'), // or: ocioRuntimeUrl: '/vendor/ocio/index.js'
299
+ ocioWasmUrl: '/vendor/ocio/ocio-wasm.wasm', // optional override for the .wasm
300
+
295
301
  // Prefix for engine-managed IndexedDB stores. Set to a unique value if multiple
296
302
  // apps embed the engine on the same origin to avoid cache collisions.
297
303
  cacheNamespace: 'my-app',
@@ -414,13 +420,14 @@ Key settings:
414
420
  | `maxBounces` | `number` | 3 | Max ray bounce depth |
415
421
  | `maxSamples` | `number` | 60 | Max accumulated samples before stopping |
416
422
  | `exposure` | `number` | 1.0 | Exposure value |
417
- | `saturation` | `number` | 1.2 | Color saturation |
423
+ | `saturation` | `number` | 1.0 | Color saturation (1 = no grade) |
418
424
  | `enableEnvironment` | `boolean` | true | Use environment lighting |
419
425
  | `environmentIntensity` | `number` | 1.0 | Environment light strength |
420
- | `environmentRotation` | `number` | 270 | Environment Y-rotation (degrees) |
426
+ | `environmentRotation` | `number` | 0 | Environment Y-rotation (degrees); 0 shows the HDRI as authored, as Blender's unmapped world does |
421
427
  | `showBackground` | `boolean` | true | Show the environment as a visible backdrop for camera-miss rays (vs. a solid/transparent background) |
422
428
  | `samplingTechnique` | `number` | 2 | Sampler: `0` PCG, `1` scrambled Halton, `2` Owen-scrambled Sobol |
423
429
  | `fireflyThreshold` | `number` | 3.0 | Firefly clamping threshold |
430
+ | `shadowTerminatorOffset` | `number` | 0.1 | Cycles' Shadow Terminator → Geometry Offset: near the light's terminator on a smooth-shaded low-poly mesh, light and environment shadow rays start on the smooth surface the vertex normals describe, not the flat facet. Blender's default; `0` disables |
424
431
  | `transmissiveBounces` | `number` | 5 | Max bounces for transmissive materials |
425
432
  | `maxSubsurfaceSteps` | `number` | 8 | Max random-walk steps for subsurface scattering (raised to 64 by `configureForMode('production')`) |
426
433
  | `enableAlphaShadows` | `boolean` | false | Alpha-tested shadow rays (enabled by `configureForMode('production')`) |
@@ -429,7 +436,8 @@ Key settings:
429
436
  | `aperture` | `number` | 5.6 | DOF aperture (f-stop) |
430
437
  | `focalLength` | `number` | 50 | DOF focal length (mm) |
431
438
  | `transparentBackground` | `boolean` | false | Transparent canvas background |
432
- | `interactionModeEnabled` | `boolean` | true | Lower quality during camera movement for smoother navigation |
439
+ | `interactionModeEnabled` | `boolean` | true | Render at lower resolution while the camera moves, keeping the full bounce budget ("Fast Navigation" in the app) |
440
+ | `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 |
433
441
  | `renderMode` | `number` | 0 | Internal preview(0)/production(1) flag driving accumulation & ASVGF behavior — normally set via `configureForMode()`, not written directly |
434
442
  | `visMode` | `number` | 0 | Debug visualization mode (0 = off) |
435
443
  | `environmentMode` | `string` | 'hdri' | Sky mode: `'hdri'` \| `'procedural'` \| `'gradient'` \| `'color'` — not routed through `engine.settings`; use `engine.environmentManager.setMode()` instead |
@@ -445,7 +453,7 @@ Key settings:
445
453
  | `pixelFreezeThreshold` | `number` | 0.02 | Relative-error threshold for a pixel to become a freeze candidate |
446
454
  | `pixelFreezeStability` | `number` | 8 | Consecutive candidate frames required before a pixel freezes |
447
455
 
448
- See `ENGINE_DEFAULTS` for the full list with default values.
456
+ See `ENGINE_DEFAULTS` for the full list with default values. The default look is AgX (`toneMapping: 6`) at neutral saturation; tone mapping is chosen through [Colour Management](#colour-management) (`engine.color.setActiveView( id )`), not `settings`.
449
457
 
450
458
  #### Rendering Modes
451
459
 
@@ -552,8 +560,43 @@ engine.updateAllMeshVisibility() // re-sync after manual object
552
560
 
553
561
  // Read access to the active scene (returns the mesh-bearing scene)
554
562
  engine.getScene()
563
+
564
+ // Where a packed value came from: 'material' | 'mapped' | 'default' | 'host'
565
+ engine.getMaterialPropertySource(index, 'ior')
566
+ ```
567
+
568
+ A property a three.js material lacks falls back to `MATERIAL_DEFAULTS` (exported) — MeshPhysicalMaterial's own values, so a glTF metallic material gets IOR 1.5, not a guess derived from its metalness. Weights and roughnesses (metalness, roughness, transmission, opacity, clearcoat, sheen, iridescence, …) are clamped to [0, 1] on upload and on `setMaterialProperty`.
569
+
570
+ ### Colour Management
571
+
572
+ `engine.color` is an OpenColorIO pipeline: what textures and lights mean, what the render happens in, and what it is shown and saved as. **It is inert until a config is loaded** — the render stays linear Rec.709 and the view transforms are three.js's own seven, so a host that never loads one sees no change. The host supplies the runtime (`ocioRuntimeFactory` or `ocioRuntimeUrl` in [`configureAssets`](#configuring-assets-cdn-urls--cache-namespace)).
573
+
574
+ ```js
575
+ configureAssets({ ocioRuntimeFactory: () => import('@bb-studio/ocio') });
576
+
577
+ await engine.loadColorConfig({ builtin: 'ocio://cg-config-v4.0.0_aces-v2.0_ocio-v2.5' }); // a runtime built-in
578
+ await engine.loadColorConfig({ files, configPath: 'config.ocio', id: 'studio' }); // or a folder: [{ relativePath, data }]
579
+
580
+ engine.color.setView({ display: 'sRGB - Display', view: 'ACES 2.0 - SDR 100 nits (Rec.709)', look: null });
581
+ engine.color.setLook(look); // a look from status().config.looks, on the active view
582
+ engine.color.setActiveView(id); // any registered view, OCIO or built-in (three.js constant)
583
+ engine.color.setContext({ SHOT: '010' }); // $SHOT in the config resolves to this
584
+ engine.color.status(); // config, working space, active view, registered views
585
+
586
+ engine.color.setWorkingSpace('ACEScg'); // render in the config's space…
587
+ await engine.applyColorWorkingSpace(); // …which rebuilds textures, materials and the environment
588
+
589
+ await engine.setTextureColorSpace(texture, 'srgb'); // null (auto), 'srgb', 'linear' or a config space
590
+ const { data } = await engine.renderToBuffer({ colorSpace: 'ACES2065-1', source: 'display' }); // float delivery buffer
591
+ await engine.unloadColorConfig();
555
592
  ```
556
593
 
594
+ - **Load and unload through `engine.loadColorConfig()` / `unloadColorConfig()`** once a scene exists: they undo an adopted working space while the config that converted the environment is still loaded.
595
+ - A view is baked to a log2 shaper and a 65³ table, interpolated tetrahedrally; the same table drives the canvas, the GPU and CPU readbacks and the menu (`listViewTransforms()`, `onRegistryChange()`). An OCIO view returns display-encoded colour, so the engine switches the output pass to linear while one is active.
596
+ - **Baked views.** `await engine.color.saveBakedView(id)` writes a view's table to a file (157 KB gzip for 65³), and `await engine.color.loadBakedView(bytes, { expect })` registers it with no runtime and no config — show the look on the first frame, load the config later. When that config loads, a baked view whose files match the fingerprint it was baked from is kept as is: no rebake, no new id, no restart.
597
+ - `renderToBuffer({ colorSpace })` takes `'srgb'` (display bytes through the active view), `'linear'` (the working-space accumulation) or a config colour space; `source: 'display'` reads what the viewport shows, denoised, instead of the raw accumulation.
598
+ - Degradations (a view that cannot bake, a display the canvas cannot show) are recorded as warnings in the [degradation contract](#degradation-contract).
599
+
557
600
  ### engine.environmentManager
558
601
 
559
602
  Environment maps, sky modes, and procedural generation.
@@ -954,6 +997,18 @@ import {
954
997
  PRODUCTION_RENDER_CONFIG,
955
998
  INTERACTIVE_RENDER_CONFIG,
956
999
  MAX_RESERVABLE_RENDER_SIZE,
1000
+ RENDER_PROFILES,
1001
+ MATERIAL_DEFAULTS,
1002
+ } from 'rayzee';
1003
+
1004
+ // Colour management — engine.color is the instance a host normally uses; these build UI against
1005
+ // it, or reach the view-transform registry without an app
1006
+ import {
1007
+ ColorManagement, getActiveColorManagement, isColorManaged, DEFAULT_WORKING_SPACE,
1008
+ listViewTransforms, getViewTransform, addViewTransform, removeViewTransform, onRegistryChange,
1009
+ buildOcioView, addOcioView, addAllOcioViews,
1010
+ convertColor, convertPixelsF32, extractMatrix, hasColorSpace,
1011
+ displayCanvasFit,
957
1012
  } from 'rayzee';
958
1013
 
959
1014
  // Leveled/namespaced logging, shared with the workers
@@ -1017,6 +1072,7 @@ import { setBindingAudit, getBindingAuditFindings, clearBindingAuditFindings } f
1017
1072
  |---|---|---|
1018
1073
  | `oidn-web` | Intel Open Image Denoise for high-quality final renders | Yes — `npm install oidn-web` (**>=0.4.0**) |
1019
1074
  | `onnxruntime-web` | AI-powered upscaling | No — loaded from CDN at runtime |
1075
+ | `@bb-studio/ocio` | OpenColorIO runtime for [Colour Management](#colour-management) (~6 MB WebAssembly) | Only to load colour configs — `npm install @bb-studio/ocio`, then pass it as `ocioRuntimeFactory` |
1020
1076
 
1021
1077
  > **Note:** `onnxruntime-web` is also listed in `package.json` under `optionalDependencies` for bundler compatibility, but the engine's own runtime path always fetches it from a CDN (see `ortRuntimeUrl` / `ortWasmPaths` in [Configuring Assets](#configuring-assets-cdn-urls--cache-namespace)) rather than importing the installed package — installing it locally has no effect unless you also override those URLs to point at your own copy.
1022
1078
 
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "./rolldown-runtime-CAFD8bLK.js";
2
- import { o as t, r as n, t as r } from "./ToneMapGPU-DzqSyzTx.js";
2
+ import { S as t, t as n, y as r } from "./ToneMapGPU-BzcLheoR.js";
3
3
  //#region src/dlss/DLSSNeural.js
4
4
  var i = /* @__PURE__ */ e({
5
5
  DLSSNeural: () => m,
@@ -39,7 +39,7 @@ var s = t("dlss"), c = 2048 * 2048, l = Object.freeze({
39
39
  }), u = null;
40
40
  function d() {
41
41
  if (u) return u;
42
- let { dlssRuntimeUrl: e, dlssAssetBaseUrl: t } = n();
42
+ let { dlssRuntimeUrl: e, dlssAssetBaseUrl: t } = r();
43
43
  return globalThis.__DLSS5_ASSET_BASE__ = t, u = new Promise((t, n) => {
44
44
  if (globalThis.DLSSRuntime?.getSrNrChain) return t(globalThis.DLSSRuntime.getSrNrChain());
45
45
  let r = document.createElement("script");
@@ -139,15 +139,15 @@ var f = "\n@group(0) @binding(0) var src: texture_2d<f32>;\n@group(0) @binding(1
139
139
  };
140
140
  }
141
141
  async readOutputToned(e) {
142
- let { validWidth: t, validHeight: n } = this.chain.pipeline.geometry;
143
- return await this._packHDR(t, n), this._toneMapper ??= new r(this.device, "rayzee:dlss-nr-tonemap"), this._toneMapper.ensureSize(t, n), {
142
+ let { validWidth: t, validHeight: r } = this.chain.pipeline.geometry;
143
+ return await this._packHDR(t, r), this._toneMapper ??= new n(this.device, "rayzee:dlss-nr-tonemap"), this._toneMapper.ensureSize(t, r), {
144
144
  rgba8: await this._toneMapper.toRGBA8(this._hdrStorage, {
145
145
  exposure: e.exposure ?? 1,
146
146
  toneMapping: e.toneMapping ?? 0,
147
147
  saturation: e.saturation ?? 1
148
148
  }),
149
149
  width: t,
150
- height: n
150
+ height: r
151
151
  };
152
152
  }
153
153
  async readOutput() {
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "./rolldown-runtime-CAFD8bLK.js";
2
- import { o as t, r as n, t as r } from "./ToneMapGPU-DzqSyzTx.js";
2
+ import { S as t, t as n, y as r } from "./ToneMapGPU-BzcLheoR.js";
3
3
  //#region src/EngineEvents.js
4
4
  var i = {
5
5
  RENDER_COMPLETE: "engine:renderComplete",
@@ -45,7 +45,7 @@ var i = {
45
45
  }), o = 2, s = t("dlss"), c = 2048, l = null;
46
46
  function u() {
47
47
  if (l) return l;
48
- let { dlssRuntimeUrl: e, dlssAssetBaseUrl: t } = n();
48
+ let { dlssRuntimeUrl: e, dlssAssetBaseUrl: t } = r();
49
49
  return globalThis.__DLSS5_ASSET_BASE__ = t, l = new Promise((t, n) => {
50
50
  if (globalThis.DLSSRuntime?.getNativeSR) return t(globalThis.DLSSRuntime.getNativeSR());
51
51
  let r = document.createElement("script");
@@ -174,11 +174,11 @@ var g = class e {
174
174
  };
175
175
  }
176
176
  async upscaleToRGBA8(e, t = {}) {
177
- let { result: n, ms: i } = await this._run(e), { outputWidth: a, outputHeight: o } = this.geometry;
178
- this._toneMapper ??= new r(this.sr.device, "rayzee:dlss-sr-tonemap"), this._toneMapper.ensureSize(a, o);
177
+ let { result: r, ms: i } = await this._run(e), { outputWidth: a, outputHeight: o } = this.geometry;
178
+ this._toneMapper ??= new n(this.sr.device, "rayzee:dlss-sr-tonemap"), this._toneMapper.ensureSize(a, o);
179
179
  let s = performance.now();
180
180
  return {
181
- rgba8: await this._toneMapper.toRGBA8(n.hdr.buffer ?? n.hdr, {
181
+ rgba8: await this._toneMapper.toRGBA8(r.hdr.buffer ?? r.hdr, {
182
182
  exposure: t.exposure ?? 1,
183
183
  toneMapping: t.toneMapping ?? 0,
184
184
  saturation: t.saturation ?? 1,