rayzee 8.5.0 → 8.6.1
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 +60 -56
- package/dist/{DLSSNeural-BNSJ8V3z.js → NeuralRetouch-CYOJ21Wn.js} +33 -33
- package/dist/{DLSSSuperRes-BoEW89Cl.js → NeuralSuperRes-Dc6jRYke.js} +25 -25
- package/dist/ToneMapGPU-BVE852iN.js +533 -0
- package/dist/rayzee.es.js +27235 -9074
- package/dist/rayzee.umd.js +255 -187
- package/package.json +1 -1
- package/src/AssetConfig.js +21 -7
- package/src/Color/BakedViews.js +134 -0
- package/src/Color/BuiltinViews.js +261 -0
- package/src/Color/ColorManagement.js +1130 -0
- package/src/Color/ColorSpaces.js +432 -0
- package/src/Color/Displays.js +19 -0
- package/src/Color/InputColorSpaces.js +243 -0
- package/src/Color/LutBake.js +371 -0
- package/src/Color/OcioRuntime.js +338 -0
- package/src/Color/OcioViews.js +419 -0
- package/src/Color/ViewTransforms.js +323 -0
- package/src/Color/WorkingMatrix.js +86 -0
- package/src/EngineDefaults.js +55 -3
- package/src/EngineIssues.js +3 -0
- package/src/Passes/AIUpscaler.js +16 -7
- package/src/Passes/OIDNDenoiser.js +13 -5
- package/src/PathTracerApp.js +227 -7
- package/src/Processor/AssetLoader.js +1 -2
- package/src/Processor/EmissiveTriangleBuilder.js +34 -2
- package/src/Processor/GeometryExtractor.js +151 -138
- package/src/Processor/LightSerializer.js +8 -3
- package/src/Processor/MaterialPacking.js +168 -0
- package/src/Processor/PackedRayBuffer.js +6 -2
- package/src/Processor/ProceduralSky.js +4 -0
- package/src/Processor/SceneProcessor.js +25 -1
- package/src/Processor/SimpleSky.js +8 -0
- package/src/Processor/TextureCreator.js +137 -91
- package/src/Processor/TextureReadback.js +92 -0
- package/src/Processor/ToneMapCPU.js +80 -165
- package/src/Processor/ToneMapGPU.js +122 -151
- package/src/RenderSettings.js +1 -0
- package/src/Stages/Compositor.js +11 -2
- package/src/Stages/PathTracer.js +2 -0
- package/src/Stages/PathTracerStage.js +1 -1
- package/src/TSL/Common.js +15 -0
- package/src/TSL/Debugger.js +2 -3
- package/src/TSL/EmissiveSampling.js +6 -8
- package/src/TSL/ExtendKernel.js +9 -0
- package/src/TSL/Fresnel.js +23 -1
- package/src/TSL/HitFacet.js +102 -0
- package/src/TSL/LightsDirect.js +12 -33
- package/src/TSL/LightsSampling.js +15 -7
- package/src/TSL/MaterialEvaluation.js +26 -19
- package/src/TSL/MaterialProperties.js +74 -37
- package/src/TSL/MaterialTransmission.js +2 -3
- package/src/TSL/ShadeKernel.js +34 -21
- package/src/TSL/ShadowTerminator.js +125 -0
- package/src/TSL/Struct.js +9 -0
- package/src/TSL/Subsurface.js +2 -3
- package/src/index.js +32 -4
- package/src/managers/DenoisingManager.js +60 -44
- package/src/managers/EnvironmentManager.js +20 -0
- package/src/managers/MaterialDataManager.js +118 -208
- package/src/managers/UniformManager.js +1 -0
- package/src/{dlss/DLSSNeural.js → neural/NeuralRetouch.js} +42 -41
- package/src/{dlss/DLSSSuperRes.js → neural/NeuralSuperRes.js} +27 -26
- package/dist/ToneMapGPU-DzqSyzTx.js +0 -341
package/src/PathTracerApp.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WebGPURenderer, RectAreaLightNode, SRGBColorSpace } from 'three/webgpu';
|
|
1
|
+
import { WebGPURenderer, RectAreaLightNode, SRGBColorSpace, LinearSRGBColorSpace } from 'three/webgpu';
|
|
2
2
|
import { texture as _tslTexture, cubeTexture as _tslCubeTexture } from 'three/tsl';
|
|
3
3
|
import {
|
|
4
4
|
Scene, EventDispatcher, Box3
|
|
@@ -20,12 +20,15 @@ import { CompletionTracker } from './Pipeline/CompletionTracker.js';
|
|
|
20
20
|
import { ENGINE_DEFAULTS as DEFAULT_STATE, PRODUCTION_RENDER_CONFIG, INTERACTIVE_RENDER_CONFIG, modePresetSettings, MAX_STORAGE_TEXTURE_SIZE, MAX_RESERVABLE_RENDER_SIZE, setReservedRenderSize, getRenderProfile } from './EngineDefaults.js';
|
|
21
21
|
import { updateStats, updateLoading, resetLoading, setStatusCallback, getDisplaySamples, disposeObjectFromMemory, disposeRenderer } from './Processor/utils.js';
|
|
22
22
|
import { BuildTimer } from './Processor/BuildTimer.js';
|
|
23
|
+
import { TextureReadback } from './Processor/TextureReadback.js';
|
|
23
24
|
import { createLogger, fmt } from './utils/Logger.js';
|
|
24
25
|
import { InteractionManager } from './managers/InteractionManager.js';
|
|
25
26
|
import { EngineEvents } from './EngineEvents.js';
|
|
26
27
|
import { IssueLog, ISSUE_CODES } from './EngineIssues.js';
|
|
27
28
|
import { SETTING_SOURCE } from './RenderSettings.js';
|
|
28
29
|
import { toneMapToRGBA8 } from './Processor/ToneMapCPU.js';
|
|
30
|
+
import { ColorManagement, setActiveColorManagement } from './Color/ColorManagement.js';
|
|
31
|
+
import { getViewTransform } from './Color/ViewTransforms.js';
|
|
29
32
|
import { AssetLoader } from './Processor/AssetLoader.js';
|
|
30
33
|
import { SceneProcessor } from './Processor/SceneProcessor.js';
|
|
31
34
|
|
|
@@ -163,6 +166,15 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
163
166
|
onIssue: ( issue ) => this.dispatchEvent( { type: EngineEvents.ISSUE, issue } ),
|
|
164
167
|
} );
|
|
165
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Colour management: what the engine renders in, shows it as, and hands out.
|
|
171
|
+
*
|
|
172
|
+
* Inert until a host loads an OCIO config — until then the working space is linear
|
|
173
|
+
* Rec.709 and the view transforms are three.js's own seven, exactly as before.
|
|
174
|
+
*/
|
|
175
|
+
this.color = new ColorManagement( { issues: this._issues } );
|
|
176
|
+
setActiveColorManagement( this.color );
|
|
177
|
+
|
|
166
178
|
// ── Settings (single source of truth for all render parameters) ──
|
|
167
179
|
this.settings = new RenderSettings(
|
|
168
180
|
{
|
|
@@ -598,6 +610,12 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
598
610
|
|
|
599
611
|
this._issues.detach(); // onIssue captures `this`; see IssueLog.detach()
|
|
600
612
|
|
|
613
|
+
// Holds the renderer and the issue log, both of which outlive it otherwise.
|
|
614
|
+
this.color?.dispose();
|
|
615
|
+
this.color = null;
|
|
616
|
+
this._textureReadback?.dispose();
|
|
617
|
+
this._textureReadback = null;
|
|
618
|
+
|
|
601
619
|
this.interactionManager?.deselect?.();
|
|
602
620
|
this.transformManager?.detach?.();
|
|
603
621
|
|
|
@@ -2312,15 +2330,23 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
2312
2330
|
* the viewport shows.
|
|
2313
2331
|
*
|
|
2314
2332
|
* @param {Object} [options]
|
|
2315
|
-
* @param {
|
|
2333
|
+
* @param {string} [options.colorSpace='srgb'] - `'srgb'` for display bytes through the active
|
|
2334
|
+
* view transform, `'linear'` for the raw working-space accumulation, or the name of a colour
|
|
2335
|
+
* space in the loaded OCIO config for a delivery buffer (float, e.g. `'ACES2065-1'`)
|
|
2316
2336
|
* @param {boolean} [options.preserveAlpha=false] - srgb only
|
|
2337
|
+
* @param {'accumulation'|'display'} [options.source='accumulation'] - `'display'` reads what the
|
|
2338
|
+
* viewport is showing — denoised when a denoiser has run, without bloom — instead of the raw
|
|
2339
|
+
* accumulation
|
|
2317
2340
|
* @returns {Promise<{data: Float32Array|Uint8ClampedArray, width: number, height: number, colorSpace: string}>}
|
|
2318
2341
|
*/
|
|
2319
|
-
async renderToBuffer( { colorSpace = 'srgb', preserveAlpha = false } = {} ) {
|
|
2342
|
+
async renderToBuffer( { colorSpace = 'srgb', preserveAlpha = false, source = 'accumulation' } = {} ) {
|
|
2320
2343
|
|
|
2321
|
-
|
|
2344
|
+
const named = colorSpace !== 'linear' && colorSpace !== 'srgb';
|
|
2345
|
+
if ( named && ! this.color?.hasConfig ) {
|
|
2322
2346
|
|
|
2323
|
-
throw new Error(
|
|
2347
|
+
throw new Error(
|
|
2348
|
+
`renderToBuffer: colorSpace must be 'linear' or 'srgb' without a colour config loaded, got "${colorSpace}"`
|
|
2349
|
+
);
|
|
2324
2350
|
|
|
2325
2351
|
}
|
|
2326
2352
|
|
|
@@ -2331,9 +2357,27 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
2331
2357
|
// The pool over-allocates to the reserve, so the texture is larger than the frame.
|
|
2332
2358
|
const { width, height } = stage;
|
|
2333
2359
|
|
|
2334
|
-
const linear =
|
|
2360
|
+
const linear = source === 'display'
|
|
2361
|
+
? await this._readDisplaySource( target, width, height )
|
|
2362
|
+
: await this.renderer.readRenderTargetPixelsAsync( target, 0, 0, width, height, 0 );
|
|
2363
|
+
|
|
2364
|
+
// `colorSpace` stays 'linear' — callers branch on it. `workingSpace` is the new, additive
|
|
2365
|
+
// answer to "linear in what primaries", which only means something once a config is loaded.
|
|
2366
|
+
if ( colorSpace === 'linear' ) {
|
|
2367
|
+
|
|
2368
|
+
return { data: linear, width, height, colorSpace, workingSpace: this.color?.workingSpace ?? null };
|
|
2369
|
+
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
// A named space is a delivery buffer, not a picture: scene-referred float in whatever the
|
|
2373
|
+
// config calls that space. Saving an EXR for a compositor and grading through an sRGB view
|
|
2374
|
+
// on screen are different questions, and this is the one that answers the first.
|
|
2375
|
+
if ( named ) {
|
|
2376
|
+
|
|
2377
|
+
const { rgba, colorSpace: got } = this.color.exportPixels( linear, colorSpace );
|
|
2378
|
+
return { data: rgba, width, height, colorSpace: got };
|
|
2335
2379
|
|
|
2336
|
-
|
|
2380
|
+
}
|
|
2337
2381
|
|
|
2338
2382
|
return {
|
|
2339
2383
|
data: toneMapToRGBA8( linear, {
|
|
@@ -2765,6 +2809,20 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
2765
2809
|
|
|
2766
2810
|
}
|
|
2767
2811
|
|
|
2812
|
+
/**
|
|
2813
|
+
* Where a material property's value came from: `'material'` (the three.js material carried
|
|
2814
|
+
* it), `'mapped'` (converted from a Basic/Lambert/Phong/Toon material), `'default'` (the model
|
|
2815
|
+
* never said; filled from MATERIAL_DEFAULTS) or `'host'` (set through setMaterialProperty).
|
|
2816
|
+
* @param {number} materialIndex
|
|
2817
|
+
* @param {string} property
|
|
2818
|
+
* @returns {string|undefined}
|
|
2819
|
+
*/
|
|
2820
|
+
getMaterialPropertySource( materialIndex, property ) {
|
|
2821
|
+
|
|
2822
|
+
return this.stages.pathTracer?.materialData.getMaterialPropertySource( materialIndex, property );
|
|
2823
|
+
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2768
2826
|
setMaterialProperty( materialIndex, property, value ) {
|
|
2769
2827
|
|
|
2770
2828
|
this.stages.pathTracer?.materialData.updateMaterialProperty( materialIndex, property, value );
|
|
@@ -2917,6 +2975,163 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
2917
2975
|
|
|
2918
2976
|
}
|
|
2919
2977
|
|
|
2978
|
+
/**
|
|
2979
|
+
* What the Compositor is drawing from, as linear float. Falls back to the accumulation when no
|
|
2980
|
+
* denoiser has published anything, which is also what the viewport shows then.
|
|
2981
|
+
* @private
|
|
2982
|
+
*/
|
|
2983
|
+
async _readDisplaySource( accumulation, width, height ) {
|
|
2984
|
+
|
|
2985
|
+
const context = this.pipeline?.context;
|
|
2986
|
+
const texture = context && this.stages.compositor?.resolveLightTexture( context );
|
|
2987
|
+
if ( ! texture || texture === accumulation.texture ) {
|
|
2988
|
+
|
|
2989
|
+
return await this.renderer.readRenderTargetPixelsAsync( accumulation, 0, 0, width, height, 0 );
|
|
2990
|
+
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
this._textureReadback ??= new TextureReadback( this.renderer );
|
|
2994
|
+
return await this._textureReadback.read( texture, width, height );
|
|
2995
|
+
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
/**
|
|
2999
|
+
* Set what colour space one texture is in, then rebuild so it takes effect.
|
|
3000
|
+
*
|
|
3001
|
+
* The per-image colour space every OCIO application offers. Three kinds of answer:
|
|
3002
|
+
* - `null` — automatic: tag, override, the config's file rules, then three.js
|
|
3003
|
+
* - `'srgb'`/`'linear'` — three.js's own two, which work with no config at all
|
|
3004
|
+
* - a config space — anything the loaded config names, including log and camera spaces
|
|
3005
|
+
*
|
|
3006
|
+
* A config space is carried as `userData.ocioColorSpace`, and the texture's three.js colour space
|
|
3007
|
+
* is set to match the colour pool while it is, so harmonization leaves the stored bytes as the
|
|
3008
|
+
* author wrote them — the named transform has to read them raw. The original is kept and put
|
|
3009
|
+
* back when the choice is cleared.
|
|
3010
|
+
*
|
|
3011
|
+
* @param {import('three').Texture} texture
|
|
3012
|
+
* @param {?string} choice
|
|
3013
|
+
*/
|
|
3014
|
+
async setTextureColorSpace( texture, choice ) {
|
|
3015
|
+
|
|
3016
|
+
if ( ! texture?.isTexture ) throw new Error( 'setTextureColorSpace wants a three.js texture' );
|
|
3017
|
+
|
|
3018
|
+
const ud = texture.userData ?? ( texture.userData = {} );
|
|
3019
|
+
if ( ud.__rayzeeOriginalColorSpace === undefined ) ud.__rayzeeOriginalColorSpace = texture.colorSpace;
|
|
3020
|
+
const original = ud.__rayzeeOriginalColorSpace;
|
|
3021
|
+
|
|
3022
|
+
delete ud.ocioColorSpace;
|
|
3023
|
+
delete ud.__rayzeeColorSpaceChoice;
|
|
3024
|
+
|
|
3025
|
+
if ( choice === null || choice === undefined ) {
|
|
3026
|
+
|
|
3027
|
+
texture.colorSpace = original;
|
|
3028
|
+
delete ud.__rayzeeOriginalColorSpace;
|
|
3029
|
+
|
|
3030
|
+
} else if ( choice === 'srgb' || choice === 'linear' ) {
|
|
3031
|
+
|
|
3032
|
+
texture.colorSpace = choice === 'srgb' ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
3033
|
+
ud.__rayzeeColorSpaceChoice = choice;
|
|
3034
|
+
|
|
3035
|
+
} else {
|
|
3036
|
+
|
|
3037
|
+
if ( ! this.color?.hasConfig ) throw new Error( `"${choice}" needs a colour config loaded` );
|
|
3038
|
+
ud.ocioColorSpace = choice;
|
|
3039
|
+
texture.colorSpace = SRGBColorSpace;
|
|
3040
|
+
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
texture.needsUpdate = true;
|
|
3044
|
+
if ( this.stages.pathTracer?.sdfs ) await this.rebuildMaterials();
|
|
3045
|
+
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
/**
|
|
3049
|
+
* Load a colour config, keeping the scene consistent with it.
|
|
3050
|
+
*
|
|
3051
|
+
* Prefer this to `app.color.loadConfig()` whenever a scene is loaded. A working space adopted
|
|
3052
|
+
* under the previous config is undone first, *while that config is still loaded* — the
|
|
3053
|
+
* environment is converted in place, and only the config that converted it can convert it
|
|
3054
|
+
* back. With `adoptWorkingSpace` the scene is rebuilt in the new space afterwards.
|
|
3055
|
+
*
|
|
3056
|
+
* @param {Object} options - as `ColorManagement.loadConfig`
|
|
3057
|
+
* @returns {Promise<Object>} the config description
|
|
3058
|
+
*/
|
|
3059
|
+
async loadColorConfig( options = {} ) {
|
|
3060
|
+
|
|
3061
|
+
await this._leaveColorWorkingSpace();
|
|
3062
|
+
const shown = getViewTransform( this.renderer?.toneMapping );
|
|
3063
|
+
const described = await this.color.loadConfig( options );
|
|
3064
|
+
if ( this.color.workingSpaceAdopted ) await this.applyColorWorkingSpace();
|
|
3065
|
+
// A baked view the config kept is still what is on screen.
|
|
3066
|
+
const kept = shown?.source === 'ocio' && getViewTransform( this.renderer?.toneMapping ) === shown;
|
|
3067
|
+
if ( ! kept || this.color.workingSpaceAdopted ) this.reset();
|
|
3068
|
+
return described;
|
|
3069
|
+
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
/** Unload the colour config, putting the scene back into linear Rec.709 first. */
|
|
3073
|
+
async unloadColorConfig() {
|
|
3074
|
+
|
|
3075
|
+
await this._leaveColorWorkingSpace();
|
|
3076
|
+
this.color.unloadConfig();
|
|
3077
|
+
this.reset();
|
|
3078
|
+
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
/** @private */
|
|
3082
|
+
async _leaveColorWorkingSpace() {
|
|
3083
|
+
|
|
3084
|
+
if ( ! this.color?.workingSpaceAdopted ) return;
|
|
3085
|
+
this.color.setWorkingSpace( null );
|
|
3086
|
+
await this.applyColorWorkingSpace();
|
|
3087
|
+
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
/**
|
|
3091
|
+
* Rebuild everything the working space touches, after it has been changed.
|
|
3092
|
+
*
|
|
3093
|
+
* Changing what the engine renders in is not a display setting — it changes what every texture,
|
|
3094
|
+
* tint and light colour already loaded *means*. Textures and materials are re-packed from their
|
|
3095
|
+
* pristine three.js sources, so they convert cleanly; the environment is converted where it
|
|
3096
|
+
* lies, from whichever space it currently holds, and its importance-sampling table is rebuilt
|
|
3097
|
+
* because its pixels moved.
|
|
3098
|
+
*
|
|
3099
|
+
* Call this after `app.color.setWorkingSpace()`. Doing nothing instead leaves a scene half in
|
|
3100
|
+
* one space and half in another, which reads as a colour cast with no obvious cause.
|
|
3101
|
+
*/
|
|
3102
|
+
async applyColorWorkingSpace() {
|
|
3103
|
+
|
|
3104
|
+
const env = this.environmentManager?.getEnvironmentTexture?.();
|
|
3105
|
+
let envChanged = false;
|
|
3106
|
+
|
|
3107
|
+
if ( env && this.color?.hasConfig ) {
|
|
3108
|
+
|
|
3109
|
+
try {
|
|
3110
|
+
|
|
3111
|
+
envChanged = this.color.convertTexturePixels( env );
|
|
3112
|
+
|
|
3113
|
+
} catch ( error ) {
|
|
3114
|
+
|
|
3115
|
+
log.warn( `environment colour conversion failed: ${error.message}` );
|
|
3116
|
+
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
// Nothing to rebuild before a scene exists; anything loaded later converts on its way in.
|
|
3122
|
+
if ( this.stages.pathTracer?.sdfs ) {
|
|
3123
|
+
|
|
3124
|
+
await this.rebuildMaterials();
|
|
3125
|
+
this._uploadEmissivePayload( this._sdf?.rebuildEmissiveColors?.() ?? null );
|
|
3126
|
+
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
if ( envChanged ) await this.environmentManager?.buildEnvironmentCDF?.();
|
|
3130
|
+
|
|
3131
|
+
this.reset();
|
|
3132
|
+
|
|
3133
|
+
}
|
|
3134
|
+
|
|
2920
3135
|
/**
|
|
2921
3136
|
* Full material rebuild (required after texture changes).
|
|
2922
3137
|
* @param {import('three').Scene} [scene]
|
|
@@ -3044,6 +3259,11 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
3044
3259
|
this.renderer.toneMappingExposure = 1.0;
|
|
3045
3260
|
this.renderer.setPixelRatio( 1.0 );
|
|
3046
3261
|
|
|
3262
|
+
// Hands every registered view transform to the renderer and keeps `outputColorSpace` in
|
|
3263
|
+
// step with whichever is selected — an OCIO view already encoded for its display must not
|
|
3264
|
+
// be encoded a second time by the output pass.
|
|
3265
|
+
this.color.attachRenderer( this.renderer );
|
|
3266
|
+
|
|
3047
3267
|
}
|
|
3048
3268
|
|
|
3049
3269
|
_initCameraManager() {
|
|
@@ -851,8 +851,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
851
851
|
this.targetModel = group;
|
|
852
852
|
|
|
853
853
|
// The light's own orientation and `scale` are already baked into the texture, so the
|
|
854
|
-
// scene is only correct at rotation 0 / intensity 1
|
|
855
|
-
// default 270° rotation lands on top and the sky sits 90° from where pbrt puts it.
|
|
854
|
+
// scene is only correct at rotation 0 / intensity 1 — pinned, whatever the host's defaults.
|
|
856
855
|
if ( environment?.texture ) this.sceneMetadata = { environment: { rotation: 0, intensity: 1 } };
|
|
857
856
|
|
|
858
857
|
updateLoading( { isLoading: true, status: 'Processing PBRT geometry...', progress: 10 } );
|
|
@@ -10,6 +10,28 @@ import { DataTexture, RGBAFormat, FloatType, NearestFilter } from 'three';
|
|
|
10
10
|
import { TRIANGLE_DATA_LAYOUT, TRI_MATERIAL_MASK } from '../EngineDefaults.js';
|
|
11
11
|
import { LightBVHBuilder } from './LightBVHBuilder.js';
|
|
12
12
|
import { createLogger, fmt } from '../utils/Logger.js';
|
|
13
|
+
import { convertLinearTriple, getWorkingMatrix } from '../Color/WorkingMatrix.js';
|
|
14
|
+
|
|
15
|
+
const NO_EMISSION = Object.freeze( { r: 0, g: 0, b: 0 } );
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A material's emission in the working space.
|
|
19
|
+
*
|
|
20
|
+
* The emitter seen directly reads the material buffer, which is already converted, and so does the
|
|
21
|
+
* shader's pick probability. This buffer is the other copy of the same colour — the one next-event
|
|
22
|
+
* estimation lights the scene with — so it has to move with them, or a working space adopted from a
|
|
23
|
+
* config lights everything in the wrong primaries and weights the pick by a colour nothing renders.
|
|
24
|
+
*/
|
|
25
|
+
function workingEmissive( emissive ) {
|
|
26
|
+
|
|
27
|
+
if ( ! emissive ) return NO_EMISSION;
|
|
28
|
+
if ( ! getWorkingMatrix() ) return emissive;
|
|
29
|
+
|
|
30
|
+
const rgb = [ emissive.r, emissive.g, emissive.b ];
|
|
31
|
+
convertLinearTriple( rgb );
|
|
32
|
+
return { r: rgb[ 0 ], g: rgb[ 1 ], b: rgb[ 2 ] };
|
|
33
|
+
|
|
34
|
+
}
|
|
13
35
|
|
|
14
36
|
const log = createLogger( 'emissive' );
|
|
15
37
|
|
|
@@ -62,6 +84,9 @@ export class EmissiveTriangleBuilder {
|
|
|
62
84
|
const flatU = chunked ? null : triangleData;
|
|
63
85
|
const flatF = chunked ? null : new Float32Array( triangleData.buffer, triangleData.byteOffset, triangleData.length );
|
|
64
86
|
|
|
87
|
+
// Once per material, not once per triangle: this loop runs over every triangle in the scene.
|
|
88
|
+
const emissiveOf = new Map();
|
|
89
|
+
|
|
65
90
|
for ( let i = 0; i < triangleCount; i ++ ) {
|
|
66
91
|
|
|
67
92
|
const triU = chunked ? chunked.chunkFor( i ) : flatU;
|
|
@@ -75,7 +100,14 @@ export class EmissiveTriangleBuilder {
|
|
|
75
100
|
if ( ! material ) continue;
|
|
76
101
|
|
|
77
102
|
// Check if emissive
|
|
78
|
-
|
|
103
|
+
let emissive = emissiveOf.get( material );
|
|
104
|
+
if ( emissive === undefined ) {
|
|
105
|
+
|
|
106
|
+
emissive = workingEmissive( material.emissive );
|
|
107
|
+
emissiveOf.set( material, emissive );
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
79
111
|
const emissiveIntensity = material.emissiveIntensity || 0;
|
|
80
112
|
|
|
81
113
|
const isEmissive = emissiveIntensity > 0 && (
|
|
@@ -461,7 +493,7 @@ export class EmissiveTriangleBuilder {
|
|
|
461
493
|
*/
|
|
462
494
|
updateMaterialEmissive( materialIndex, material, triangleData, materials, triangleCount ) {
|
|
463
495
|
|
|
464
|
-
const emissive = material.emissive
|
|
496
|
+
const emissive = workingEmissive( material.emissive );
|
|
465
497
|
const emissiveIntensity = material.emissiveIntensity || 0;
|
|
466
498
|
const isNowEmissive = emissiveIntensity > 0 && ( emissive.r > 0 || emissive.g > 0 || emissive.b > 0 );
|
|
467
499
|
|