rayzee 5.10.2 → 6.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 +82 -22
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js +2 -0
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js.map +1 -0
- package/dist/rayzee.es.js +1299 -1843
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +50 -74
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -4
- package/src/AssetConfig.js +56 -0
- package/src/EngineDefaults.js +5 -3
- package/src/EngineEvents.js +1 -0
- package/src/Passes/AIUpscaler.js +44 -22
- package/src/Passes/OIDNDenoiser.js +4 -104
- package/src/PathTracerApp.js +59 -63
- package/src/Processor/AssetLoader.js +5 -2
- package/src/Processor/TextureCreator.js +42 -15
- package/src/Processor/Workers/AIUpscalerWorker.js +21 -6
- package/src/Stages/ASVGF.js +6 -27
- package/src/Stages/AdaptiveSampling.js +10 -26
- package/src/Stages/PathTracer.js +4 -5
- package/src/TSL/BVHTraversal.js +2 -18
- package/src/TSL/Clearcoat.js +1 -2
- package/src/TSL/Common.js +0 -13
- package/src/TSL/EmissiveSampling.js +0 -16
- package/src/TSL/Environment.js +0 -7
- package/src/TSL/LightsDirect.js +3 -379
- package/src/TSL/LightsSampling.js +0 -171
- package/src/TSL/MaterialEvaluation.js +3 -103
- package/src/TSL/MaterialProperties.js +1 -56
- package/src/TSL/MaterialSampling.js +2 -284
- package/src/TSL/MaterialTransmission.js +0 -93
- package/src/TSL/Random.js +0 -23
- package/src/TSL/Struct.js +0 -21
- package/src/TSL/TextureSampling.js +0 -69
- package/src/index.js +5 -2
- package/src/managers/DenoisingManager.js +13 -5
- package/src/managers/OverlayManager.js +14 -2
- package/src/managers/VideoRenderManager.js +4 -4
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js +0 -2
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js.map +0 -1
- package/src/Processor/createRenderTargetHelper.js +0 -521
- package/src/TSL/RayIntersection.js +0 -162
- package/src/managers/helpers/StatsHelper.js +0 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rayzee",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Real-time WebGPU path tracing engine built on Three.js",
|
|
6
6
|
"main": "dist/rayzee.umd.js",
|
|
@@ -36,9 +36,6 @@
|
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"three": ">=0.184.0"
|
|
38
38
|
},
|
|
39
|
-
"dependencies": {
|
|
40
|
-
"stats-gl": "^4.0.2"
|
|
41
|
-
},
|
|
42
39
|
"optionalDependencies": {
|
|
43
40
|
"oidn-web": ">=0.3.0",
|
|
44
41
|
"onnxruntime-web": ">=1.0.0"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine asset configuration. CDN URLs and cache namespaces are configurable so
|
|
3
|
+
* downstream consumers aren't pinned to the upstream Rayzee deployment's defaults.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* import { configureAssets } from 'rayzee';
|
|
7
|
+
* configureAssets({
|
|
8
|
+
* stbnScalarAtlas: '/assets/stbn_scalar_atlas.png',
|
|
9
|
+
* dracoDecoderPath: '/draco/',
|
|
10
|
+
* cacheNamespace: 'my-app',
|
|
11
|
+
* });
|
|
12
|
+
*
|
|
13
|
+
* Call before constructing PathTracerApp. Per-key partial overrides are supported.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const config = {
|
|
17
|
+
// STBN blue-noise atlases (NVIDIA-RTX/STBN). Decoded as Float32 textures.
|
|
18
|
+
stbnScalarAtlas: 'https://assets.rayzee.atulmourya.com/noise/stbn_scalar_atlas.png',
|
|
19
|
+
stbnVec2Atlas: 'https://assets.rayzee.atulmourya.com/noise/stbn_vec2_atlas.png',
|
|
20
|
+
|
|
21
|
+
// onnxruntime-web (loaded lazily by AI upscaler worker via dynamic import).
|
|
22
|
+
ortRuntimeUrl: 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/ort.webgpu.bundle.min.mjs',
|
|
23
|
+
ortWasmPaths: 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/',
|
|
24
|
+
|
|
25
|
+
// Draco / KTX2 decoder paths for GLTFLoader.
|
|
26
|
+
dracoDecoderPath: 'https://www.gstatic.com/draco/v1/decoders/',
|
|
27
|
+
ktx2TranscoderPath: 'https://cdn.jsdelivr.net/npm/three@0.183.2/examples/jsm/libs/basis/',
|
|
28
|
+
|
|
29
|
+
// OIDN denoiser model weights (oidn-web tza files).
|
|
30
|
+
oidnWeightsBaseUrl: 'https://cdn.jsdelivr.net/npm/denoiser/tzas/',
|
|
31
|
+
|
|
32
|
+
// AI upscaler ONNX model base URL. Quality presets resolve relative paths against this.
|
|
33
|
+
upscalerModelBaseUrl: 'https://huggingface.co/notaneimu/onnx-image-models/resolve/main/',
|
|
34
|
+
|
|
35
|
+
// Prefix used when the engine writes to client-side stores (IndexedDB, etc).
|
|
36
|
+
// Set to a unique value to avoid collisions when multiple apps embed the engine on the same origin.
|
|
37
|
+
cacheNamespace: 'rayzee',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Override asset URLs and cache namespace. Partial — only provided keys are replaced.
|
|
42
|
+
* @param {Partial<typeof config>} overrides
|
|
43
|
+
*/
|
|
44
|
+
export function configureAssets( overrides ) {
|
|
45
|
+
|
|
46
|
+
if ( ! overrides ) return;
|
|
47
|
+
Object.assign( config, overrides );
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Returns a snapshot of current asset config. */
|
|
52
|
+
export function getAssetConfig() {
|
|
53
|
+
|
|
54
|
+
return { ...config };
|
|
55
|
+
|
|
56
|
+
}
|
package/src/EngineDefaults.js
CHANGED
|
@@ -436,15 +436,17 @@ export const TEXTURE_CONSTANTS = {
|
|
|
436
436
|
// Default texture matrix for materials
|
|
437
437
|
export const DEFAULT_TEXTURE_MATRIX = [ 0, 0, 1, 1, 0, 0, 0, 1 ];
|
|
438
438
|
|
|
439
|
-
// Render
|
|
440
|
-
|
|
439
|
+
// Render quality configurations.
|
|
440
|
+
// 'interactive' — low-sample, bounded bounces, no offline denoising, controls enabled.
|
|
441
|
+
// 'production' — high-sample, deep bounces, OIDN enabled, controls disabled.
|
|
442
|
+
export const PRODUCTION_RENDER_CONFIG = {
|
|
441
443
|
maxSamples: 30, bounces: 20, transmissiveBounces: 8, samplesPerPixel: 1,
|
|
442
444
|
renderMode: 1, enableAlphaShadows: true, tiles: 3, tilesHelper: true,
|
|
443
445
|
enableOIDN: true, oidnQuality: 'balance',
|
|
444
446
|
interactionModeEnabled: false,
|
|
445
447
|
};
|
|
446
448
|
|
|
447
|
-
export const
|
|
449
|
+
export const INTERACTIVE_RENDER_CONFIG = {
|
|
448
450
|
maxSamples: ENGINE_DEFAULTS.maxSamples, bounces: ENGINE_DEFAULTS.bounces,
|
|
449
451
|
samplesPerPixel: ENGINE_DEFAULTS.samplesPerPixel, renderMode: ENGINE_DEFAULTS.renderMode, enableAlphaShadows: ENGINE_DEFAULTS.enableAlphaShadows,
|
|
450
452
|
transmissiveBounces: ENGINE_DEFAULTS.transmissiveBounces,
|
package/src/EngineEvents.js
CHANGED
package/src/Passes/AIUpscaler.js
CHANGED
|
@@ -1,35 +1,53 @@
|
|
|
1
1
|
import { EventDispatcher, ACESFilmicToneMapping } from 'three';
|
|
2
2
|
import { TONE_MAP_FNS, SRGB_GAMMA, applySaturation } from '../Processor/ToneMapCPU.js';
|
|
3
3
|
import { fetchAsWorker } from '../Processor/Workers/fetchAsWorker.js';
|
|
4
|
+
import { getAssetConfig } from '../AssetConfig.js';
|
|
4
5
|
import AI_UPSCALER_WORKER_URL from '../Processor/Workers/AIUpscalerWorker.js?worker&url';
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
// ─── Model Configuration ───────────────────────────────────────────────────────
|
|
9
|
+
// Quality presets reference relative paths against asset-config `upscalerModelBaseUrl`.
|
|
8
10
|
|
|
9
|
-
const
|
|
11
|
+
const QUALITY_PRESET_PATHS = {
|
|
12
|
+
fast: {
|
|
13
|
+
2: '2x-spanx2-ch48.onnx',
|
|
14
|
+
4: '4xNomos8k_span_otf_strong_fp32_opset17.onnx'
|
|
15
|
+
},
|
|
16
|
+
balanced: {
|
|
17
|
+
2: '2xNomosUni_compact_otf_medium.onnx',
|
|
18
|
+
4: 'RealESRGAN_x4plus.onnx'
|
|
19
|
+
},
|
|
20
|
+
quality: {
|
|
21
|
+
2: '2x-realesrgan-x2plus.onnx',
|
|
22
|
+
4: '4xNomos2_hq_mosr_fp32.onnx'
|
|
23
|
+
}
|
|
24
|
+
};
|
|
10
25
|
|
|
11
|
-
|
|
26
|
+
function _resolveQualityPresets() {
|
|
27
|
+
|
|
28
|
+
const { upscalerModelBaseUrl } = getAssetConfig();
|
|
29
|
+
const out = {};
|
|
30
|
+
for ( const q in QUALITY_PRESET_PATHS ) {
|
|
31
|
+
|
|
32
|
+
out[ q ] = {};
|
|
33
|
+
for ( const s in QUALITY_PRESET_PATHS[ q ] ) {
|
|
34
|
+
|
|
35
|
+
out[ q ][ s ] = upscalerModelBaseUrl + QUALITY_PRESET_PATHS[ q ][ s ];
|
|
12
36
|
|
|
13
|
-
// Quality presets: each has a 2x and 4x model (all NCHW, dynamic input dims)
|
|
14
|
-
QUALITY_PRESETS: {
|
|
15
|
-
fast: {
|
|
16
|
-
// 1.6MB — SPAN
|
|
17
|
-
2: HF_BASE + '2x-spanx2-ch48.onnx',
|
|
18
|
-
// 1.6MB — SPAN
|
|
19
|
-
4: HF_BASE + '4xNomos8k_span_otf_strong_fp32_opset17.onnx'
|
|
20
|
-
},
|
|
21
|
-
balanced: {
|
|
22
|
-
// 2.4MB — SRVGGNetCompact
|
|
23
|
-
2: HF_BASE + '2xNomosUni_compact_otf_medium.onnx',
|
|
24
|
-
// 4.9MB — SRVGGNetCompact
|
|
25
|
-
4: HF_BASE + 'RealESRGAN_x4plus.onnx'
|
|
26
|
-
},
|
|
27
|
-
quality: {
|
|
28
|
-
// 67MB — RRDBNet
|
|
29
|
-
2: HF_BASE + '2x-realesrgan-x2plus.onnx',
|
|
30
|
-
// 16.5MB — MoSR
|
|
31
|
-
4: HF_BASE + '4xNomos2_hq_mosr_fp32.onnx'
|
|
32
37
|
}
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return out;
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const MODEL_CONFIG = {
|
|
46
|
+
|
|
47
|
+
get QUALITY_PRESETS() {
|
|
48
|
+
|
|
49
|
+
return _resolveQualityPresets();
|
|
50
|
+
|
|
33
51
|
},
|
|
34
52
|
|
|
35
53
|
// Larger tiles = fewer GPU dispatches = faster. 512 works on most GPUs with 4GB+ VRAM.
|
|
@@ -189,11 +207,15 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
189
207
|
|
|
190
208
|
};
|
|
191
209
|
|
|
210
|
+
const { ortRuntimeUrl, ortWasmPaths, cacheNamespace } = getAssetConfig();
|
|
192
211
|
this._worker.addEventListener( 'message', handler );
|
|
193
212
|
this._worker.postMessage( {
|
|
194
213
|
type: 'load',
|
|
195
214
|
url,
|
|
196
|
-
sessionOptions: MODEL_CONFIG.SESSION_OPTIONS
|
|
215
|
+
sessionOptions: MODEL_CONFIG.SESSION_OPTIONS,
|
|
216
|
+
ortRuntimeUrl,
|
|
217
|
+
ortWasmPaths,
|
|
218
|
+
cacheNamespace,
|
|
197
219
|
} );
|
|
198
220
|
|
|
199
221
|
} );
|
|
@@ -42,14 +42,13 @@ function removeOidnTfjsBackend() {
|
|
|
42
42
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
import { createRenderTargetHelper } from '../Processor/createRenderTargetHelper.js';
|
|
46
45
|
import { TONE_MAP_FNS, linearToSRGB, applySaturation } from '../Processor/ToneMapCPU.js';
|
|
46
|
+
import { getAssetConfig } from '../AssetConfig.js';
|
|
47
47
|
|
|
48
48
|
/** Reusable RGB output buffer (avoids per-pixel allocation). */
|
|
49
49
|
const _tmOut = new Float32Array( 3 );
|
|
50
50
|
|
|
51
51
|
const MODEL_CONFIG = {
|
|
52
|
-
BASE_URL: 'https://cdn.jsdelivr.net/npm/denoiser/tzas/',
|
|
53
52
|
// clean-aux models — first-hit albedo/normal are deterministic per pixel
|
|
54
53
|
QUALITY_MODELS: {
|
|
55
54
|
fast: 'rt_hdr_alb_nrm_small',
|
|
@@ -82,7 +81,6 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
82
81
|
this.camera = camera;
|
|
83
82
|
this.input = renderer.domElement;
|
|
84
83
|
this.output = output;
|
|
85
|
-
this.debugContainer = options.debugContainer || null;
|
|
86
84
|
this.extractGBufferData = options.extractGBufferData || null;
|
|
87
85
|
this.getMRTRenderTarget = options.getMRTRenderTarget || null;
|
|
88
86
|
|
|
@@ -143,11 +141,6 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
143
141
|
this.currentTZAUrl = null;
|
|
144
142
|
this.unet = null;
|
|
145
143
|
|
|
146
|
-
// For debug visualization
|
|
147
|
-
this.debugHelpers = null;
|
|
148
|
-
this._lastAlbedoTexture = null;
|
|
149
|
-
this._lastNormalTexture = null;
|
|
150
|
-
|
|
151
144
|
// Initialize asynchronously
|
|
152
145
|
this._initialize().catch( error => {
|
|
153
146
|
|
|
@@ -163,7 +156,6 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
163
156
|
try {
|
|
164
157
|
|
|
165
158
|
this._setupCanvas();
|
|
166
|
-
this._initDebugVisualization();
|
|
167
159
|
await this._setupUNetDenoiser();
|
|
168
160
|
|
|
169
161
|
} catch ( error ) {
|
|
@@ -174,14 +166,6 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
174
166
|
|
|
175
167
|
}
|
|
176
168
|
|
|
177
|
-
_initDebugVisualization() {
|
|
178
|
-
|
|
179
|
-
// Note: Debug helpers will be created lazily when MRT textures are available
|
|
180
|
-
// This avoids creating helpers without proper texture references
|
|
181
|
-
this.debugHelpers = null;
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
169
|
_setupCanvas() {
|
|
186
170
|
|
|
187
171
|
if ( ! this.output.getContext ) {
|
|
@@ -277,9 +261,10 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
277
261
|
|
|
278
262
|
_generateTzaUrl() {
|
|
279
263
|
|
|
280
|
-
const {
|
|
264
|
+
const { oidnWeightsBaseUrl } = getAssetConfig();
|
|
265
|
+
const { QUALITY_MODELS } = MODEL_CONFIG;
|
|
281
266
|
const modelName = QUALITY_MODELS[ this.quality ] || QUALITY_MODELS.balance;
|
|
282
|
-
return `${
|
|
267
|
+
return `${oidnWeightsBaseUrl}${modelName}.tza`;
|
|
283
268
|
|
|
284
269
|
}
|
|
285
270
|
|
|
@@ -900,78 +885,6 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
900
885
|
|
|
901
886
|
}
|
|
902
887
|
|
|
903
|
-
/**
|
|
904
|
-
* Update debug visualization using MRT textures directly
|
|
905
|
-
* @param {RenderTarget} mrtRenderTarget - The MRT render target containing albedo and normal
|
|
906
|
-
*/
|
|
907
|
-
_updateDebugVisualization( mrtRenderTarget ) {
|
|
908
|
-
|
|
909
|
-
if ( ! mrtRenderTarget?.textures || mrtRenderTarget.textures.length < 3 ) {
|
|
910
|
-
|
|
911
|
-
return;
|
|
912
|
-
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
// Check if textures have changed (render target was recreated)
|
|
916
|
-
const texturesChanged = this.debugHelpers &&
|
|
917
|
-
( this._lastAlbedoTexture !== mrtRenderTarget.textures[ 2 ] ||
|
|
918
|
-
this._lastNormalTexture !== mrtRenderTarget.textures[ 1 ] );
|
|
919
|
-
|
|
920
|
-
// Create or recreate helpers when textures change
|
|
921
|
-
if ( ! this.debugHelpers || texturesChanged ) {
|
|
922
|
-
|
|
923
|
-
// Dispose existing helpers if they exist
|
|
924
|
-
if ( this.debugHelpers ) {
|
|
925
|
-
|
|
926
|
-
this.debugHelpers.albedo?.dispose();
|
|
927
|
-
this.debugHelpers.normal?.dispose();
|
|
928
|
-
console.log( 'OIDNDenoiser: Recreating debug helpers due to texture change' );
|
|
929
|
-
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
// Pass full MRT render target with textureIndex for async readback
|
|
933
|
-
this.debugHelpers = {
|
|
934
|
-
albedo: createRenderTargetHelper( this.renderer, mrtRenderTarget, {
|
|
935
|
-
width: 250,
|
|
936
|
-
height: 250,
|
|
937
|
-
position: 'bottom-right',
|
|
938
|
-
theme: 'dark',
|
|
939
|
-
title: 'OIDN Albedo',
|
|
940
|
-
autoUpdate: false,
|
|
941
|
-
textureIndex: 2
|
|
942
|
-
} ),
|
|
943
|
-
normal: createRenderTargetHelper( this.renderer, mrtRenderTarget, {
|
|
944
|
-
width: 250,
|
|
945
|
-
height: 250,
|
|
946
|
-
position: 'bottom-left',
|
|
947
|
-
theme: 'dark',
|
|
948
|
-
title: 'OIDN Normal',
|
|
949
|
-
autoUpdate: false,
|
|
950
|
-
textureIndex: 1
|
|
951
|
-
} )
|
|
952
|
-
};
|
|
953
|
-
|
|
954
|
-
// Store references to track texture changes
|
|
955
|
-
this._lastAlbedoTexture = mrtRenderTarget.textures[ 2 ];
|
|
956
|
-
this._lastNormalTexture = mrtRenderTarget.textures[ 1 ];
|
|
957
|
-
|
|
958
|
-
// Add helpers to DOM
|
|
959
|
-
const container = this.debugContainer || document.body;
|
|
960
|
-
container.appendChild( this.debugHelpers.albedo );
|
|
961
|
-
container.appendChild( this.debugHelpers.normal );
|
|
962
|
-
|
|
963
|
-
// Hide by default (visibility state will be restored by calling code)
|
|
964
|
-
this.debugHelpers.albedo.hide();
|
|
965
|
-
this.debugHelpers.normal.hide();
|
|
966
|
-
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
// Update the displays
|
|
970
|
-
this.debugHelpers.albedo.update();
|
|
971
|
-
this.debugHelpers.normal.update();
|
|
972
|
-
|
|
973
|
-
}
|
|
974
|
-
|
|
975
888
|
_destroyPendingStagingBuffers() {
|
|
976
889
|
|
|
977
890
|
for ( const buf of this._pendingStagingBuffers ) {
|
|
@@ -999,19 +912,6 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
999
912
|
removeOidnTfjsBackend();
|
|
1000
913
|
this._destroyGPUInputBuffers();
|
|
1001
914
|
|
|
1002
|
-
// Dispose debug helpers
|
|
1003
|
-
if ( this.debugHelpers ) {
|
|
1004
|
-
|
|
1005
|
-
this.debugHelpers.albedo?.dispose();
|
|
1006
|
-
this.debugHelpers.normal?.dispose();
|
|
1007
|
-
this.debugHelpers = null;
|
|
1008
|
-
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
// Clear texture references
|
|
1012
|
-
this._lastAlbedoTexture = null;
|
|
1013
|
-
this._lastNormalTexture = null;
|
|
1014
|
-
|
|
1015
915
|
// Clean up DOM
|
|
1016
916
|
if ( this.output?.parentNode ) {
|
|
1017
917
|
|
package/src/PathTracerApp.js
CHANGED
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
} from 'three';
|
|
6
6
|
import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js';
|
|
7
7
|
import { SceneHelpers } from './SceneHelpers.js';
|
|
8
|
-
import { createStats } from './managers/helpers/StatsHelper.js';
|
|
9
8
|
import { PathTracer } from './Stages/PathTracer.js';
|
|
10
9
|
import { NormalDepth } from './Stages/NormalDepth.js';
|
|
11
10
|
import { MotionVector } from './Stages/MotionVector.js';
|
|
@@ -19,7 +18,7 @@ import { SSRC } from './Stages/SSRC.js';
|
|
|
19
18
|
import { Compositor } from './Stages/Compositor.js';
|
|
20
19
|
import { RenderPipeline } from './Pipeline/RenderPipeline.js';
|
|
21
20
|
import { CompletionTracker } from './Pipeline/CompletionTracker.js';
|
|
22
|
-
import { ENGINE_DEFAULTS as DEFAULT_STATE,
|
|
21
|
+
import { ENGINE_DEFAULTS as DEFAULT_STATE, PRODUCTION_RENDER_CONFIG, INTERACTIVE_RENDER_CONFIG } from './EngineDefaults.js';
|
|
23
22
|
import { updateStats, updateLoading, resetLoading, setStatusCallback, getDisplaySamples, disposeObjectFromMemory } from './Processor/utils.js';
|
|
24
23
|
import { BuildTimer } from './Processor/BuildTimer.js';
|
|
25
24
|
import { InteractionManager } from './managers/InteractionManager.js';
|
|
@@ -64,8 +63,11 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
64
63
|
* @param {HTMLCanvasElement} canvas - Canvas element for rendering
|
|
65
64
|
* @param {Object} [options] - Engine options
|
|
66
65
|
* @param {boolean} [options.autoResize=true] - Automatically listen for window resize events
|
|
67
|
-
* @param {
|
|
68
|
-
*
|
|
66
|
+
* @param {HTMLElement} [options.container] - Single DOM parent the engine mounts all auxiliary
|
|
67
|
+
* elements into (HUD overlay, denoiser canvas). Defaults to `canvas.parentNode`.
|
|
68
|
+
*
|
|
69
|
+
* The engine dispatches `EngineEvents.FRAME` after each animate() iteration so hosts can
|
|
70
|
+
* tick external instrumentation (e.g. a stats panel) without coupling the engine to it.
|
|
69
71
|
*/
|
|
70
72
|
constructor( canvas, options = {} ) {
|
|
71
73
|
|
|
@@ -85,8 +87,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
85
87
|
|
|
86
88
|
this.canvas = canvas;
|
|
87
89
|
this._autoResize = options.autoResize !== false;
|
|
88
|
-
this.
|
|
89
|
-
this._statsContainer = options.statsContainer || null;
|
|
90
|
+
this._container = options.container || null;
|
|
90
91
|
|
|
91
92
|
// ── Settings (single source of truth for all render parameters) ──
|
|
92
93
|
this.settings = new RenderSettings( DEFAULT_STATE );
|
|
@@ -210,8 +211,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
210
211
|
this.stages.pathTracer.materialData.setMaterialData( new Float32Array( 16 ) );
|
|
211
212
|
this.stages.pathTracer.setupMaterial();
|
|
212
213
|
|
|
213
|
-
if ( this._showStats ) this._initStats();
|
|
214
|
-
|
|
215
214
|
this.isInitialized = true;
|
|
216
215
|
console.log( 'WebGPU Path Tracer App initialized' );
|
|
217
216
|
|
|
@@ -228,7 +227,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
228
227
|
|
|
229
228
|
if ( this._loadingInProgress || this._sdf?.isProcessing ) {
|
|
230
229
|
|
|
231
|
-
this.
|
|
230
|
+
this.dispatchEvent( { type: EngineEvents.FRAME } );
|
|
232
231
|
return;
|
|
233
232
|
|
|
234
233
|
}
|
|
@@ -330,7 +329,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
330
329
|
}
|
|
331
330
|
|
|
332
331
|
this._renderHelperOverlay();
|
|
333
|
-
this.
|
|
332
|
+
this.dispatchEvent( { type: EngineEvents.FRAME } );
|
|
334
333
|
|
|
335
334
|
this.renderer.resolveTimestampsAsync?.( TimestampQuery.RENDER );
|
|
336
335
|
this.renderer.resolveTimestampsAsync?.( TimestampQuery.COMPUTE );
|
|
@@ -363,7 +362,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
363
362
|
|
|
364
363
|
this._paused = true;
|
|
365
364
|
this.stopAnimation();
|
|
366
|
-
if ( this._stats ) this._stats.dom.style.display = 'none';
|
|
367
365
|
|
|
368
366
|
}
|
|
369
367
|
|
|
@@ -372,7 +370,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
372
370
|
|
|
373
371
|
this._paused = false;
|
|
374
372
|
if ( ! this.animationManagerId ) this.animate();
|
|
375
|
-
if ( this._stats ) this._stats.dom.style.display = '';
|
|
376
373
|
|
|
377
374
|
}
|
|
378
375
|
|
|
@@ -513,13 +510,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
513
510
|
if ( this.renderer ) this.renderer._canvasTarget = null;
|
|
514
511
|
this.renderer = null;
|
|
515
512
|
|
|
516
|
-
if ( this._stats ) {
|
|
517
|
-
|
|
518
|
-
this._stats.dom.remove();
|
|
519
|
-
this._stats = null;
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
|
|
523
513
|
this.stages = {};
|
|
524
514
|
this.isInitialized = false;
|
|
525
515
|
|
|
@@ -910,26 +900,16 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
910
900
|
// ═══════════════════════════════════════════════════════════════
|
|
911
901
|
|
|
912
902
|
/**
|
|
913
|
-
* Configures the engine for a specific rendering
|
|
914
|
-
* @param {
|
|
903
|
+
* Configures the engine for a specific rendering quality tier.
|
|
904
|
+
* @param {'interactive' | 'production'} mode
|
|
915
905
|
* @param {Object} [options]
|
|
916
906
|
*/
|
|
917
907
|
configureForMode( mode, options = {} ) {
|
|
918
908
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
this.pauseRendering = true;
|
|
922
|
-
this.cameraManager.controls.enabled = false;
|
|
923
|
-
this.renderer?.domElement && ( this.renderer.domElement.style.display = 'none' );
|
|
924
|
-
this.denoisingManager?.denoiser?.output && ( this.denoisingManager.denoiser.output.style.display = 'none' );
|
|
925
|
-
return;
|
|
926
|
-
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
const isFinal = mode === 'final-render';
|
|
930
|
-
const config = isFinal ? FINAL_RENDER_CONFIG : PREVIEW_RENDER_CONFIG;
|
|
909
|
+
const isProduction = mode === 'production';
|
|
910
|
+
const config = isProduction ? PRODUCTION_RENDER_CONFIG : INTERACTIVE_RENDER_CONFIG;
|
|
931
911
|
|
|
932
|
-
this.cameraManager.controls.enabled = !
|
|
912
|
+
this.cameraManager.controls.enabled = ! isProduction;
|
|
933
913
|
|
|
934
914
|
// Batch uniform updates via settings
|
|
935
915
|
this.settings.setMany( {
|
|
@@ -970,9 +950,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
970
950
|
|
|
971
951
|
}
|
|
972
952
|
|
|
973
|
-
this.renderer?.domElement && ( this.renderer.domElement.style.display = 'block' );
|
|
974
|
-
this.denoisingManager?.denoiser?.output && ( this.denoisingManager.denoiser.output.style.display = 'block' );
|
|
975
|
-
|
|
976
953
|
this.needsReset = false;
|
|
977
954
|
this.pauseRendering = false;
|
|
978
955
|
this.reset();
|
|
@@ -1018,26 +995,21 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1018
995
|
}
|
|
1019
996
|
|
|
1020
997
|
/**
|
|
1021
|
-
*
|
|
998
|
+
* Captures the current render as a Blob. Returns null if no canvas is
|
|
999
|
+
* available. The host is responsible for downloading or otherwise
|
|
1000
|
+
* consuming the result.
|
|
1001
|
+
*
|
|
1002
|
+
* @param {Object} [options]
|
|
1003
|
+
* @param {string} [options.type='image/png'] - MIME type for the encoded image
|
|
1004
|
+
* @param {number} [options.quality] - 0–1 quality hint for lossy formats
|
|
1005
|
+
* @returns {Promise<Blob|null>}
|
|
1022
1006
|
*/
|
|
1023
|
-
screenshot() {
|
|
1007
|
+
screenshot( { type = 'image/png', quality } = {} ) {
|
|
1024
1008
|
|
|
1025
1009
|
const canvas = this.getCanvas();
|
|
1026
|
-
if ( ! canvas ) return;
|
|
1027
|
-
|
|
1028
|
-
try {
|
|
1029
|
-
|
|
1030
|
-
const data = canvas.toDataURL( 'image/png' );
|
|
1031
|
-
const link = document.createElement( 'a' );
|
|
1032
|
-
link.href = data;
|
|
1033
|
-
link.download = 'screenshot.png';
|
|
1034
|
-
link.click();
|
|
1035
|
-
|
|
1036
|
-
} catch ( error ) {
|
|
1037
|
-
|
|
1038
|
-
console.error( 'Screenshot failed:', error );
|
|
1010
|
+
if ( ! canvas ) return Promise.resolve( null );
|
|
1039
1011
|
|
|
1040
|
-
|
|
1012
|
+
return new Promise( ( resolve ) => canvas.toBlob( resolve, type, quality ) );
|
|
1041
1013
|
|
|
1042
1014
|
}
|
|
1043
1015
|
|
|
@@ -1146,6 +1118,34 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1146
1118
|
|
|
1147
1119
|
}
|
|
1148
1120
|
|
|
1121
|
+
/**
|
|
1122
|
+
* The active mesh-bearing scene. Prefer this over reading `scene`/`meshScene`
|
|
1123
|
+
* directly — the engine may swap the underlying scene between rebuilds.
|
|
1124
|
+
* @returns {import('three').Scene}
|
|
1125
|
+
*/
|
|
1126
|
+
getScene() {
|
|
1127
|
+
|
|
1128
|
+
return this.meshScene || this.scene;
|
|
1129
|
+
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// Sets when `visible` is a boolean; toggles when it's an updater (prev) => next.
|
|
1133
|
+
/**
|
|
1134
|
+
* @param {string} uuid
|
|
1135
|
+
* @param {boolean | ((prev: boolean) => boolean)} visible
|
|
1136
|
+
* @returns {boolean | null} new visibility, or null if the mesh wasn't found
|
|
1137
|
+
*/
|
|
1138
|
+
setMeshVisibilityByUuid( uuid, visible ) {
|
|
1139
|
+
|
|
1140
|
+
const object = this.getScene()?.getObjectByProperty( 'uuid', uuid );
|
|
1141
|
+
if ( ! object ) return null;
|
|
1142
|
+
const next = typeof visible === 'function' ? !! visible( object.visible ) : !! visible;
|
|
1143
|
+
object.visible = next;
|
|
1144
|
+
this.updateAllMeshVisibility();
|
|
1145
|
+
return next;
|
|
1146
|
+
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
1149
|
/**
|
|
1150
1150
|
* Updates a material's texture transform (offset, repeat, rotation).
|
|
1151
1151
|
* @param {number} materialIndex
|
|
@@ -1537,9 +1537,9 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1537
1537
|
stage.isComplete = false;
|
|
1538
1538
|
this.completion.resumeFromPause();
|
|
1539
1539
|
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1540
|
+
// Restore live preview: abort() on the denoising manager already
|
|
1541
|
+
// handles canvas opacity, denoiser output visibility, and upscaler reset.
|
|
1542
|
+
this.denoisingManager?.abort( this.canvas );
|
|
1543
1543
|
|
|
1544
1544
|
this.dispatchEvent( { type: EngineEvents.RENDER_RESET } );
|
|
1545
1545
|
this.wake();
|
|
@@ -1548,13 +1548,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1548
1548
|
|
|
1549
1549
|
}
|
|
1550
1550
|
|
|
1551
|
-
_initStats() {
|
|
1552
|
-
|
|
1553
|
-
const container = this._statsContainer || this.canvas.parentElement || document.body;
|
|
1554
|
-
this._stats = createStats( this.renderer, container );
|
|
1555
|
-
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
1551
|
_setupAutoExposureListener() {
|
|
1559
1552
|
|
|
1560
1553
|
if ( ! this.stages.autoExposure ) return;
|
|
@@ -1592,6 +1585,9 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1592
1585
|
renderHeight: this.denoisingManager?._lastRenderHeight || this.canvas.clientHeight || 1,
|
|
1593
1586
|
} );
|
|
1594
1587
|
|
|
1588
|
+
this._container = this._container || this.canvas.parentNode || null;
|
|
1589
|
+
this.overlayManager.mount( this._container );
|
|
1590
|
+
|
|
1595
1591
|
}
|
|
1596
1592
|
|
|
1597
1593
|
|
|
@@ -11,6 +11,7 @@ import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';
|
|
|
11
11
|
import { unzipSync, strFromU8 } from 'three/addons/libs/fflate.module.js';
|
|
12
12
|
import { disposeObjectFromMemory, updateLoading } from './utils';
|
|
13
13
|
import { BuildTimer } from './BuildTimer.js';
|
|
14
|
+
import { getAssetConfig } from '../AssetConfig.js';
|
|
14
15
|
|
|
15
16
|
// Define supported file formats
|
|
16
17
|
const SUPPORTED_FORMATS = {
|
|
@@ -739,12 +740,14 @@ export class AssetLoader extends EventDispatcher {
|
|
|
739
740
|
// worker pools. Callers must invoke _disposeGLTFLoader() to terminate them.
|
|
740
741
|
async createGLTFLoader() {
|
|
741
742
|
|
|
743
|
+
const { dracoDecoderPath, ktx2TranscoderPath } = getAssetConfig();
|
|
744
|
+
|
|
742
745
|
const dracoLoader = new DRACOLoader();
|
|
743
746
|
dracoLoader.setDecoderConfig( { type: 'js' } );
|
|
744
|
-
dracoLoader.setDecoderPath(
|
|
747
|
+
dracoLoader.setDecoderPath( dracoDecoderPath );
|
|
745
748
|
|
|
746
749
|
const ktx2Loader = new KTX2Loader();
|
|
747
|
-
ktx2Loader.setTranscoderPath(
|
|
750
|
+
ktx2Loader.setTranscoderPath( ktx2TranscoderPath );
|
|
748
751
|
|
|
749
752
|
if ( this.renderer ) {
|
|
750
753
|
|