rayzee 8.4.1 → 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.
- package/README.md +61 -9
- package/dist/{DLSSNeural-BNSJ8V3z.js → DLSSNeural-BhBCBgKf.js} +5 -5
- package/dist/{DLSSSuperRes-BoEW89Cl.js → DLSSSuperRes-BCA7F_k5.js} +5 -5
- package/dist/ToneMapGPU-BzcLheoR.js +533 -0
- package/dist/rayzee.es.js +27278 -9133
- package/dist/rayzee.umd.js +255 -187
- package/package.json +1 -1
- package/src/AssetConfig.js +15 -2
- 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 +84 -4
- package/src/EngineIssues.js +3 -1
- package/src/Passes/AIUpscaler.js +16 -7
- package/src/PathTracerApp.js +318 -35
- package/src/Processor/AssetLoader.js +47 -55
- package/src/Processor/CameraOptimizer.js +22 -6
- package/src/Processor/EmissiveTriangleBuilder.js +34 -2
- package/src/Processor/GeometryExtractor.js +151 -138
- package/src/Processor/LightSerializer.js +17 -20
- 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 +10 -1
- package/src/Stages/Compositor.js +11 -2
- package/src/Stages/PathTracer.js +21 -4
- package/src/Stages/PathTracerStage.js +11 -18
- 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 +29 -2
- package/src/managers/DenoisingManager.js +3 -7
- package/src/managers/EnvironmentManager.js +20 -0
- package/src/managers/LightManager.js +17 -15
- package/src/managers/MaterialDataManager.js +118 -208
- package/src/managers/UniformManager.js +1 -0
- package/src/managers/VideoRenderManager.js +9 -9
- package/dist/ToneMapGPU-DzqSyzTx.js +0 -341
- package/src/LightUnits.js +0 -122
|
@@ -19,12 +19,11 @@ export class CameraOptimizer {
|
|
|
19
19
|
this.interactionMode = false;
|
|
20
20
|
this.interactionTimeout = null;
|
|
21
21
|
this.originalValues = {};
|
|
22
|
+
this.appliedValues = {};
|
|
22
23
|
this.wasAccumulationEnabled = true; // Track original accumulation state
|
|
23
24
|
|
|
24
25
|
// Enhanced interaction mode settings for reduced quality during interaction
|
|
25
26
|
this.interactionQualitySettings = {
|
|
26
|
-
maxBounceCount: 1,
|
|
27
|
-
// pixelRatio: 0.25,
|
|
28
27
|
enableAccumulation: false,
|
|
29
28
|
...settings.qualitySettings
|
|
30
29
|
};
|
|
@@ -54,6 +53,7 @@ export class CameraOptimizer {
|
|
|
54
53
|
// Enter interaction mode and save original values
|
|
55
54
|
this.interactionMode = true;
|
|
56
55
|
this.originalValues = {}; // Reset stored values
|
|
56
|
+
this.appliedValues = {};
|
|
57
57
|
|
|
58
58
|
// Store original accumulation state before any changes
|
|
59
59
|
if ( this.material.uniforms.enableAccumulation ) {
|
|
@@ -80,7 +80,8 @@ export class CameraOptimizer {
|
|
|
80
80
|
|
|
81
81
|
// Handle material uniforms
|
|
82
82
|
this.originalValues[ key ] = this.material.uniforms[ key ].value;
|
|
83
|
-
this.material.uniforms[ key ].value = this.interactionQualitySettings[ key ];
|
|
83
|
+
this.material.uniforms[ key ].value = this._resolveQualityValue( key, this.interactionQualitySettings[ key ] );
|
|
84
|
+
this.appliedValues[ key ] = this.material.uniforms[ key ].value;
|
|
84
85
|
|
|
85
86
|
}
|
|
86
87
|
|
|
@@ -136,8 +137,13 @@ export class CameraOptimizer {
|
|
|
136
137
|
|
|
137
138
|
} else if ( this.material.uniforms[ key ] ) {
|
|
138
139
|
|
|
139
|
-
//
|
|
140
|
-
|
|
140
|
+
// A value written while we held it (a settings change) is newer than the one we saved.
|
|
141
|
+
const applied = this.appliedValues[ key ];
|
|
142
|
+
if ( applied === undefined || this.material.uniforms[ key ].value === applied ) {
|
|
143
|
+
|
|
144
|
+
this.material.uniforms[ key ].value = this.originalValues[ key ];
|
|
145
|
+
|
|
146
|
+
}
|
|
141
147
|
|
|
142
148
|
}
|
|
143
149
|
|
|
@@ -145,6 +151,7 @@ export class CameraOptimizer {
|
|
|
145
151
|
|
|
146
152
|
this.interactionMode = false;
|
|
147
153
|
this.originalValues = {}; // Clear stored values
|
|
154
|
+
this.appliedValues = {};
|
|
148
155
|
|
|
149
156
|
// Call exit callback first (this may trigger a reset)
|
|
150
157
|
if ( this.onExitCallback ) {
|
|
@@ -213,7 +220,8 @@ export class CameraOptimizer {
|
|
|
213
220
|
|
|
214
221
|
} else if ( this.material.uniforms[ key ] ) {
|
|
215
222
|
|
|
216
|
-
this.material.uniforms[ key ].value = settings[ key ];
|
|
223
|
+
this.material.uniforms[ key ].value = this._resolveQualityValue( key, settings[ key ] );
|
|
224
|
+
this.appliedValues[ key ] = this.material.uniforms[ key ].value;
|
|
217
225
|
|
|
218
226
|
}
|
|
219
227
|
|
|
@@ -223,6 +231,14 @@ export class CameraOptimizer {
|
|
|
223
231
|
|
|
224
232
|
}
|
|
225
233
|
|
|
234
|
+
// A function derives the interaction value from the one it replaces.
|
|
235
|
+
_resolveQualityValue( key, target ) {
|
|
236
|
+
|
|
237
|
+
if ( typeof target !== 'function' ) return target;
|
|
238
|
+
return target( this.originalValues[ key ] ?? this.material.uniforms[ key ].value );
|
|
239
|
+
|
|
240
|
+
}
|
|
241
|
+
|
|
226
242
|
/**
|
|
227
243
|
* Set the interaction delay
|
|
228
244
|
* @param {number} delay - Delay in milliseconds
|
|
@@ -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
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BufferAttribute, Vector3, Vector2, Color, Matrix3, Matrix4, FrontSide, BackSide, DoubleSide, RGBAFormat } from "three";
|
|
2
2
|
import {
|
|
3
|
-
TEXTURE_CONSTANTS, TRIANGLE_DATA_LAYOUT, packNormalOct, packTriangleFlags
|
|
3
|
+
TEXTURE_CONSTANTS, TRIANGLE_DATA_LAYOUT, MATERIAL_DEFAULTS, packNormalOct, packTriangleFlags
|
|
4
4
|
} from '../EngineDefaults.js';
|
|
5
5
|
import { ISSUE_CODES } from '../EngineIssues.js';
|
|
6
6
|
import { ChunkedRecords, SHARED_MEMORY_AVAILABLE } from './ChunkedRecords.js';
|
|
@@ -10,6 +10,20 @@ const log = createLogger( 'geometry' );
|
|
|
10
10
|
|
|
11
11
|
const MAX_TEXTURES_LIMIT = TEXTURE_CONSTANTS.MAX_TEXTURES_LIMIT;
|
|
12
12
|
|
|
13
|
+
const COLOR_DEFAULTS = new Set( [ 'color', 'emissive', 'attenuationColor', 'sheenColor', 'specularColor', 'subsurfaceColor' ] );
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Where a packed material value came from: the three.js material carried it, a legacy-type
|
|
17
|
+
* conversion (Basic/Lambert/Phong/Toon) produced it, the engine filled it from
|
|
18
|
+
* MATERIAL_DEFAULTS, or a host set it at runtime through setMaterialProperty.
|
|
19
|
+
*/
|
|
20
|
+
export const MATERIAL_VALUE_SOURCE = Object.freeze( {
|
|
21
|
+
MATERIAL: 'material',
|
|
22
|
+
MAPPED: 'mapped',
|
|
23
|
+
DEFAULT: 'default',
|
|
24
|
+
HOST: 'host',
|
|
25
|
+
} );
|
|
26
|
+
|
|
13
27
|
const IDENTITY_ELEMENTS = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ];
|
|
14
28
|
|
|
15
29
|
function isIdentityElements( e ) {
|
|
@@ -19,6 +33,36 @@ function isIdentityElements( e ) {
|
|
|
19
33
|
|
|
20
34
|
}
|
|
21
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The texture that feeds each GPU map slot. Re-packing texture indices without this loses the
|
|
38
|
+
* unlit routing (a MeshBasicMaterial's map is also its emission map).
|
|
39
|
+
* @param {import('three').Material} material
|
|
40
|
+
* @returns {Object<string, import('three').Texture|undefined>}
|
|
41
|
+
*/
|
|
42
|
+
export function resolveMaterialTextures( material ) {
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
map: material.map,
|
|
46
|
+
normalMap: material.normalMap,
|
|
47
|
+
bumpMap: material.bumpMap,
|
|
48
|
+
roughnessMap: material.roughnessMap,
|
|
49
|
+
metalnessMap: material.metalnessMap,
|
|
50
|
+
emissiveMap: material.emissiveMap ?? ( material.isMeshBasicMaterial ? material.map : undefined ),
|
|
51
|
+
displacementMap: material.displacementMap,
|
|
52
|
+
anisotropyMap: material.anisotropyMap,
|
|
53
|
+
clearcoatMap: material.clearcoatMap,
|
|
54
|
+
clearcoatRoughnessMap: material.clearcoatRoughnessMap,
|
|
55
|
+
transmissionMap: material.transmissionMap,
|
|
56
|
+
sheenColorMap: material.sheenColorMap,
|
|
57
|
+
sheenRoughnessMap: material.sheenRoughnessMap,
|
|
58
|
+
specularIntensityMap: material.specularIntensityMap,
|
|
59
|
+
specularColorMap: material.specularColorMap,
|
|
60
|
+
iridescenceMap: material.iridescenceMap,
|
|
61
|
+
iridescenceThicknessMap: material.iridescenceThicknessMap,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
22
66
|
/**
|
|
23
67
|
* glTF 2.0 alphaMode for a three.js material: 0 OPAQUE, 1 MASK, 2 BLEND.
|
|
24
68
|
*
|
|
@@ -734,43 +778,15 @@ export class GeometryExtractor {
|
|
|
734
778
|
|
|
735
779
|
getPhysicalDefaults() {
|
|
736
780
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
thickness: 0.1,
|
|
747
|
-
attenuationColor: new Color( 0xffffff ),
|
|
748
|
-
attenuationDistance: Infinity, // No attenuation by default
|
|
749
|
-
dispersion: 0.0,
|
|
750
|
-
sheen: 0.0,
|
|
751
|
-
sheenRoughness: 1.0,
|
|
752
|
-
sheenColor: new Color( 0x000000 ),
|
|
753
|
-
specularIntensity: 1.0,
|
|
754
|
-
specularColor: new Color( 0xffffff ),
|
|
755
|
-
clearcoat: 0.0,
|
|
756
|
-
clearcoatRoughness: 0.0,
|
|
757
|
-
iridescence: 0.0,
|
|
758
|
-
iridescenceIOR: 1.3,
|
|
759
|
-
iridescenceThicknessRange: [ 100, 400 ],
|
|
760
|
-
normalScale: { x: 1, y: 1 },
|
|
761
|
-
bumpScale: 1.0,
|
|
762
|
-
displacementScale: 1.0,
|
|
763
|
-
alphaTest: 0.0,
|
|
764
|
-
// Subsurface scattering (no native MeshPhysicalMaterial equivalent)
|
|
765
|
-
subsurface: 0.0,
|
|
766
|
-
subsurfaceColor: new Color( 0xffffff ),
|
|
767
|
-
subsurfaceRadius: [ 1.0, 0.2, 0.1 ], // skin-like: red travels furthest
|
|
768
|
-
subsurfaceRadiusScale: 1.0,
|
|
769
|
-
subsurfaceAnisotropy: 0.0,
|
|
770
|
-
// Surface specular anisotropy (native MeshPhysicalMaterial / KHR_materials_anisotropy)
|
|
771
|
-
anisotropy: 0.0,
|
|
772
|
-
anisotropyRotation: 0.0
|
|
773
|
-
};
|
|
781
|
+
const defaults = {};
|
|
782
|
+
for ( const [ key, value ] of Object.entries( MATERIAL_DEFAULTS ) ) {
|
|
783
|
+
|
|
784
|
+
defaults[ key ] = COLOR_DEFAULTS.has( key ) ? new Color().fromArray( value ) : Array.isArray( value ) ? [ ...value ] : value;
|
|
785
|
+
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
defaults.normalScale = { x: MATERIAL_DEFAULTS.normalScale[ 0 ], y: MATERIAL_DEFAULTS.normalScale[ 1 ] };
|
|
789
|
+
return defaults;
|
|
774
790
|
|
|
775
791
|
}
|
|
776
792
|
|
|
@@ -782,12 +798,13 @@ export class GeometryExtractor {
|
|
|
782
798
|
switch ( materialType ) {
|
|
783
799
|
|
|
784
800
|
case 'basic':
|
|
785
|
-
//
|
|
801
|
+
// Unlit (GLTFLoader's KHR_materials_unlit): colour and texture are emission, nothing reflects.
|
|
786
802
|
mapped.emissive = material.color.clone();
|
|
787
803
|
mapped.emissiveIntensity = 1.0;
|
|
788
|
-
mapped.color = new Color( 0x000000 );
|
|
804
|
+
mapped.color = new Color( 0x000000 );
|
|
789
805
|
mapped.roughness = 1.0;
|
|
790
806
|
mapped.metalness = 0.0;
|
|
807
|
+
mapped.specularIntensity = 0.0;
|
|
791
808
|
break;
|
|
792
809
|
|
|
793
810
|
case 'lambert':
|
|
@@ -807,14 +824,14 @@ export class GeometryExtractor {
|
|
|
807
824
|
|
|
808
825
|
}
|
|
809
826
|
|
|
810
|
-
//
|
|
827
|
+
// Strength in specularIntensity, hue alone in specularColor; carrying it in both squared it.
|
|
811
828
|
if ( material.specular ) {
|
|
812
829
|
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
830
|
+
const s = material.specular;
|
|
831
|
+
const specularLuminance = s.r * 0.299 + s.g * 0.587 + s.b * 0.114;
|
|
832
|
+
const peak = Math.max( s.r, s.g, s.b );
|
|
816
833
|
mapped.specularIntensity = Math.min( specularLuminance * 2.0, 1.0 );
|
|
817
|
-
mapped.specularColor =
|
|
834
|
+
mapped.specularColor = peak > 0 ? s.clone().multiplyScalar( 1 / peak ) : new Color( 1, 1, 1 );
|
|
818
835
|
|
|
819
836
|
}
|
|
820
837
|
|
|
@@ -842,121 +859,117 @@ export class GeometryExtractor {
|
|
|
842
859
|
const defaults = this.getPhysicalDefaults();
|
|
843
860
|
const materialType = this.getMaterialType( material );
|
|
844
861
|
const legacyMapping = this.mapLegacyMaterialToPhysical( material, materialType );
|
|
862
|
+
const textures = resolveMaterialTextures( material );
|
|
863
|
+
const sources = {};
|
|
845
864
|
|
|
846
|
-
|
|
847
|
-
// over the metal Fresnel hack: glTF defaults metallicFactor to 1, which otherwise stamps
|
|
848
|
-
// 2.5 onto water/glass authored as MeshStandardMaterial.
|
|
849
|
-
const isTransmissive = ( material.transmission ?? 0.0 ) > 0.0;
|
|
850
|
-
const isMetallic = ( material.metalness ?? legacyMapping.metalness ?? 0.0 ) > 0.1;
|
|
851
|
-
const defaultIOR = ( isMetallic && ! isTransmissive ) ? 2.5 : defaults.ior;
|
|
865
|
+
const pick = ( key ) => {
|
|
852
866
|
|
|
853
|
-
|
|
854
|
-
let baseColor = material.color || new Color( 0xffffff );
|
|
855
|
-
if ( materialType === 'basic' && ! material.map ) {
|
|
867
|
+
if ( legacyMapping[ key ] !== undefined ) {
|
|
856
868
|
|
|
857
|
-
|
|
858
|
-
|
|
869
|
+
sources[ key ] = MATERIAL_VALUE_SOURCE.MAPPED;
|
|
870
|
+
return legacyMapping[ key ];
|
|
859
871
|
|
|
860
|
-
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if ( material[ key ] !== undefined && material[ key ] !== null ) {
|
|
875
|
+
|
|
876
|
+
sources[ key ] = MATERIAL_VALUE_SOURCE.MATERIAL;
|
|
877
|
+
return material[ key ];
|
|
878
|
+
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
sources[ key ] = MATERIAL_VALUE_SOURCE.DEFAULT;
|
|
882
|
+
return defaults[ key ];
|
|
883
|
+
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
for ( const key of [ 'transparent', 'alphaMode', 'side', 'depthWrite' ] ) sources[ key ] = MATERIAL_VALUE_SOURCE.MATERIAL;
|
|
861
887
|
|
|
862
888
|
return {
|
|
863
889
|
uuid: material.uuid,
|
|
864
890
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
emissiveIntensity: legacyMapping.emissiveIntensity ?? material.emissiveIntensity ?? defaults.emissiveIntensity,
|
|
891
|
+
color: pick( 'color' ),
|
|
892
|
+
emissive: pick( 'emissive' ),
|
|
893
|
+
emissiveIntensity: pick( 'emissiveIntensity' ),
|
|
869
894
|
|
|
870
|
-
// Surface properties
|
|
871
895
|
// Floor at 0.02 (the sampler's own VNDF-PDF clamp, MaterialProperties.js) rather than
|
|
872
896
|
// 0.05, so near-mirror metals stay sharp without entering a new firefly regime.
|
|
873
|
-
roughness: Math.max( 0.02,
|
|
874
|
-
metalness:
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
specularColor: legacyMapping.specularColor ?? material.specularColor ?? defaults.specularColor,
|
|
911
|
-
|
|
912
|
-
// Surface detail properties
|
|
913
|
-
normalScale: material.normalScale ?? defaults.normalScale,
|
|
914
|
-
bumpScale: material.bumpScale ?? defaults.bumpScale,
|
|
915
|
-
displacementScale: material.displacementScale ?? defaults.displacementScale,
|
|
916
|
-
|
|
917
|
-
// Transparency and alpha
|
|
897
|
+
roughness: Math.max( 0.02, pick( 'roughness' ) ),
|
|
898
|
+
metalness: pick( 'metalness' ),
|
|
899
|
+
|
|
900
|
+
ior: pick( 'ior' ),
|
|
901
|
+
opacity: pick( 'opacity' ),
|
|
902
|
+
|
|
903
|
+
transmission: pick( 'transmission' ),
|
|
904
|
+
thickness: pick( 'thickness' ),
|
|
905
|
+
attenuationColor: pick( 'attenuationColor' ),
|
|
906
|
+
attenuationDistance: pick( 'attenuationDistance' ),
|
|
907
|
+
|
|
908
|
+
dispersion: pick( 'dispersion' ),
|
|
909
|
+
sheen: pick( 'sheen' ),
|
|
910
|
+
sheenRoughness: pick( 'sheenRoughness' ),
|
|
911
|
+
sheenColor: pick( 'sheenColor' ),
|
|
912
|
+
clearcoat: pick( 'clearcoat' ),
|
|
913
|
+
clearcoatRoughness: pick( 'clearcoatRoughness' ),
|
|
914
|
+
iridescence: pick( 'iridescence' ),
|
|
915
|
+
iridescenceIOR: pick( 'iridescenceIOR' ),
|
|
916
|
+
iridescenceThicknessRange: pick( 'iridescenceThicknessRange' ),
|
|
917
|
+
|
|
918
|
+
subsurface: pick( 'subsurface' ),
|
|
919
|
+
subsurfaceColor: pick( 'subsurfaceColor' ),
|
|
920
|
+
subsurfaceRadius: pick( 'subsurfaceRadius' ),
|
|
921
|
+
subsurfaceRadiusScale: pick( 'subsurfaceRadiusScale' ),
|
|
922
|
+
subsurfaceAnisotropy: pick( 'subsurfaceAnisotropy' ),
|
|
923
|
+
|
|
924
|
+
anisotropy: pick( 'anisotropy' ),
|
|
925
|
+
anisotropyRotation: pick( 'anisotropyRotation' ),
|
|
926
|
+
|
|
927
|
+
specularIntensity: pick( 'specularIntensity' ),
|
|
928
|
+
specularColor: pick( 'specularColor' ),
|
|
929
|
+
|
|
930
|
+
normalScale: pick( 'normalScale' ),
|
|
931
|
+
bumpScale: pick( 'bumpScale' ),
|
|
932
|
+
displacementScale: pick( 'displacementScale' ),
|
|
933
|
+
|
|
918
934
|
transparent: material.transparent ? 1 : 0,
|
|
919
|
-
alphaTest:
|
|
935
|
+
alphaTest: pick( 'alphaTest' ),
|
|
920
936
|
alphaMode: deriveAlphaMode( material ),
|
|
921
937
|
|
|
922
|
-
// Rendering properties
|
|
923
938
|
side: this.getMaterialSide( material ),
|
|
924
939
|
depthWrite: material.depthWrite ?? true ? 1 : 0,
|
|
925
940
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
anisotropyMap: this.processTexture( material.anisotropyMap, this.anisotropyMaps ),
|
|
941
|
+
map: this.processTexture( textures.map, this.maps ),
|
|
942
|
+
normalMap: this.processTexture( textures.normalMap, this.normalMaps ),
|
|
943
|
+
bumpMap: this.processTexture( textures.bumpMap, this.bumpMaps ),
|
|
944
|
+
roughnessMap: this.processTexture( textures.roughnessMap, this.roughnessMaps ),
|
|
945
|
+
metalnessMap: this.processTexture( textures.metalnessMap, this.metalnessMaps ),
|
|
946
|
+
emissiveMap: this.processTexture( textures.emissiveMap, this.emissiveMaps ),
|
|
947
|
+
displacementMap: this.processTexture( textures.displacementMap, this.displacementMaps ),
|
|
948
|
+
anisotropyMap: this.processTexture( textures.anisotropyMap, this.anisotropyMaps ),
|
|
935
949
|
|
|
936
950
|
// Advanced texture maps (MeshPhysicalMaterial only). Folded into their scalar factors
|
|
937
951
|
// in ShadeKernel (applyExtensionMaps). thicknessMap stays dropped — thickness has no
|
|
938
952
|
// render effect yet (see gap-plan Phase 4.4).
|
|
939
|
-
clearcoatMap: this.processTexture(
|
|
940
|
-
clearcoatRoughnessMap: this.processTexture(
|
|
941
|
-
transmissionMap: this.processTexture(
|
|
953
|
+
clearcoatMap: this.processTexture( textures.clearcoatMap, this.clearcoatMaps ),
|
|
954
|
+
clearcoatRoughnessMap: this.processTexture( textures.clearcoatRoughnessMap, this.clearcoatRoughnessMaps ),
|
|
955
|
+
transmissionMap: this.processTexture( textures.transmissionMap, this.transmissionMaps ),
|
|
942
956
|
thicknessMap: this.processTexture( material.thicknessMap, [] ),
|
|
943
|
-
sheenColorMap: this.processTexture(
|
|
944
|
-
sheenRoughnessMap: this.processTexture(
|
|
945
|
-
specularIntensityMap: this.processTexture(
|
|
946
|
-
specularColorMap: this.processTexture(
|
|
947
|
-
iridescenceMap: this.processTexture(
|
|
948
|
-
iridescenceThicknessMap: this.processTexture(
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
// Material type for debugging/optimization
|
|
957
|
+
sheenColorMap: this.processTexture( textures.sheenColorMap, this.sheenColorMaps ),
|
|
958
|
+
sheenRoughnessMap: this.processTexture( textures.sheenRoughnessMap, this.sheenRoughnessMaps ),
|
|
959
|
+
specularIntensityMap: this.processTexture( textures.specularIntensityMap, this.specularIntensityMaps ),
|
|
960
|
+
specularColorMap: this.processTexture( textures.specularColorMap, this.specularColorMaps ),
|
|
961
|
+
iridescenceMap: this.processTexture( textures.iridescenceMap, this.iridescenceMaps ),
|
|
962
|
+
iridescenceThicknessMap: this.processTexture( textures.iridescenceThicknessMap, this.iridescenceThicknessMaps ),
|
|
963
|
+
|
|
964
|
+
mapMatrix: this.getTextureMatrix( textures.map ),
|
|
965
|
+
normalMapMatrices: this.getTextureMatrix( textures.normalMap ),
|
|
966
|
+
bumpMapMatrices: this.getTextureMatrix( textures.bumpMap ),
|
|
967
|
+
roughnessMapMatrices: this.getTextureMatrix( textures.roughnessMap ),
|
|
968
|
+
metalnessMapMatrices: this.getTextureMatrix( textures.metalnessMap ),
|
|
969
|
+
emissiveMapMatrices: this.getTextureMatrix( textures.emissiveMap ),
|
|
970
|
+
displacementMapMatrices: this.getTextureMatrix( textures.displacementMap ),
|
|
971
|
+
|
|
972
|
+
sources,
|
|
960
973
|
originalType: materialType
|
|
961
974
|
};
|
|
962
975
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Vector3, Quaternion } from 'three';
|
|
2
2
|
import { blackbodyToLinearRGB } from './blackbody.js';
|
|
3
|
-
import {
|
|
3
|
+
import { convertLinearTriple } from '../Color/WorkingMatrix.js';
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
5
|
+
// Point & spot lights specify Power in Watts; the shader uses radiant intensity
|
|
6
|
+
// (W/sr) as `intensity / dist²`, so convert with I = P / 4π (isotropic emitter),
|
|
7
|
+
// matching Blender. Area lights are already radiant power; the Sun is irradiance.
|
|
8
|
+
const INV_4PI = 1 / ( 4 * Math.PI );
|
|
9
9
|
|
|
10
10
|
// Effective emission colour + intensity after per-light Exposure (EV stops) and
|
|
11
11
|
// optional blackbody Temperature tint (Blender-style). Both fold into the values
|
|
@@ -16,15 +16,19 @@ function effectiveEmission( light ) {
|
|
|
16
16
|
const exposure = Number.isFinite( ud.exposure ) ? ud.exposure : 0;
|
|
17
17
|
const intensity = light.intensity * Math.pow( 2, exposure );
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
const rgb = [ light.color.r, light.color.g, light.color.b ];
|
|
20
20
|
if ( ud.useTemperature ) {
|
|
21
21
|
|
|
22
22
|
const [ tr, tg, tb ] = blackbodyToLinearRGB( ud.temperature ?? 6500 );
|
|
23
|
-
|
|
23
|
+
rgb[ 0 ] *= tr; rgb[ 1 ] *= tg; rgb[ 2 ] *= tb;
|
|
24
24
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
// A light colour is authored against sRGB primaries, like everything else three.js hands over.
|
|
28
|
+
// Rendering in a wider working space without converting it tints every surface it touches.
|
|
29
|
+
convertLinearTriple( rgb );
|
|
30
|
+
|
|
31
|
+
return { r: rgb[ 0 ], g: rgb[ 1 ], b: rgb[ 2 ], intensity };
|
|
28
32
|
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -62,10 +66,9 @@ export class LightSerializer {
|
|
|
62
66
|
|
|
63
67
|
calculateLightImportance( light, type = 'directional' ) {
|
|
64
68
|
|
|
65
|
-
//
|
|
66
|
-
// differs from what the panel shows.
|
|
69
|
+
// Calculate luminance-weighted importance
|
|
67
70
|
const luminance = 0.2126 * light.color.r + 0.7152 * light.color.g + 0.0722 * light.color.b;
|
|
68
|
-
let importance =
|
|
71
|
+
let importance = light.intensity * luminance;
|
|
69
72
|
|
|
70
73
|
// Area lights get additional importance based on size
|
|
71
74
|
if ( type === 'area' ) {
|
|
@@ -174,12 +177,6 @@ export class LightSerializer {
|
|
|
174
177
|
|
|
175
178
|
const em = effectiveEmission( light );
|
|
176
179
|
|
|
177
|
-
// The shader recovers radiance as power/(π·area), so hand it the power that inverts to
|
|
178
|
-
// the radiance the light holds. Area is measured off u/v exactly as the shader does it.
|
|
179
|
-
const rectArea = 4 * u.clone().cross( v ).length();
|
|
180
|
-
const emittingArea = shape > 0.5 ? rectArea * Math.PI / 4 : rectArea;
|
|
181
|
-
const power = em.intensity * Math.PI * ( normalize > 0.5 ? emittingArea : 1 );
|
|
182
|
-
|
|
183
180
|
// Store in cache with importance (16 floats, vec4-aligned)
|
|
184
181
|
this.areaLightCache.push( {
|
|
185
182
|
data: [
|
|
@@ -187,7 +184,7 @@ export class LightSerializer {
|
|
|
187
184
|
u.x, u.y, u.z, // u half-vector (3)
|
|
188
185
|
v.x, v.y, v.z, // v half-vector (3)
|
|
189
186
|
em.r, em.g, em.b, // color (3) — incl. temperature tint
|
|
190
|
-
|
|
187
|
+
em.intensity, // radiant power in Watts (1) — incl. exposure
|
|
191
188
|
normalize, // power-normalize flag (1)
|
|
192
189
|
spread, // emission spread in radians (1)
|
|
193
190
|
shape, // 0 = rect, 1 = disk/ellipse (1)
|
|
@@ -217,7 +214,7 @@ export class LightSerializer {
|
|
|
217
214
|
data: [
|
|
218
215
|
position.x, position.y, position.z, // position (3)
|
|
219
216
|
em.r, em.g, em.b, // color (3) — incl. temperature tint
|
|
220
|
-
em.intensity, // radiant intensity W/sr
|
|
217
|
+
em.intensity * INV_4PI, // radiant intensity W/sr = power(W)/4π, incl. exposure (1)
|
|
221
218
|
light.distance || 0.0, // cutoff distance (0 = infinite) (1)
|
|
222
219
|
light.decay !== undefined ? light.decay : 2.0 // decay exponent (1)
|
|
223
220
|
],
|
|
@@ -260,7 +257,7 @@ export class LightSerializer {
|
|
|
260
257
|
position.x, position.y, position.z, // position (3)
|
|
261
258
|
direction.x, direction.y, direction.z, // direction (3)
|
|
262
259
|
em.r, em.g, em.b, // color (3) — incl. temperature tint
|
|
263
|
-
em.intensity, // radiant intensity W/sr
|
|
260
|
+
em.intensity * INV_4PI, // radiant intensity W/sr = power(W)/4π, incl. exposure (1)
|
|
264
261
|
light.angle || Math.PI / 4, // cone half-angle in radians (1)
|
|
265
262
|
light.penumbra || 0.0, // penumbra [0,1] (1)
|
|
266
263
|
light.distance || 0.0, // cutoff distance (0 = infinite) (1)
|