rayzee 9.4.1 → 9.5.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 +226 -25
- package/dist/ActiveColor-YvuQq44O.js +13 -0
- package/dist/ArchiveImporter-xuUJiklP.js +4301 -0
- package/dist/AssetConfig-DSEntE65.js +30 -0
- package/dist/BidirectionalIntegrator-C5PKjJoT.js +934 -0
- package/dist/ColorManagement-CheBCPaW.js +1488 -0
- package/dist/EngineDefaults-DH3bI5t-.js +844 -0
- package/dist/EngineEvents-Ci8dVWCP.js +73 -0
- package/dist/EngineIssues-CIZnXHsF.js +72 -0
- package/dist/FileFormats-sPGJeCRc.js +102 -0
- package/dist/Logger-BnHghprG.js +97 -0
- package/dist/{NeuralRetouch-vkdXXyK2.js → NeuralRetouch-CW11yLZ_.js} +8 -6
- package/dist/{NeuralSuperRes-FVqRJKQV.js → NeuralSuperRes-BekaecYg.js} +55 -120
- package/dist/PathTracer-Ccl9KOuF.js +29729 -0
- package/dist/PhysicalSky-Dv2PMFGW.js +1219 -0
- package/dist/RayzeeRenderer-BQxnzCTD.js +3542 -0
- package/dist/SolarLimb-ken-JbDK.js +721 -0
- package/dist/ToneMapGPU-BHi5JzIq.js +177 -0
- package/dist/ViewTransforms-D0rTLBsX.js +255 -0
- package/dist/WorkingMatrix-C6y233hT.js +27 -0
- package/dist/ZipReader-gDSWr0I5.js +98 -0
- package/dist/addons/archives.es.js +3 -0
- package/dist/addons/bidirectional.es.js +2 -0
- package/dist/addons/color.es.js +3 -0
- package/dist/addons/formats.es.js +2 -0
- package/dist/addons/physical-sky.es.js +2 -0
- package/dist/addons/storage.es.js +3 -0
- package/dist/archiveFormats-BZw7TiDp.js +75 -0
- package/dist/identity-DrQsGT2o.js +39 -0
- package/dist/openStorage-DwcBRUj7.js +820 -0
- package/dist/rayzee-core.es.js +12 -0
- package/dist/rayzee.es.js +2266 -44125
- package/dist/rayzee.umd.js +485 -473
- package/dist/sceneCachePolicy-BmwYOYDu.js +183 -0
- package/dist/shared-BGBcSxBQ.js +17 -0
- package/package.json +23 -2
- package/src/Color/ActiveColor.js +34 -0
- package/src/Color/BasicColor.js +147 -0
- package/src/Color/ColorManagement.js +7 -31
- package/src/EngineDefaults.js +23 -14
- package/src/EngineEvents.js +2 -0
- package/src/EngineIssues.js +1 -0
- package/src/PathTracerApp.js +617 -4016
- package/src/Pipeline/PipelineContext.js +0 -4
- package/src/Platform.js +36 -0
- package/src/Processor/ArchiveCache.js +1 -1
- package/src/Processor/ArchiveImporter.js +1259 -0
- package/src/Processor/AssetLoader.js +322 -1746
- package/src/Processor/AtmosphereModel.js +1 -2
- package/src/Processor/BVHBuilder.js +4 -95
- package/src/Processor/EnvironmentExactTable.js +187 -0
- package/src/Processor/EquirectHDRInfo.js +30 -223
- package/src/Processor/FileFormats.js +116 -0
- package/src/Processor/GLTFDecoders.js +85 -0
- package/src/Processor/GLTFDiffuseTransmission.js +63 -0
- package/src/Processor/GeometryExtractor.js +12 -1
- package/src/Processor/IESParser.js +47 -35
- package/src/Processor/KernelManager.js +35 -2
- package/src/Processor/MaterialPacking.js +8 -7
- package/src/Processor/PBRT/PBRTMaterials.js +129 -41
- package/src/Processor/PBRT/PBRTParser.js +30 -17
- package/src/Processor/PBRT/PBRTSceneBuilder.js +548 -37
- package/src/Processor/PBRT/PBRTTextureBake.js +154 -0
- package/src/Processor/PBRT/PFM.js +59 -0
- package/src/Processor/PackedRayBuffer.js +116 -9
- package/src/Processor/ParallelBVHBuilder.js +3 -6
- package/src/Processor/PhysicalSky.js +5 -4
- package/src/Processor/PlatformImageLoader.js +50 -6
- package/src/Processor/QueueManager.js +12 -3
- package/src/Processor/ReinsertionOptimizer.js +2 -2
- package/src/Processor/SceneProcessor.js +15 -55
- package/src/Processor/ShaderBuilder.js +6 -16
- package/src/Processor/SimpleSky.js +1 -62
- package/src/Processor/SolarLimb.js +3 -0
- package/src/Processor/TextureCreator.js +1 -1
- package/src/Processor/Workers/BVHSubtreeWorker.js +1 -34
- package/src/Processor/Workers/BVHWorker.js +5 -18
- package/src/Processor/Workers/CDFWorker.js +6 -183
- package/src/Processor/archiveFormats.js +7 -0
- package/src/Processor/utils.js +2 -1
- package/src/RayzeeRenderer.js +3902 -0
- package/src/RenderSettings.js +49 -41
- package/src/Stages/ASVGF.js +5 -7
- package/src/Stages/AutoExposure.js +3 -12
- package/src/Stages/Compositor.js +8 -23
- package/src/Stages/NRD.js +9 -2
- package/src/Stages/NormalDepth.js +9 -6
- package/src/Stages/PathTracer.js +254 -38
- package/src/Stages/PathTracerStage.js +11 -16
- package/src/Storage/BLASCache.js +1 -1
- package/src/Storage/CDFCache.js +18 -17
- package/src/Storage/DownloadCache.js +1 -1
- package/src/Storage/SceneGraphCodec.js +46 -3
- package/src/Storage/SpillStore.js +1 -1
- package/src/Storage/StorageManager.js +2 -4
- package/src/Storage/areas.js +4 -0
- package/src/TSL/BVHTraversal.js +62 -24
- package/src/TSL/Bidirectional.js +204 -0
- package/src/TSL/BidirectionalLamps.js +132 -0
- package/src/TSL/Clearcoat.js +15 -6
- package/src/TSL/Common.js +19 -14
- package/src/TSL/ConnectKernel.js +147 -0
- package/src/TSL/Debugger.js +1 -1
- package/src/TSL/Displacement.js +4 -3
- package/src/TSL/EmissiveSampling.js +64 -17
- package/src/TSL/Environment.js +77 -106
- package/src/TSL/EnvironmentCDF.js +85 -39
- package/src/TSL/ExtendKernel.js +8 -3
- package/src/TSL/FinalWriteKernel.js +4 -4
- package/src/TSL/GenerateKernel.js +18 -4
- package/src/TSL/HitFacet.js +35 -5
- package/src/TSL/LightBVHSampling.js +40 -9
- package/src/TSL/LightGenerateKernel.js +293 -0
- package/src/TSL/LightGuide.js +198 -0
- package/src/TSL/LightSplatKernel.js +165 -0
- package/src/TSL/LightsCore.js +13 -38
- package/src/TSL/LightsDirect.js +28 -29
- package/src/TSL/LightsIndirect.js +3 -1
- package/src/TSL/LightsSampling.js +90 -38
- package/src/TSL/MaterialEvaluation.js +113 -31
- package/src/TSL/MaterialProperties.js +44 -17
- package/src/TSL/MaterialTransmission.js +52 -47
- package/src/TSL/MergeKernel.js +218 -0
- package/src/TSL/PathTracerCore.js +65 -29
- package/src/TSL/Random.js +4 -0
- package/src/TSL/SceneResources.js +51 -0
- package/src/TSL/ShadeKernel.js +881 -103
- package/src/TSL/Struct.js +10 -0
- package/src/TSL/Sun.js +1 -1
- package/src/TSL/TextureSampling.js +65 -60
- package/src/addons/archives.js +14 -0
- package/src/addons/bidirectional.js +13 -0
- package/src/addons/color.js +32 -0
- package/src/addons/formats.js +16 -0
- package/src/addons/physicalSky.js +14 -0
- package/src/addons/storage.js +19 -0
- package/src/core.js +35 -0
- package/src/index.js +3 -1
- package/src/integrators/BidirectionalIntegrator.js +832 -0
- package/src/managers/EnvironmentManager.js +94 -91
- package/src/managers/MaterialDataManager.js +42 -0
- package/src/managers/UniformManager.js +0 -1
- package/dist/ToneMapGPU-DKQM1pMd.js +0 -552
- package/dist/rolldown-runtime-CAFD8bLK.js +0 -11
- package/src/Processor/TreeletOptimizer.js +0 -574
|
@@ -10,6 +10,7 @@ import { DistributionGGX } from './MaterialProperties.js';
|
|
|
10
10
|
import { ImportanceSampleGGX } from './MaterialSampling.js';
|
|
11
11
|
import { getRandomSample1D, getRandomSample2D, pcgHash } from './Random.js';
|
|
12
12
|
import { handleSubsurfaceEntry, SubsurfaceEntryResult } from './Subsurface.js';
|
|
13
|
+
import { materialLayers } from './SceneResources.js';
|
|
13
14
|
|
|
14
15
|
// ================================================================================
|
|
15
16
|
// STRUCTS (local to transmission)
|
|
@@ -158,8 +159,9 @@ export const calculateBeerLawAbsorption = /*@__PURE__*/ wgslFn( `
|
|
|
158
159
|
export const sampleMicrofacetTransmission = Fn( ( [
|
|
159
160
|
V, N, ior, roughness, entering, dispersion, xi, rngState, pathWavelength,
|
|
160
161
|
pixelCoord, resolution, frame, dimBase,
|
|
161
|
-
] ) => {
|
|
162
|
+
], builder ) => {
|
|
162
163
|
|
|
164
|
+
const layers = materialLayers( builder );
|
|
163
165
|
const result = MicrofacetTransmissionResult( {
|
|
164
166
|
direction: vec3( 0.0 ),
|
|
165
167
|
halfVector: vec3( 0.0 ),
|
|
@@ -169,48 +171,7 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
169
171
|
pathWavelength: pathWavelength,
|
|
170
172
|
} ).toVar();
|
|
171
173
|
|
|
172
|
-
|
|
173
|
-
If( roughness.lessThanEqual( 0.05 ).and( dispersion.greaterThan( 0.0 ) ), () => {
|
|
174
|
-
|
|
175
|
-
result.halfVector.assign( N );
|
|
176
|
-
result.didReflect.assign( false );
|
|
177
|
-
|
|
178
|
-
const eta = ior;
|
|
179
|
-
const etaRatio = select( entering, float( 1.0 ).div( eta ), eta ).toVar();
|
|
180
|
-
|
|
181
|
-
// Reuse the path's locked wavelength if any; else sample a new one and tint once.
|
|
182
|
-
If( pathWavelength.greaterThan( 0.0 ), () => {
|
|
183
|
-
|
|
184
|
-
const lockedIOR = iorFromWavelength( ior, dispersion, pathWavelength );
|
|
185
|
-
etaRatio.assign( select( entering, float( 1.0 ).div( lockedIOR ), lockedIOR ) );
|
|
186
|
-
|
|
187
|
-
} ).Else( () => {
|
|
188
|
-
|
|
189
|
-
const spectralSample = SpectralSample.wrap( sampleWavelengthForDispersion( ior, dispersion, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 5 ) ), rngState, resolution, frame ) ) );
|
|
190
|
-
etaRatio.assign( select( entering, float( 1.0 ).div( spectralSample.ior ), spectralSample.ior ) );
|
|
191
|
-
result.colorWeight.assign( spectralSample.colorWeight );
|
|
192
|
-
result.pathWavelength.assign( spectralSample.wavelength );
|
|
193
|
-
|
|
194
|
-
} );
|
|
195
|
-
|
|
196
|
-
// Perfect refraction using surface normal
|
|
197
|
-
const refractDir = refract( V.negate(), N, etaRatio ).toVar();
|
|
198
|
-
|
|
199
|
-
// Check for total internal reflection
|
|
200
|
-
If( dot( refractDir, refractDir ).lessThan( 0.001 ), () => {
|
|
201
|
-
|
|
202
|
-
result.direction.assign( reflect( V.negate(), N ) );
|
|
203
|
-
result.didReflect.assign( true );
|
|
204
|
-
result.pdf.assign( 1.0 );
|
|
205
|
-
|
|
206
|
-
} ).Else( () => {
|
|
207
|
-
|
|
208
|
-
result.direction.assign( refractDir );
|
|
209
|
-
result.pdf.assign( 1.0 );
|
|
210
|
-
|
|
211
|
-
} );
|
|
212
|
-
|
|
213
|
-
} ).Else( () => {
|
|
174
|
+
const microfacet = () => {
|
|
214
175
|
|
|
215
176
|
// Use minimum roughness to avoid numerical issues for rough surfaces
|
|
216
177
|
const transmissionRoughness = max( MIN_ROUGHNESS, roughness );
|
|
@@ -223,7 +184,7 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
223
184
|
const etaRatio = select( entering, float( 1.0 ).div( ior ), ior ).toVar();
|
|
224
185
|
|
|
225
186
|
// Reuse the path's locked wavelength if any; else sample a new one and tint once.
|
|
226
|
-
If( dispersion.greaterThan( 0.0 ), () => {
|
|
187
|
+
if ( layers.dispersion ) If( dispersion.greaterThan( 0.0 ), () => {
|
|
227
188
|
|
|
228
189
|
If( pathWavelength.greaterThan( 0.0 ), () => {
|
|
229
190
|
|
|
@@ -278,7 +239,51 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
278
239
|
|
|
279
240
|
} );
|
|
280
241
|
|
|
281
|
-
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// For smooth surfaces with dispersion, use perfect refraction with spectral IOR
|
|
245
|
+
if ( ! layers.dispersion ) microfacet();
|
|
246
|
+
else If( roughness.lessThanEqual( 0.05 ).and( dispersion.greaterThan( 0.0 ) ), () => {
|
|
247
|
+
|
|
248
|
+
result.halfVector.assign( N );
|
|
249
|
+
result.didReflect.assign( false );
|
|
250
|
+
|
|
251
|
+
const eta = ior;
|
|
252
|
+
const etaRatio = select( entering, float( 1.0 ).div( eta ), eta ).toVar();
|
|
253
|
+
|
|
254
|
+
// Reuse the path's locked wavelength if any; else sample a new one and tint once.
|
|
255
|
+
If( pathWavelength.greaterThan( 0.0 ), () => {
|
|
256
|
+
|
|
257
|
+
const lockedIOR = iorFromWavelength( ior, dispersion, pathWavelength );
|
|
258
|
+
etaRatio.assign( select( entering, float( 1.0 ).div( lockedIOR ), lockedIOR ) );
|
|
259
|
+
|
|
260
|
+
} ).Else( () => {
|
|
261
|
+
|
|
262
|
+
const spectralSample = SpectralSample.wrap( sampleWavelengthForDispersion( ior, dispersion, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 5 ) ), rngState, resolution, frame ) ) );
|
|
263
|
+
etaRatio.assign( select( entering, float( 1.0 ).div( spectralSample.ior ), spectralSample.ior ) );
|
|
264
|
+
result.colorWeight.assign( spectralSample.colorWeight );
|
|
265
|
+
result.pathWavelength.assign( spectralSample.wavelength );
|
|
266
|
+
|
|
267
|
+
} );
|
|
268
|
+
|
|
269
|
+
// Perfect refraction using surface normal
|
|
270
|
+
const refractDir = refract( V.negate(), N, etaRatio ).toVar();
|
|
271
|
+
|
|
272
|
+
// Check for total internal reflection
|
|
273
|
+
If( dot( refractDir, refractDir ).lessThan( 0.001 ), () => {
|
|
274
|
+
|
|
275
|
+
result.direction.assign( reflect( V.negate(), N ) );
|
|
276
|
+
result.didReflect.assign( true );
|
|
277
|
+
result.pdf.assign( 1.0 );
|
|
278
|
+
|
|
279
|
+
} ).Else( () => {
|
|
280
|
+
|
|
281
|
+
result.direction.assign( refractDir );
|
|
282
|
+
result.pdf.assign( 1.0 );
|
|
283
|
+
|
|
284
|
+
} );
|
|
285
|
+
|
|
286
|
+
} ).Else( microfacet );
|
|
282
287
|
|
|
283
288
|
return result;
|
|
284
289
|
|
|
@@ -430,7 +435,7 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
430
435
|
transmissiveTraversals,
|
|
431
436
|
currentMediumIOR, previousMediumIOR,
|
|
432
437
|
pathWavelength,
|
|
433
|
-
] ) => {
|
|
438
|
+
], builder ) => {
|
|
434
439
|
|
|
435
440
|
const result = MaterialInteractionResult( {
|
|
436
441
|
continueRay: false,
|
|
@@ -534,7 +539,7 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
534
539
|
|
|
535
540
|
// Subsurface (independent of transmission; works at transmission==0). Entry is a lottery
|
|
536
541
|
// (prob = weight) so 1-weight falls through to the opaque BRDF; exit is deterministic.
|
|
537
|
-
If( handled.not().and( material.subsurface.greaterThan( 0.0 ) ), () => {
|
|
542
|
+
if ( materialLayers( builder ).subsurface ) If( handled.not().and( material.subsurface.greaterThan( 0.0 ) ), () => {
|
|
538
543
|
|
|
539
544
|
const entering = dot( ray.direction, normal ).lessThan( 0.0 );
|
|
540
545
|
const doEnter = entering.not().or( getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 8 ) ), rngState, resolution, frame ).lessThan( material.subsurface ) );
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MergeKernel.js — vertex merging (Georgiev et al. 2012, "Light Transport Simulation with Vertex Connection and
|
|
3
|
+
* Merging"): each camera vertex Shade left pending gathers the light vertices within the merge radius, as photon
|
|
4
|
+
* mapping does, MIS-weighted against every other strategy. It is the one strategy for light no path can connect:
|
|
5
|
+
* a lamp behind glass seen in a mirror, a caustic seen through water.
|
|
6
|
+
*
|
|
7
|
+
* The radius is a few pixels' footprint where the camera vertex is (Bidirectional.js mergeRadiusAt), so it grows
|
|
8
|
+
* with the distance from the camera. The light vertex cache is filed by that radius in shells, each a hash grid of
|
|
9
|
+
* cells two of its largest radii wide: one list a cell, threaded through the cached records' spare lane (`next`),
|
|
10
|
+
* built after the light pass by mergeInsert. A gather searches the one or two shells its sphere reaches, 2³ cells each.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
Fn, float, vec3, vec4, int, uint, ivec3, If, Loop, instanceIndex, atomicLoad, atomicStore, atomicFunc, Return, dot, floor, max, min, select,
|
|
15
|
+
length, log2, exp2, Break,
|
|
16
|
+
} from 'three/tsl';
|
|
17
|
+
|
|
18
|
+
import { evaluateMaterialResponse } from './MaterialEvaluation.js';
|
|
19
|
+
import { calculateMaterialPDF } from './LightsSampling.js';
|
|
20
|
+
import { luminance } from './Common.js';
|
|
21
|
+
import { regularizePathContribution } from './PathTracerCore.js';
|
|
22
|
+
import { resolveSurfaceMaterial, misMergePartial, misWeight, strategyWeight, mergeRadiusAt, mergeEta, STRATEGY } from './Bidirectional.js';
|
|
23
|
+
import { COUNTER } from '../Processor/QueueManager.js';
|
|
24
|
+
import {
|
|
25
|
+
readRayRadiance, writeRayRadiance, pendingVertex, cachedVertex, readVertexTag, readVertexRecord, readVertexPosition,
|
|
26
|
+
readVertexNext, writeVertexNext, readLightPathLength,
|
|
27
|
+
} from '../Processor/PackedRayBuffer.js';
|
|
28
|
+
|
|
29
|
+
const WG_SIZE = 256;
|
|
30
|
+
export const MERGE_EMPTY = 0xFFFFFFFF;
|
|
31
|
+
// A cell's list is walked at most this far: a bound on the work, never reached at the radius the radius scale gives.
|
|
32
|
+
const MAX_CELL_STEPS = 1024;
|
|
33
|
+
|
|
34
|
+
// Shells of radius ratio 1.25: a sphere whose radius changes by (1 ± slope) across it spans at most two.
|
|
35
|
+
export const MERGE_SHELL_RATIO = 1.25;
|
|
36
|
+
const LOG2_RATIO = Math.log2( MERGE_SHELL_RATIO );
|
|
37
|
+
|
|
38
|
+
const shellOf = ( bdpt, radius ) => int( max( floor( log2( radius.div( bdpt.mergeMin ) ).div( LOG2_RATIO ) ), 0.0 ) );
|
|
39
|
+
|
|
40
|
+
// Twice the largest radius whose sphere can reach the shell (its own largest, over 1 − slope).
|
|
41
|
+
const shellCell = ( bdpt, shell ) => bdpt.mergeMin.mul( exp2( float( shell.add( int( 1 ) ) ).mul( LOG2_RATIO ) ) ).mul( 2.0 )
|
|
42
|
+
.div( max( float( 1.0 ).sub( bdpt.mergeSlope ), 0.5 ) );
|
|
43
|
+
|
|
44
|
+
const cellHash = ( shell, cell, hashMask ) => uint( cell.x ).mul( uint( 73856093 ) )
|
|
45
|
+
.bitXor( uint( cell.y ).mul( uint( 19349663 ) ) )
|
|
46
|
+
.bitXor( uint( cell.z ).mul( uint( 83492791 ) ) )
|
|
47
|
+
.bitXor( uint( shell ).mul( uint( 2654435761 ) ) )
|
|
48
|
+
.bitAnd( hashMask );
|
|
49
|
+
|
|
50
|
+
// A light vertex's shell and cell.
|
|
51
|
+
const filing = ( bdpt, p ) => {
|
|
52
|
+
|
|
53
|
+
const shell = shellOf( bdpt, mergeRadiusAt( bdpt, p ) ).toVar();
|
|
54
|
+
return { shell, cell: ivec3( floor( p.div( shellCell( bdpt, shell ) ) ) ).toVar() };
|
|
55
|
+
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** Empties every list head. */
|
|
59
|
+
export function buildMergeClearKernel( { head, hashSize } ) {
|
|
60
|
+
|
|
61
|
+
return Fn( () => {
|
|
62
|
+
|
|
63
|
+
If( instanceIndex.lessThan( uint( hashSize ) ), () => {
|
|
64
|
+
|
|
65
|
+
atomicStore( head.element( instanceIndex ), uint( MERGE_EMPTY ) );
|
|
66
|
+
|
|
67
|
+
} );
|
|
68
|
+
|
|
69
|
+
} )().compute( Math.ceil( hashSize / WG_SIZE ) * WG_SIZE, [ WG_SIZE ] );
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Files each light vertex of this frame at the head of its cell's list. */
|
|
74
|
+
export function buildMergeInsertKernel( { hitBufferRW, head, bidirectional: bdpt } ) {
|
|
75
|
+
|
|
76
|
+
const { lightPaths, slotsPerPath, lightTag, hashMask } = bdpt;
|
|
77
|
+
|
|
78
|
+
return Fn( () => {
|
|
79
|
+
|
|
80
|
+
const slot = instanceIndex.toVar();
|
|
81
|
+
If( slot.greaterThanEqual( lightPaths.mul( slotsPerPath ) ), () => {
|
|
82
|
+
|
|
83
|
+
Return();
|
|
84
|
+
|
|
85
|
+
} );
|
|
86
|
+
|
|
87
|
+
const depth = slot.mod( slotsPerPath ).toVar();
|
|
88
|
+
If( depth.greaterThanEqual( readLightPathLength( hitBufferRW, cachedVertex( slot.sub( depth ) ), lightTag ) ), () => {
|
|
89
|
+
|
|
90
|
+
Return();
|
|
91
|
+
|
|
92
|
+
} );
|
|
93
|
+
|
|
94
|
+
const at = cachedVertex( slot );
|
|
95
|
+
const { shell, cell } = filing( bdpt, readVertexPosition( hitBufferRW, at ) );
|
|
96
|
+
const bucket = cellHash( shell, cell, hashMask );
|
|
97
|
+
writeVertexNext( hitBufferRW, at, atomicFunc( 'atomicExchange', head.element( bucket ), slot ) );
|
|
98
|
+
|
|
99
|
+
} );
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function buildMergeKernel( params ) {
|
|
104
|
+
|
|
105
|
+
const {
|
|
106
|
+
rayBufferRW, hitBufferRO, activeIndicesRO, counters, head, materialBuffer, bidirectional: bdpt,
|
|
107
|
+
maxBounceCount, globalIlluminationIntensity, fireflyThreshold, accumFrame,
|
|
108
|
+
} = params;
|
|
109
|
+
const { passTag, slotsPerPath, strategyView, hashMask } = bdpt;
|
|
110
|
+
|
|
111
|
+
return Fn( () => {
|
|
112
|
+
|
|
113
|
+
const tid = instanceIndex;
|
|
114
|
+
If( tid.greaterThanEqual( atomicLoad( counters.element( uint( COUNTER.ENTERING_COUNT ) ) ) ), () => {
|
|
115
|
+
|
|
116
|
+
Return();
|
|
117
|
+
|
|
118
|
+
} );
|
|
119
|
+
|
|
120
|
+
const rayID = activeIndicesRO.element( tid ).toVar();
|
|
121
|
+
const pending = pendingVertex( rayID );
|
|
122
|
+
If( readVertexTag( hitBufferRO, pending ).notEqual( passTag ), () => {
|
|
123
|
+
|
|
124
|
+
Return();
|
|
125
|
+
|
|
126
|
+
} );
|
|
127
|
+
|
|
128
|
+
const cam = readVertexRecord( hitBufferRO, pending );
|
|
129
|
+
const cameraDepth = int( cam.extra ).toVar();
|
|
130
|
+
// A merged path of k scattering vertices: the camera's cameraDepth + 1 and the light's l share one.
|
|
131
|
+
const lightBudget = int( maxBounceCount ).add( int( 1 ) ).sub( cameraDepth ).toVar();
|
|
132
|
+
const material = resolveSurfaceMaterial( cam.materialIndex, cam.uv, cam.N, materialBuffer );
|
|
133
|
+
const gathered = vec3( 0.0 ).toVar();
|
|
134
|
+
|
|
135
|
+
const radius = mergeRadiusAt( bdpt, cam.position ).toVar();
|
|
136
|
+
const radius2 = radius.mul( radius ).toVar();
|
|
137
|
+
const eta = mergeEta( bdpt, radius ).toVar();
|
|
138
|
+
const trusted = eta.mul( bdpt.mergeTrust );
|
|
139
|
+
const vc = float( 1.0 ).div( max( trusted.mul( trusted ), 1e-30 ) ).toVar();
|
|
140
|
+
// The shells the sphere reaches: its radius at its nearest and farthest points from the camera.
|
|
141
|
+
const distance = length( cam.position.sub( bdpt.cameraPosition ) ).toVar();
|
|
142
|
+
const nearShell = shellOf( bdpt, max( bdpt.mergeConst.add( bdpt.mergeSlope.mul( max( distance.sub( radius ), 0.0 ) ) ), bdpt.mergeMin ) ).toVar();
|
|
143
|
+
const farShell = min( shellOf( bdpt, bdpt.mergeConst.add( bdpt.mergeSlope.mul( distance.add( radius ) ) ) ), nearShell.add( int( 1 ) ) ).toVar();
|
|
144
|
+
|
|
145
|
+
// Per shell, the 2 × 2 × 2 cells the sphere can reach, the cells being at least two radii wide.
|
|
146
|
+
Loop( { start: int( 0 ), end: int( 16 ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
147
|
+
|
|
148
|
+
const shell = nearShell.add( i.shiftRight( int( 3 ) ) ).toVar();
|
|
149
|
+
If( shell.greaterThan( farShell ), () => {
|
|
150
|
+
|
|
151
|
+
Break();
|
|
152
|
+
|
|
153
|
+
} );
|
|
154
|
+
const cellSize = shellCell( bdpt, shell ).toVar();
|
|
155
|
+
const corner = i.bitAnd( int( 7 ) );
|
|
156
|
+
const cell = ivec3( floor( cam.position.div( cellSize ).sub( 0.5 ) ) )
|
|
157
|
+
.add( ivec3( corner.bitAnd( int( 1 ) ), corner.shiftRight( int( 1 ) ).bitAnd( int( 1 ) ), corner.shiftRight( int( 2 ) ) ) ).toVar();
|
|
158
|
+
const slot = atomicLoad( head.element( cellHash( shell, cell, hashMask ) ) ).toVar();
|
|
159
|
+
const steps = int( 0 ).toVar();
|
|
160
|
+
|
|
161
|
+
Loop( slot.notEqual( uint( MERGE_EMPTY ) ).and( steps.lessThan( int( MAX_CELL_STEPS ) ) ), () => {
|
|
162
|
+
|
|
163
|
+
const at = cachedVertex( slot );
|
|
164
|
+
const position = readVertexPosition( hitBufferRO, at ).toVar();
|
|
165
|
+
const offset = position.sub( cam.position );
|
|
166
|
+
const l = int( slot.mod( slotsPerPath ) ).add( int( 1 ) ).toVar();
|
|
167
|
+
If( dot( offset, offset ).lessThan( radius2 ).and( l.lessThanEqual( lightBudget ) ), () => {
|
|
168
|
+
|
|
169
|
+
// Its own shell and cell only: two lists sharing a bucket (a hash collision) must not count it twice.
|
|
170
|
+
const own = filing( bdpt, position );
|
|
171
|
+
If( own.shell.equal( shell ).and( own.cell.x.equal( cell.x ) ).and( own.cell.y.equal( cell.y ) ).and( own.cell.z.equal( cell.z ) ), () => {
|
|
172
|
+
|
|
173
|
+
const light = readVertexRecord( hitBufferRO, at );
|
|
174
|
+
const cosShading = dot( light.V, cam.N ).toVar();
|
|
175
|
+
const cosFacet = dot( light.V, cam.facetN ).toVar();
|
|
176
|
+
// Light arriving on this side. A corner's other wall stays in: its light vertices stand in for the
|
|
177
|
+
// part of the sphere past the corner, which the density divides by too.
|
|
178
|
+
If( cosShading.greaterThan( 0.0 ).and( cosFacet.greaterThan( 0.0 ) ), () => {
|
|
179
|
+
|
|
180
|
+
const f = evaluateMaterialResponse( cam.V, light.V, cam.N, material );
|
|
181
|
+
const wLight = misMergePartial( light, calculateMaterialPDF( cam.V, light.V, cam.N, material ), vc );
|
|
182
|
+
const wCamera = misMergePartial( cam, calculateMaterialPDF( light.V, cam.V, cam.N, material ), vc );
|
|
183
|
+
const scattering = cameraDepth.add( l );
|
|
184
|
+
// Alone, a path of k scattering vertices is reached by k merges.
|
|
185
|
+
const weight = strategyWeight( strategyView, STRATEGY.MERGE, misWeight( wLight, wCamera ), float( 1.0 ).div( float( scattering ) ) );
|
|
186
|
+
const gi = select( scattering.greaterThan( int( 1 ) ), globalIlluminationIntensity, float( 1.0 ) );
|
|
187
|
+
// The photon's flux is per unit area; the BSDF wants it per projected solid angle (Veach 5.3.2).
|
|
188
|
+
const correction = cosShading.div( max( cosFacet, 1e-4 ) );
|
|
189
|
+
gathered.addAssign( regularizePathContribution(
|
|
190
|
+
cam.throughput.mul( f ).mul( light.throughput ).mul( weight.mul( correction ).div( eta ).mul( gi ) ),
|
|
191
|
+
float( scattering.sub( int( 1 ) ) ), fireflyThreshold, int( accumFrame ),
|
|
192
|
+
) );
|
|
193
|
+
|
|
194
|
+
} );
|
|
195
|
+
|
|
196
|
+
} );
|
|
197
|
+
|
|
198
|
+
} );
|
|
199
|
+
|
|
200
|
+
slot.assign( readVertexNext( hitBufferRO, at ) );
|
|
201
|
+
steps.addAssign( int( 1 ) );
|
|
202
|
+
|
|
203
|
+
} );
|
|
204
|
+
|
|
205
|
+
} );
|
|
206
|
+
|
|
207
|
+
If( luminance( gathered ).greaterThan( 0.0 ), () => {
|
|
208
|
+
|
|
209
|
+
const radiance = readRayRadiance( rayBufferRW, rayID ).toVar();
|
|
210
|
+
writeRayRadiance( rayBufferRW, rayID, vec4( radiance.xyz.add( gathered ), radiance.w ) );
|
|
211
|
+
|
|
212
|
+
} );
|
|
213
|
+
|
|
214
|
+
} );
|
|
215
|
+
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export { WG_SIZE as MERGE_WG_SIZE };
|
|
@@ -45,8 +45,10 @@ import {
|
|
|
45
45
|
computeAnisoAlphas,
|
|
46
46
|
calculateBRDFWeights,
|
|
47
47
|
sheenSamplingRoughness,
|
|
48
|
+
diffuseTransmissionPdf,
|
|
48
49
|
} from './MaterialProperties.js';
|
|
49
|
-
import { evaluateMaterialResponseFromDots, evaluateSpecularDeltaFromDots } from './MaterialEvaluation.js';
|
|
50
|
+
import { evaluateMaterialResponseFromDots, evaluateSpecularDeltaFromDots, evaluateDiffuseTransmission } from './MaterialEvaluation.js';
|
|
51
|
+
import { materialLayers } from './SceneResources.js';
|
|
50
52
|
|
|
51
53
|
import {
|
|
52
54
|
ImportanceSampleCosine,
|
|
@@ -71,14 +73,16 @@ export const generateSampledDirection = Fn( ( [
|
|
|
71
73
|
mc,
|
|
72
74
|
weightsComputed, cachedBrdfWeights,
|
|
73
75
|
materialCacheCached, cachedMaterialCache,
|
|
74
|
-
] ) => {
|
|
76
|
+
], builder ) => {
|
|
75
77
|
|
|
78
|
+
const layers = materialLayers( builder );
|
|
76
79
|
const resultDirection = vec3( 0.0 ).toVar();
|
|
77
80
|
const resultValue = vec3( 0.0 ).toVar();
|
|
78
81
|
const resultPdf = float( 0.0 ).toVar();
|
|
79
82
|
const resultIsTransmission = tslBool( false ).toVar();
|
|
80
83
|
const resultColorWeight = vec3( 1.0 ).toVar();
|
|
81
84
|
const resultIsDelta = tslBool( false ).toVar();
|
|
85
|
+
const resultIsDiffuseTransmission = tslBool( false ).toVar();
|
|
82
86
|
|
|
83
87
|
// Compute BRDF weights
|
|
84
88
|
const weights = cachedBrdfWeights.toVar();
|
|
@@ -114,17 +118,42 @@ export const generateSampledDirection = Fn( ( [
|
|
|
114
118
|
const cumulativeDiffuse = weights.diffuse.toVar();
|
|
115
119
|
const cumulativeSpecular = cumulativeDiffuse.add( weights.specular ).toVar();
|
|
116
120
|
const cumulativeSheen = cumulativeSpecular.add( weights.sheen ).toVar();
|
|
117
|
-
const cumulativeClearcoat = cumulativeSheen.add( weights.clearcoat );
|
|
121
|
+
const cumulativeClearcoat = cumulativeSheen.add( weights.clearcoat ).toVar();
|
|
122
|
+
const cumulativeDiffuseTransmission = cumulativeClearcoat.add( weights.diffuseTransmission );
|
|
118
123
|
|
|
119
124
|
// Chained If/ElseIf so emitted WGSL becomes a single mutually-exclusive branch
|
|
120
|
-
// (replaces five separate If blocks gated on a `sampled` flag — divergence hotspot)
|
|
121
|
-
|
|
125
|
+
// (replaces five separate If blocks gated on a `sampled` flag — divergence hotspot). A layer no material uses
|
|
126
|
+
// has weight 0, so its link could never be taken and is left out.
|
|
127
|
+
const isotropicSpecular = () => {
|
|
128
|
+
|
|
129
|
+
If( material.roughness.greaterThan( 0.0 ), () => {
|
|
130
|
+
|
|
131
|
+
const TBN = constructTBN( { N } );
|
|
132
|
+
const localV = TBN.transpose().mul( V );
|
|
133
|
+
|
|
134
|
+
// VNDF sampling
|
|
135
|
+
const localH = sampleGGXVNDF( { V: localV, roughness: material.roughness, Xi: xi } );
|
|
136
|
+
H.assign( TBN.mul( localH ) );
|
|
137
|
+
|
|
138
|
+
resultDirection.assign( reflect( V.negate(), H ) );
|
|
139
|
+
|
|
140
|
+
} ).Else( () => {
|
|
141
|
+
|
|
142
|
+
resultDirection.assign( reflect( V.negate(), N ) );
|
|
143
|
+
resultIsDelta.assign( true );
|
|
144
|
+
|
|
145
|
+
} );
|
|
146
|
+
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
let lobes = If( rand.lessThan( cumulativeDiffuse ), () => {
|
|
122
150
|
|
|
123
151
|
resultDirection.assign( ImportanceSampleCosine( { N, xi } ) );
|
|
124
152
|
|
|
125
153
|
} ).ElseIf( rand.lessThan( cumulativeSpecular ), () => {
|
|
126
154
|
|
|
127
|
-
|
|
155
|
+
if ( ! layers.anisotropy ) isotropicSpecular();
|
|
156
|
+
else If( material.anisotropy.greaterThan( 0.0 ), () => {
|
|
128
157
|
|
|
129
158
|
// Shared frame → sampler and eval/PDF stay bit-identical (MIS consistency)
|
|
130
159
|
const f = AnisoFrame.wrap( anisoTangentFrame( N, material.anisotropyRotation ) );
|
|
@@ -138,25 +167,11 @@ export const generateSampledDirection = Fn( ( [
|
|
|
138
167
|
|
|
139
168
|
resultDirection.assign( reflect( V.negate(), H ) );
|
|
140
169
|
|
|
141
|
-
} ).
|
|
142
|
-
|
|
143
|
-
const TBN = constructTBN( { N } );
|
|
144
|
-
const localV = TBN.transpose().mul( V );
|
|
145
|
-
|
|
146
|
-
// VNDF sampling
|
|
147
|
-
const localH = sampleGGXVNDF( { V: localV, roughness: material.roughness, Xi: xi } );
|
|
148
|
-
H.assign( TBN.mul( localH ) );
|
|
149
|
-
|
|
150
|
-
resultDirection.assign( reflect( V.negate(), H ) );
|
|
151
|
-
|
|
152
|
-
} ).Else( () => {
|
|
153
|
-
|
|
154
|
-
resultDirection.assign( reflect( V.negate(), N ) );
|
|
155
|
-
resultIsDelta.assign( true );
|
|
170
|
+
} ).Else( isotropicSpecular );
|
|
156
171
|
|
|
157
|
-
|
|
172
|
+
} );
|
|
158
173
|
|
|
159
|
-
|
|
174
|
+
if ( layers.sheen ) lobes = lobes.ElseIf( rand.lessThan( cumulativeSheen ), () => {
|
|
160
175
|
|
|
161
176
|
H.assign( ImportanceSampleGGX( { N, roughness: sheenSamplingRoughness( material.sheenRoughness ), Xi: xi } ) );
|
|
162
177
|
resultDirection.assign( reflect( V.negate(), H ) );
|
|
@@ -165,7 +180,9 @@ export const generateSampledDirection = Fn( ( [
|
|
|
165
180
|
// would add a P(reject)·cos term to the true density that calculateBSDFSamplingPDF cannot
|
|
166
181
|
// model, and dividing by the smaller modelled density inflated the lobe 23 %.
|
|
167
182
|
|
|
168
|
-
} )
|
|
183
|
+
} );
|
|
184
|
+
|
|
185
|
+
if ( layers.clearcoat ) lobes = lobes.ElseIf( rand.lessThan( cumulativeClearcoat ), () => {
|
|
169
186
|
|
|
170
187
|
// VNDF, not ImportanceSampleGGX: the mixture density reports this lobe with
|
|
171
188
|
// calculateVNDFPDF, and sampling the half-vector GGX distribution instead would make the
|
|
@@ -175,7 +192,16 @@ export const generateSampledDirection = Fn( ( [
|
|
|
175
192
|
H.assign( ccTBN.mul( sampleGGXVNDF( { V: ccTBN.transpose().mul( V ), roughness: clearcoatRoughness, Xi: xi } ) ) );
|
|
176
193
|
resultDirection.assign( reflect( V.negate(), H ) );
|
|
177
194
|
|
|
178
|
-
} )
|
|
195
|
+
} );
|
|
196
|
+
|
|
197
|
+
if ( layers.diffuseTransmission ) lobes = lobes.ElseIf( rand.lessThan( cumulativeDiffuseTransmission ), () => {
|
|
198
|
+
|
|
199
|
+
resultDirection.assign( ImportanceSampleCosine( { N: N.negate(), xi } ) );
|
|
200
|
+
resultIsDiffuseTransmission.assign( true );
|
|
201
|
+
|
|
202
|
+
} );
|
|
203
|
+
|
|
204
|
+
lobes.Else( () => {
|
|
179
205
|
|
|
180
206
|
const entering = dot( V, N ).greaterThan( 0.0 );
|
|
181
207
|
// pathWavelength=0 — the spectral lock happens downstream; colorWeight is carried out on
|
|
@@ -193,10 +219,11 @@ export const generateSampledDirection = Fn( ( [
|
|
|
193
219
|
|
|
194
220
|
} );
|
|
195
221
|
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
If( resultIsTransmission.not(), () => {
|
|
222
|
+
// Below the surface only the diffuse transmission lobe draws, so its own density is the mixture's.
|
|
223
|
+
const reflected = () => {
|
|
199
224
|
|
|
225
|
+
// One mixture density for every reflection lobe: any of them could have produced this
|
|
226
|
+
// direction, so the chosen lobe's own pdf is not the density we sampled from.
|
|
200
227
|
const dotsOut = DotProducts.wrap( computeDotProductsAniso( N, V, resultDirection, material ) );
|
|
201
228
|
If( resultIsDelta, () => {
|
|
202
229
|
|
|
@@ -213,7 +240,15 @@ export const generateSampledDirection = Fn( ( [
|
|
|
213
240
|
|
|
214
241
|
} );
|
|
215
242
|
|
|
216
|
-
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
if ( ! layers.diffuseTransmission ) If( resultIsTransmission.not(), reflected );
|
|
246
|
+
else If( resultIsDiffuseTransmission, () => {
|
|
247
|
+
|
|
248
|
+
resultValue.assign( evaluateDiffuseTransmission( material, max( dot( N, V ), 0.001 ) ) );
|
|
249
|
+
resultPdf.assign( diffuseTransmissionPdf( weights.diffuseTransmission, dot( N, resultDirection ).negate() ) );
|
|
250
|
+
|
|
251
|
+
} ).ElseIf( resultIsTransmission.not(), reflected );
|
|
217
252
|
|
|
218
253
|
resultPdf.assign( max( resultPdf, MIN_PDF ) );
|
|
219
254
|
|
|
@@ -223,6 +258,7 @@ export const generateSampledDirection = Fn( ( [
|
|
|
223
258
|
pdf: resultPdf,
|
|
224
259
|
isTransmission: resultIsTransmission,
|
|
225
260
|
colorWeight: resultColorWeight,
|
|
261
|
+
isDiffuseTransmission: resultIsDiffuseTransmission,
|
|
226
262
|
} );
|
|
227
263
|
|
|
228
264
|
} );
|
package/src/TSL/Random.js
CHANGED
|
@@ -123,6 +123,8 @@ export const RandomPointInCircle = ( rngState ) => {
|
|
|
123
123
|
// +13 clearcoat lobe (Clearcoat)
|
|
124
124
|
// +14 area/spot light phi (LightsSampling)
|
|
125
125
|
// +15 light uv second component (LightsSampling)
|
|
126
|
+
// +16 light path to connect to (ConnectKernel, bidirectional)
|
|
127
|
+
// +17 light vertex on that path (ConnectKernel, bidirectional)
|
|
126
128
|
//
|
|
127
129
|
// Stride is 32 rather than the ~16 in use so a new call site does not force a renumbering.
|
|
128
130
|
// The index only keys a scramble seed, so spare slots cost nothing.
|
|
@@ -131,6 +133,8 @@ export const RandomPointInCircle = ( rngState ) => {
|
|
|
131
133
|
// the next bounce's dimensions, so they live above AUX_BASE:
|
|
132
134
|
// AUX_BASE + 0..63 env-backdrop blur taps (ShadeKernel)
|
|
133
135
|
// AUX_BASE + 64.. light reservoir, one dimension per scene light (LightsSampling)
|
|
136
|
+
// AUX_BASE + 2048.. light subpath emission (LightGenerateKernel); light paths draw at pixels
|
|
137
|
+
// offset by LIGHT_PIXEL_ROW_OFFSET rows, so no camera path shares a sequence
|
|
134
138
|
export const SAMPLER_DIMS_PER_BOUNCE = 32;
|
|
135
139
|
export const SAMPLER_DIM_AUX_BASE = 4096;
|
|
136
140
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A renderer's own scene resources for its shaders: material texture buckets, the albedo maps alpha-cutout
|
|
3
|
+
* shadow rays read, gobo and IES textures, and the alpha-shadow switch. They ride in the build context of each
|
|
4
|
+
* kernel that reads them: a TSL function's body runs when its kernel compiles, often at the first dispatch, so
|
|
5
|
+
* module-level state was read from whichever renderer — or pipeline stage — had set it last.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {Object} SceneResources
|
|
8
|
+
* @property {Array<Node>} srgbBuckets - sRGB material bucket texture nodes (albedo, emissive, sheen, specular colour)
|
|
9
|
+
* @property {Array<Node>} linearBuckets - linear material bucket texture nodes
|
|
10
|
+
* @property {?Array<Node>} shadowAlbedoMaps - sRGB buckets alpha-cutout shadow rays sample, or null
|
|
11
|
+
* @property {?Node} goboMaps - spot / directional gobo texture array, or null
|
|
12
|
+
* @property {?Node} iesProfiles - IES profile texture array, or null
|
|
13
|
+
* @property {?Node} alphaShadows - int uniform, 1 = shadow rays test texture alpha; null = never
|
|
14
|
+
* @property {?Object<string, boolean>} materialLayers - the MATERIAL_LAYERS some material uses; null = all of them
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { context } from 'three/tsl';
|
|
18
|
+
|
|
19
|
+
const KEY = 'rayzeeScene';
|
|
20
|
+
|
|
21
|
+
/** Builds `node` (a kernel's root call) with these resources in its context. */
|
|
22
|
+
export const withSceneResources = ( node, resources ) => context( node, { [ KEY ]: resources } );
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The resources of the kernel being built; call from a TSL function body with its builder.
|
|
26
|
+
* @returns {SceneResources}
|
|
27
|
+
*/
|
|
28
|
+
export function sceneResources( builder ) {
|
|
29
|
+
|
|
30
|
+
const resources = builder.context[ KEY ];
|
|
31
|
+
if ( ! resources ) throw new Error( 'this kernel samples scene resources: build its root with withSceneResources()' );
|
|
32
|
+
return resources;
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The material layers a kernel compiles only while some material in its scene has them. */
|
|
37
|
+
export const MATERIAL_LAYERS = Object.freeze( [ 'clearcoat', 'sheen', 'iridescence', 'anisotropy', 'subsurface', 'dispersion', 'diffuseTransmission' ] );
|
|
38
|
+
|
|
39
|
+
/** Every layer compiled in. */
|
|
40
|
+
export const ALL_MATERIAL_LAYERS = Object.freeze( Object.fromEntries( MATERIAL_LAYERS.map( ( layer ) => [ layer, true ] ) ) );
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The material layers the kernel being built compiles; call from a TSL function body with its builder. A layer no
|
|
44
|
+
* material uses is left out of every function that reads it. Outside a scene kernel (a test), every layer is in.
|
|
45
|
+
* @returns {Object<string, boolean>}
|
|
46
|
+
*/
|
|
47
|
+
export function materialLayers( builder ) {
|
|
48
|
+
|
|
49
|
+
return builder.context[ KEY ]?.materialLayers ?? ALL_MATERIAL_LAYERS;
|
|
50
|
+
|
|
51
|
+
}
|