rayzee 7.30.1 → 7.31.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 CHANGED
@@ -545,6 +545,8 @@ engine.denoisingManager.setAutoExposureParams({ keyValue: 0.18 })
545
545
  // OIDN & Upscaler
546
546
  engine.denoisingManager.setOIDNEnabled(true)
547
547
  engine.denoisingManager.setOIDNQuality('high')
548
+ engine.denoisingManager.setStrategy('oidn') // OIDN owns the live view; see below
549
+ engine.denoisingManager.continuousDenoiseInterval = 250 // cap refreshes at 4/sec (default 8 = uncapped)
548
550
  engine.denoisingManager.setUpscalerEnabled(true)
549
551
  engine.denoisingManager.setUpscalerScaleFactor(2)
550
552
  engine.denoisingManager.setUpscalerQuality('high')
@@ -735,7 +737,7 @@ engine.addEventListener(EngineEvents.RENDER_COMPLETE, (e) => {
735
737
  | `RENDER_COMPLETE` | Rendering has converged |
736
738
  | `RENDER_RESET` | Accumulation buffer is reset |
737
739
  | `FRAME` | Fires once per `animate()` tick — hook external instrumentation (stats panels, telemetry) here |
738
- | `DENOISING_START` / `DENOISING_END` | Denoiser runs |
740
+ | `DENOISING_START` / `DENOISING_END` | Denoiser runs. `event.continuous` is `true` for a cadence denoise of the still-accumulating image, `false` for the one that ends a render |
739
741
  | `UPSCALING_START` / `UPSCALING_PROGRESS` / `UPSCALING_END` | AI upscaler runs |
740
742
  | `LOADING_UPDATE` / `LOADING_RESET` | Asset loading progress |
741
743
  | `STATS_UPDATE` | Performance stats updated |
@@ -849,7 +851,10 @@ import { setBindingAudit, getBindingAuditFindings, clearBindingAuditFindings } f
849
851
 
850
852
  ### Enabling OIDN (Intel Open Image Denoise)
851
853
 
852
- OIDN provides high-quality AI denoising for final renders. It runs automatically after the render converges (reaches `maxSamples`).
854
+ OIDN provides high-quality AI denoising. It runs automatically once the render converges (reaches
855
+ `maxSamples`), and — in `'interactive'` mode — also on a cadence while the image is still
856
+ accumulating, so a preview shows a clean picture as it refines instead of only at the end. See
857
+ [Continuous denoising](#continuous-denoising).
853
858
 
854
859
  1. **Install the package**
855
860
 
@@ -883,6 +888,112 @@ OIDN provides high-quality AI denoising for final renders. It runs automatically
883
888
  | `'balance'` | 1.8 MB | accumulated | General use |
884
889
  | `'high'` | 7.3 MB | accumulated | Final renders — used by `configureForMode('production')` |
885
890
 
891
+ #### When the denoiser runs
892
+
893
+ Two independent decisions, because they answer different questions — *what cleans the view while the
894
+ render works*, and *does the finished image get a proper pass*:
895
+
896
+ ```js
897
+ engine.denoisingManager.setStrategy('oidn'); // 'none' | 'edgeaware' | 'asvgf' | 'nrd' | 'oidn'
898
+ engine.denoisingManager.setOIDNEnabled(false); // a full OIDN pass on the finished image
899
+ ```
900
+
901
+ Exactly one denoiser owns the live view, which is why OIDN is an entry in that list rather than a
902
+ parallel switch — two of them would mean paying for a per-frame denoise whose result the OIDN
903
+ overlay immediately covers. But choosing OIDN there says nothing about the finished image, and
904
+ switching the final pass on says nothing about the live view. All six combinations are reachable:
905
+
906
+ | `setStrategy` | `setOIDNEnabled` | Camera moving | Still, accumulating | Finished |
907
+ |---|---|---|---|---|
908
+ | `'none'` | `false` | raw | raw | raw |
909
+ | `'none'` | `true` | raw | raw | OIDN |
910
+ | `'asvgf'` | `false` | ASVGF | ASVGF | ASVGF's last frame |
911
+ | `'asvgf'` | `true` | ASVGF | ASVGF | OIDN |
912
+ | `'oidn'` | `false` | raw | OIDN, refreshing | one last refresh |
913
+ | `'oidn'` | `true` | raw | OIDN, refreshing | OIDN, full quality |
914
+
915
+ `denoiser.enabled` is the union of the two — "OIDN is in use at all", which is what the aux G-buffer
916
+ wiring needs — so read the two decisions back from `denoisingManager.denoiserStrategy` and
917
+ `denoisingManager.finalDenoise`, not from it.
918
+
919
+ With OIDN on the live view and the final pass off, the render still closes with one more refresh: the
920
+ cadence's last tick lands short of the end, and the picture should match the render that finished. On
921
+ a 150-sample render that gap measured **11 samples at 512²** and **1 at 1024²** — larger where the
922
+ renderer is fast, because more samples land between refreshes. It uses whatever model the refreshes
923
+ were already on: no reload for an image the user never asked to be denoised at full quality.
924
+
925
+ In the app that is `Real-Time Denoiser` (None / EdgeAware / ASVGF / NRD / **OIDN (AI)**) and the
926
+ `Final Denoise (OIDN)` switch. Deterministic mode pins the live refreshes off, since which frame a
927
+ wall-clock cadence lands on is not reproducible.
928
+
929
+ Leaving the live view raw is not just "denoising off" — it is the only way to see the true noise
930
+ level, which is how you judge whether a render has actually settled. That is row one and row two.
931
+
932
+ #### Quality while it runs vs. quality when it finishes
933
+
934
+ `oidnQuality` is **the quality of the finished image**. The refreshes along the way use the cheapest
935
+ model that reads the same kind of aux buffer, and the chosen model is put back for the last denoise:
936
+
937
+ | `oidnQuality` | refreshes use | finished image |
938
+ |---|---|---|
939
+ | `fast` | `fast` | `fast` |
940
+ | `fast-clean` / `balance` / `high` | `fast-clean` | as chosen |
941
+
942
+ Two constraints shape that table, and neither is optional:
943
+
944
+ - **The aux kind must not change mid-render.** `setCleanAuxNormal()` throws away the accumulated
945
+ albedo/normal, so a refresh model that disagreed with the final one would leave the final denoise
946
+ reading an aux buffer one sample deep. That is why the cheap model is `fast-clean` and not `fast`.
947
+ - **Switching models reloads the weights, ~1 s.** So it happens at most once per render, and only
948
+ once a denoise has actually measured too slow to be a live view (`> 120 ms`). A tier a machine can
949
+ afford is kept — at 512² that is every tier. The verdict survives a camera move, because the
950
+ device does not get faster between them; it is re-taken when the tier or the resolution changes.
951
+
952
+ #### What paces the refreshes
953
+
954
+ Two knobs with two different jobs, and the gap between refreshes is whichever is larger:
955
+
956
+ - **The cost floor protects the renderer.** The gap is at least twice what the last denoise actually
957
+ cost, so denoising never takes more than about half the wall clock, at any resolution, on any GPU.
958
+ This is not configurable, and it is what binds at the default.
959
+ - **`continuousDenoiseInterval` caps the refresh rate in absolute terms**, for a host that wants
960
+ fewer updates than the renderer could afford — a laptop on battery, or a viewport where 30 updates
961
+ a second is distracting. The default (8 ms) is below any real denoise cost, so it never binds:
962
+ refreshes run as often as the cost floor allows. Raising it gives a flat `1000 / interval` cap.
963
+
964
+ Measured, `fast` model:
965
+
966
+ | `continuousDenoiseInterval` | 8 | 50 | 100 | 200 | 400 |
967
+ |---|---|---|---|---|---|
968
+ | 512² (denoise 12 ms) | 35/sec | 18/sec | 9.6/sec | 4.9/sec | 2.6/sec |
969
+ | 1024² (denoise 50 ms) | 9/sec | 9/sec | 9/sec | 4.9/sec | 2.6/sec |
970
+
971
+ The two columns converge once the interval is the larger of the two — below that the cost floor is
972
+ holding 1024² down to 9/sec regardless of what the interval says.
973
+
974
+ A fixed millisecond interval cannot do this job: the same `fast` model measures 14 ms at 512², 48 ms
975
+ at 1024² and ~800 ms at 2048². Measured where the GPU is saturated (1536²), the multiplier is the
976
+ whole trade — 1x gives 1.6 refreshes/sec at 62 % of the sample rate, 2x gives 0.8/sec at 89 %, 3x
977
+ gives 0.6/sec at 97 %.
978
+
979
+ This replaced a sample-growth gate (refresh only once the sample count had grown 1.4x), which was
980
+ written when a denoise cost 100-330 ms. Once the output pack moved to the GPU and a denoise got
981
+ cheap, that gate only cost refreshes: removing it took 512² from 2 to 31 refreshes/sec and 1024²
982
+ from 2 to 8.9, both at an unchanged sample rate, while 1536² and 2048² did not move at all because
983
+ the cost floor already bound there. Refreshing faster is also *smoother*, not shimmerier — less
984
+ changes underneath between refreshes.
985
+
986
+ Nothing runs while the camera is moving; the raw frame shows during navigation.
987
+
988
+ Cadence runs are tagged so a host can tell them apart from the denoise that ends a render:
989
+
990
+ ```js
991
+ engine.addEventListener(EngineEvents.DENOISING_END, e => {
992
+ if (e.continuous) return; // a background refresh, not the final image
993
+ saveResult();
994
+ });
995
+ ```
996
+
886
997
  `'fast'` and `'fast-clean'` are the same network size and cost the same to run; they differ only in
887
998
  which auxiliary guide their weights expect. That makes the ordering **not** a simple quality ladder:
888
999
  at 1 spp the accumulated guide has one sample, so `'fast-clean'` is fed something it was not trained