rayzee 7.30.2 → 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 +113 -2
- package/dist/rayzee.es.js +588 -314
- package/dist/rayzee.umd.js +242 -17
- package/package.json +1 -1
- package/src/EngineDefaults.js +9 -1
- package/src/Passes/OIDNDenoiser.js +497 -347
- package/src/PathTracerApp.js +15 -2
- package/src/Processor/ToneMapWGSL.js +162 -0
- package/src/managers/DenoisingManager.js +251 -22
- package/src/managers/OverlayManager.js +4 -0
- package/src/managers/VideoRenderManager.js +5 -5
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
|
|
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
|