rayzee 9.4.2 → 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 +223 -21
- 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 -44079
- 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 +22 -8
- 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/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 +85 -33
- 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
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LightSplatKernel.js — light tracing: every stored light vertex to the pinhole, into the pixel it
|
|
3
|
+
* projects to. Fixed-point u32 channels: WGSL has no float atomics, and integer sums are order-free.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Fn, float, vec3, vec4, int, uint, If, instanceIndex, atomicAdd, atomicLoad, atomicStore, Return,
|
|
8
|
+
dot, sqrt, max, min, floor, length, localId, workgroupId,
|
|
9
|
+
} from 'three/tsl';
|
|
10
|
+
|
|
11
|
+
import { evaluateMaterialResponse } from './MaterialEvaluation.js';
|
|
12
|
+
import { calculateMaterialPDF } from './LightsSampling.js';
|
|
13
|
+
import { traceShadowRayRefractiveOpaque } from './LightsDirect.js';
|
|
14
|
+
import { traverseBVHShadowCameraCulled } from './BVHTraversal.js';
|
|
15
|
+
import { offsetRayOrigin, SHADOW_END, sanitizeRGB } from './Common.js';
|
|
16
|
+
import { pcgHash } from './Random.js';
|
|
17
|
+
import { regularizePathContribution } from './PathTracerCore.js';
|
|
18
|
+
import { resolveSurfaceMaterial, lightEndCosine, misPartial, misWeight, strategyWeight, STRATEGY, SPLAT_SCALE, SPLAT_MAX } from './Bidirectional.js';
|
|
19
|
+
import {
|
|
20
|
+
cachedVertex, readVertexRecord, readLightPathLength, readRayRadiance, writeRayRadiance,
|
|
21
|
+
} from '../Processor/PackedRayBuffer.js';
|
|
22
|
+
|
|
23
|
+
const WG_SIZE = 256;
|
|
24
|
+
const RESOLVE_WG_SIZE = 16;
|
|
25
|
+
|
|
26
|
+
export function buildLightSplatKernel( params ) {
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
hitBufferRO, splatBuffer,
|
|
30
|
+
bvhBuffer, triangleBuffer, materialBuffer,
|
|
31
|
+
lightPaths, slotsPerPath, lightTag, strategyView,
|
|
32
|
+
cameraPosition, cameraForward, cameraViewMatrix, cameraProjectionMatrix, pixelArea,
|
|
33
|
+
renderWidth, renderHeight,
|
|
34
|
+
globalIlluminationIntensity, fireflyThreshold, accumFrame, frame,
|
|
35
|
+
mergeVm = null,
|
|
36
|
+
} = params;
|
|
37
|
+
|
|
38
|
+
return Fn( () => {
|
|
39
|
+
|
|
40
|
+
const slot = instanceIndex.toVar();
|
|
41
|
+
If( slot.greaterThanEqual( lightPaths.mul( slotsPerPath ) ), () => {
|
|
42
|
+
|
|
43
|
+
Return();
|
|
44
|
+
|
|
45
|
+
} );
|
|
46
|
+
|
|
47
|
+
const c = slot.mod( slotsPerPath ).toVar();
|
|
48
|
+
const stored = readLightPathLength( hitBufferRO, cachedVertex( slot.sub( c ) ), lightTag );
|
|
49
|
+
If( c.greaterThanEqual( stored ), () => {
|
|
50
|
+
|
|
51
|
+
Return();
|
|
52
|
+
|
|
53
|
+
} );
|
|
54
|
+
|
|
55
|
+
const record = readVertexRecord( hitBufferRO, cachedVertex( slot ) );
|
|
56
|
+
const v = { ...record, vm: mergeVm?.( record.position ) ?? null };
|
|
57
|
+
|
|
58
|
+
const toCamera = cameraPosition.sub( v.position ).toVar();
|
|
59
|
+
const dist2 = max( dot( toCamera, toCamera ), 1e-12 ).toVar();
|
|
60
|
+
const dir = toCamera.div( sqrt( dist2 ) ).toVar();
|
|
61
|
+
const cosFacet = dot( dir, v.facetN ).toVar();
|
|
62
|
+
const cosCamera = dot( dir, cameraForward ).negate().toVar();
|
|
63
|
+
// The camera is this vertex's viewer: like a camera path, only the facet decides which side it is on.
|
|
64
|
+
If( cosFacet.lessThanEqual( 0.0 ).or( cosCamera.lessThanEqual( 1e-6 ) ).or( v.extra.notEqual( uint( 0 ) ) ), () => {
|
|
65
|
+
|
|
66
|
+
Return();
|
|
67
|
+
|
|
68
|
+
} );
|
|
69
|
+
|
|
70
|
+
const clip = cameraProjectionMatrix.mul( cameraViewMatrix.mul( vec4( v.position, 1.0 ) ) ).toVar();
|
|
71
|
+
If( clip.w.lessThanEqual( 0.0 ), () => {
|
|
72
|
+
|
|
73
|
+
Return();
|
|
74
|
+
|
|
75
|
+
} );
|
|
76
|
+
|
|
77
|
+
const px = floor( clip.x.div( clip.w ).mul( 0.5 ).add( 0.5 ).mul( float( renderWidth ) ) ).toVar();
|
|
78
|
+
const py = floor( float( 0.5 ).sub( clip.y.div( clip.w ).mul( 0.5 ) ).mul( float( renderHeight ) ) ).toVar();
|
|
79
|
+
If( px.lessThan( 0.0 ).or( py.lessThan( 0.0 ) ).or( px.greaterThanEqual( float( renderWidth ) ) ).or( py.greaterThanEqual( float( renderHeight ) ) ), () => {
|
|
80
|
+
|
|
81
|
+
Return();
|
|
82
|
+
|
|
83
|
+
} );
|
|
84
|
+
|
|
85
|
+
const material = resolveSurfaceMaterial( v.materialIndex, v.uv, v.N, materialBuffer );
|
|
86
|
+
const f = evaluateMaterialResponse( dir, v.V, v.N, material ).toVar();
|
|
87
|
+
|
|
88
|
+
// The pinhole's solid-angle density for a uniform point in a pixel: 1 / (A_pixel cos³θ).
|
|
89
|
+
const cameraPdfW = float( 1.0 ).div( pixelArea.mul( cosCamera ).mul( cosCamera ).mul( cosCamera ) );
|
|
90
|
+
const toSurface = cameraPdfW.div( dist2 ).toVar();
|
|
91
|
+
|
|
92
|
+
// Georgiev (46); the camera side has no sum of its own.
|
|
93
|
+
const wLight = misPartial( toSurface.mul( cosFacet ).div( float( lightPaths ) ), v, calculateMaterialPDF( dir, v.V, v.N, material ) );
|
|
94
|
+
const bounces = c.add( uint( 1 ) );
|
|
95
|
+
const gi = float( 1.0 ).toVar();
|
|
96
|
+
If( bounces.greaterThan( uint( 1 ) ), () => {
|
|
97
|
+
|
|
98
|
+
gi.assign( globalIlluminationIntensity );
|
|
99
|
+
|
|
100
|
+
} );
|
|
101
|
+
|
|
102
|
+
const contribution = regularizePathContribution(
|
|
103
|
+
v.throughput.mul( f ).mul( lightEndCosine( v.V, v.N, v.facetN, dir ) )
|
|
104
|
+
.mul( toSurface.div( float( lightPaths ) ).mul( strategyWeight( strategyView, STRATEGY.LIGHT_TRACE, misWeight( wLight, float( 0.0 ) ) ) ).mul( gi ) ),
|
|
105
|
+
float( c ), fireflyThreshold, int( accumFrame ),
|
|
106
|
+
).toVar();
|
|
107
|
+
|
|
108
|
+
const origin = offsetRayOrigin( v.position, v.facetN ).toVar();
|
|
109
|
+
const segment = cameraPosition.sub( origin ).toVar();
|
|
110
|
+
const segmentLength = length( segment ).toVar();
|
|
111
|
+
// This segment stands in for the primary ray, which sees through the faces it culls.
|
|
112
|
+
const visibility = traceShadowRayRefractiveOpaque(
|
|
113
|
+
origin, segment.div( segmentLength ), segmentLength.mul( SHADOW_END ),
|
|
114
|
+
traverseBVHShadowCameraCulled, bvhBuffer, triangleBuffer, materialBuffer,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
If( visibility.greaterThan( 0.0 ), () => {
|
|
118
|
+
|
|
119
|
+
const value = min( sanitizeRGB( contribution.mul( visibility ) ), vec3( SPLAT_MAX ) ).mul( SPLAT_SCALE ).toVar();
|
|
120
|
+
const dither = float( pcgHash( { state: slot.bitXor( uint( frame ).mul( uint( 0x9E3779B9 ) ) ) } ).shiftRight( uint( 8 ) ) ).mul( 1.0 / 16777216.0 ).toVar();
|
|
121
|
+
const base = uint( py ).mul( uint( renderWidth ) ).add( uint( px ) ).mul( uint( 3 ) ).toVar();
|
|
122
|
+
atomicAdd( splatBuffer.element( base ), uint( floor( value.x.add( dither ) ) ) );
|
|
123
|
+
atomicAdd( splatBuffer.element( base.add( uint( 1 ) ) ), uint( floor( value.y.add( dither ) ) ) );
|
|
124
|
+
atomicAdd( splatBuffer.element( base.add( uint( 2 ) ) ), uint( floor( value.z.add( dither ) ) ) );
|
|
125
|
+
|
|
126
|
+
} );
|
|
127
|
+
|
|
128
|
+
} );
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function buildSplatResolveKernel( params ) {
|
|
133
|
+
|
|
134
|
+
const { rayBufferRW, splatBuffer, renderWidth, chunkRowBase, chunkRows } = params;
|
|
135
|
+
|
|
136
|
+
return Fn( () => {
|
|
137
|
+
|
|
138
|
+
const gx = int( workgroupId.x ).mul( RESOLVE_WG_SIZE ).add( int( localId.x ) );
|
|
139
|
+
const localGy = int( workgroupId.y ).mul( RESOLVE_WG_SIZE ).add( int( localId.y ) );
|
|
140
|
+
|
|
141
|
+
If( gx.lessThan( renderWidth ).and( localGy.lessThan( chunkRows ) ), () => {
|
|
142
|
+
|
|
143
|
+
const rayID = uint( localGy.mul( renderWidth ).add( gx ) );
|
|
144
|
+
const base = uint( localGy.add( chunkRowBase ).mul( renderWidth ).add( gx ) ).mul( uint( 3 ) ).toVar();
|
|
145
|
+
const r = atomicLoad( splatBuffer.element( base ) ).toVar();
|
|
146
|
+
const g = atomicLoad( splatBuffer.element( base.add( uint( 1 ) ) ) ).toVar();
|
|
147
|
+
const b = atomicLoad( splatBuffer.element( base.add( uint( 2 ) ) ) ).toVar();
|
|
148
|
+
|
|
149
|
+
If( r.bitOr( g ).bitOr( b ).notEqual( uint( 0 ) ), () => {
|
|
150
|
+
|
|
151
|
+
atomicStore( splatBuffer.element( base ), uint( 0 ) );
|
|
152
|
+
atomicStore( splatBuffer.element( base.add( uint( 1 ) ) ), uint( 0 ) );
|
|
153
|
+
atomicStore( splatBuffer.element( base.add( uint( 2 ) ) ), uint( 0 ) );
|
|
154
|
+
const radiance = readRayRadiance( rayBufferRW, rayID ).toVar();
|
|
155
|
+
writeRayRadiance( rayBufferRW, rayID, vec4( radiance.xyz.add( vec3( float( r ), float( g ), float( b ) ).div( SPLAT_SCALE ) ), radiance.w ) );
|
|
156
|
+
|
|
157
|
+
} );
|
|
158
|
+
|
|
159
|
+
} );
|
|
160
|
+
|
|
161
|
+
} );
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export { WG_SIZE as LIGHT_SPLAT_WG_SIZE, RESOLVE_WG_SIZE as SPLAT_RESOLVE_WG_SIZE };
|
package/src/TSL/LightsCore.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Lights Core - Ported from lights_core.fs
|
|
2
2
|
// Light data structures, access functions, and utility functions
|
|
3
3
|
|
|
4
|
+
import { sceneResources } from './SceneResources.js';
|
|
4
5
|
import {
|
|
5
6
|
Fn, wgslFn,
|
|
6
7
|
vec2,
|
|
@@ -268,28 +269,14 @@ export const getSpotAttenuation = /*@__PURE__*/ wgslFn( `
|
|
|
268
269
|
// SPOT LIGHT GOBO (PROJECTION MASK) SAMPLING
|
|
269
270
|
// ================================================================================
|
|
270
271
|
|
|
271
|
-
// Module-level state for spot light gobo masks.
|
|
272
|
-
// Set by ShaderBuilder before graph construction.
|
|
273
|
-
let _goboMapsTexNode = null;
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Set the DataArrayTexture node used to sample spot light gobo masks.
|
|
277
|
-
* Must be called before the shader graph is constructed.
|
|
278
|
-
* @param {TextureNode} node - TSL texture node for the gobo DataArrayTexture
|
|
279
|
-
*/
|
|
280
|
-
export function setGoboMapsTexture( node ) {
|
|
281
|
-
|
|
282
|
-
_goboMapsTexNode = node;
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
286
272
|
// Sample a spot light's gobo mask. Returns 1.0 if no gobo assigned.
|
|
287
273
|
// Projects the surface direction onto a plane perpendicular to the light's
|
|
288
274
|
// forward axis at unit distance; cone edge maps to UV ±0.5 around centre.
|
|
289
275
|
//
|
|
290
276
|
// `lightDir` = unit direction from surface TO light (matches `LightSample.direction`).
|
|
291
|
-
export const sampleSpotGoboMask = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
|
|
277
|
+
export const sampleSpotGoboMask = /*@__PURE__*/ Fn( ( [ light, lightDir ], builder ) => {
|
|
292
278
|
|
|
279
|
+
const { goboMaps } = sceneResources( builder );
|
|
293
280
|
const mask = float( 1.0 ).toVar();
|
|
294
281
|
|
|
295
282
|
If( light.goboIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
@@ -319,13 +306,13 @@ export const sampleSpotGoboMask = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
|
|
|
319
306
|
const u = clamp( px.mul( invTan ).add( 0.5 ), float( 0.0 ), float( 1.0 ) );
|
|
320
307
|
const v = clamp( py.mul( invTan ).add( 0.5 ), float( 0.0 ), float( 1.0 ) );
|
|
321
308
|
|
|
322
|
-
if (
|
|
309
|
+
if ( goboMaps ) {
|
|
323
310
|
|
|
324
311
|
// Sample min(.r, .a) so masks encoded in either RGB-luminance
|
|
325
312
|
// or alpha (Kenney's "Transparent" variants store the shape in alpha
|
|
326
313
|
// with RGB=white) both produce the expected result.
|
|
327
314
|
// Sign of goboIntensity encodes inversion: negative = inverted, |value| = strength.
|
|
328
|
-
const tex = texture(
|
|
315
|
+
const tex = texture( goboMaps, vec2( u, v ) ).depth( light.goboIndex );
|
|
329
316
|
const sample = min( tex.r, tex.a );
|
|
330
317
|
const inverted = light.goboIntensity.lessThan( 0.0 );
|
|
331
318
|
const effective = select( inverted, float( 1.0 ).sub( sample ), sample );
|
|
@@ -353,8 +340,9 @@ export const sampleSpotGoboMask = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
|
|
|
353
340
|
// can cover any scene size by adjusting the scale.
|
|
354
341
|
//
|
|
355
342
|
// `surfacePoint` = world-space position of the surface being shaded.
|
|
356
|
-
export const sampleDirectionalGoboMask = /*@__PURE__*/ Fn( ( [ light, surfacePoint ] ) => {
|
|
343
|
+
export const sampleDirectionalGoboMask = /*@__PURE__*/ Fn( ( [ light, surfacePoint ], builder ) => {
|
|
357
344
|
|
|
345
|
+
const { goboMaps } = sceneResources( builder );
|
|
358
346
|
const mask = float( 1.0 ).toVar();
|
|
359
347
|
|
|
360
348
|
If( light.goboIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
@@ -379,9 +367,9 @@ export const sampleDirectionalGoboMask = /*@__PURE__*/ Fn( ( [ light, surfacePoi
|
|
|
379
367
|
const uTiled = u.sub( u.floor() );
|
|
380
368
|
const vTiled = v.sub( v.floor() );
|
|
381
369
|
|
|
382
|
-
if (
|
|
370
|
+
if ( goboMaps ) {
|
|
383
371
|
|
|
384
|
-
const tex = texture(
|
|
372
|
+
const tex = texture( goboMaps, vec2( uTiled, vTiled ) ).depth( light.goboIndex );
|
|
385
373
|
const sample = min( tex.r, tex.a );
|
|
386
374
|
const inverted = light.goboIntensity.lessThan( 0.0 );
|
|
387
375
|
const effective = select( inverted, float( 1.0 ).sub( sample ), sample );
|
|
@@ -400,20 +388,6 @@ export const sampleDirectionalGoboMask = /*@__PURE__*/ Fn( ( [ light, surfacePoi
|
|
|
400
388
|
// IES PROFILE (PHOTOMETRIC INTENSITY) SAMPLING
|
|
401
389
|
// ================================================================================
|
|
402
390
|
|
|
403
|
-
// Module-level texture node for IES profile DataArrayTexture.
|
|
404
|
-
// Set by ShaderBuilder before graph construction.
|
|
405
|
-
let _iesProfilesTexNode = null;
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Bind the DataArrayTexture node carrying all loaded IES profiles.
|
|
409
|
-
* @param {TextureNode} node
|
|
410
|
-
*/
|
|
411
|
-
export function setIESProfilesTexture( node ) {
|
|
412
|
-
|
|
413
|
-
_iesProfilesTexNode = node;
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
|
|
417
391
|
// Sample a spot light's IES profile. Returns a normalized multiplier in [0,1]
|
|
418
392
|
// (or 1.0 if no profile assigned).
|
|
419
393
|
//
|
|
@@ -421,8 +395,9 @@ export function setIESProfilesTexture( node ) {
|
|
|
421
395
|
// where V=0 is along the light's "forward" axis (the spot's direction).
|
|
422
396
|
//
|
|
423
397
|
// `lightDir` = unit direction from surface TO light (matches LightSample.direction).
|
|
424
|
-
export const sampleIESProfile = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
|
|
398
|
+
export const sampleIESProfile = /*@__PURE__*/ Fn( ( [ light, lightDir ], builder ) => {
|
|
425
399
|
|
|
400
|
+
const { iesProfiles } = sceneResources( builder );
|
|
426
401
|
const result = float( 1.0 ).toVar();
|
|
427
402
|
|
|
428
403
|
If( light.iesIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
@@ -451,9 +426,9 @@ export const sampleIESProfile = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
|
|
|
451
426
|
const phi = atan( py, px );
|
|
452
427
|
const u = phi.div( float( 2.0 * Math.PI ) ).add( 0.5 );
|
|
453
428
|
|
|
454
|
-
if (
|
|
429
|
+
if ( iesProfiles ) {
|
|
455
430
|
|
|
456
|
-
const sample = texture(
|
|
431
|
+
const sample = texture( iesProfiles, vec2( u, v ) ).depth( light.iesIndex ).r;
|
|
457
432
|
// Blend between flat (1.0) and full profile by iesIntensity.
|
|
458
433
|
const strength = clamp( light.iesIntensity, float( 0.0 ), float( 1.0 ) );
|
|
459
434
|
result.assign( mix( float( 1.0 ), sample, strength ) );
|
package/src/TSL/LightsDirect.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Direct lighting calculations including shadow ray tracing
|
|
3
3
|
// and contribution calculations for all light types.
|
|
4
4
|
|
|
5
|
+
import { sceneResources } from './SceneResources.js';
|
|
5
6
|
import {
|
|
6
7
|
Fn,
|
|
7
8
|
float,
|
|
@@ -28,43 +29,27 @@ import {
|
|
|
28
29
|
import { Ray, ShadowMaterial, HitInfo } from './Struct.js';
|
|
29
30
|
import {
|
|
30
31
|
REC709_LUMINANCE_COEFFICIENTS, getShadowMaterial, instanceRows,
|
|
31
|
-
instanceFaceNormalToWorld, triangleRow,
|
|
32
|
+
instanceFaceNormalToWorld, triangleRow, shadowFlagsSettle, offsetRayOrigin,
|
|
32
33
|
} from './Common.js';
|
|
33
34
|
import { fresnelDielectric } from './Fresnel.js';
|
|
34
35
|
import { calculateBeerLawAbsorption } from './MaterialTransmission.js';
|
|
35
36
|
import { getTransformedUV, sampleBucket } from './TextureSampling.js';
|
|
36
37
|
|
|
37
|
-
// Module-level state for alpha-cutout shadow testing.
|
|
38
|
-
// Set by PathTracer before shade-graph construction.
|
|
39
|
-
let _shadowAlbedoMaps = null;
|
|
40
|
-
|
|
41
|
-
export { setAlphaShadowsUniform } from './Common.js';
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Set the sRGB bucket texture node array for alpha-aware shadow rays (albedo alpha).
|
|
45
|
-
* Must be called before the shade graph is constructed.
|
|
46
|
-
* @param {Array} buckets - K sRGB bucket texture nodes
|
|
47
|
-
*/
|
|
48
|
-
export function setShadowAlbedoMaps( buckets ) {
|
|
49
|
-
|
|
50
|
-
_shadowAlbedoMaps = buckets;
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
38
|
// ================================================================================
|
|
55
39
|
// SHADOW RAY TRACING
|
|
56
40
|
// ================================================================================
|
|
57
41
|
|
|
58
42
|
// Note: traverseBVH is passed as a parameter to avoid circular dependency
|
|
59
|
-
|
|
43
|
+
const makeTraceShadowRay = ( refractiveBlocks ) => Fn( ( [
|
|
60
44
|
origin, dir, maxDist,
|
|
61
45
|
// BVH traversal function and textures passed as parameters
|
|
62
46
|
traverseBVHShadowFn,
|
|
63
47
|
bvhBuffer,
|
|
64
48
|
triangleBuffer,
|
|
65
49
|
materialBuffer,
|
|
66
|
-
] ) => {
|
|
50
|
+
], builder ) => {
|
|
67
51
|
|
|
52
|
+
const { alphaShadows, shadowAlbedoMaps } = sceneResources( builder );
|
|
68
53
|
const transmittance = float( 1.0 ).toVar();
|
|
69
54
|
const rayOrigin = origin.toVar();
|
|
70
55
|
const remainingDist = float( maxDist ).toVar();
|
|
@@ -93,7 +78,7 @@ export const traceShadowRay = Fn( ( [
|
|
|
93
78
|
// alphaMode/transparent/transmission/opacity all indicate a fully opaque surface.
|
|
94
79
|
// Short-circuits the 7-slot getShadowMaterial fetch and the whole alpha decision tree.
|
|
95
80
|
const flags = triangleRow( triangleBuffer, shadowHit.triangleIndex, 4 ).z;
|
|
96
|
-
If( shadowFlagsSettle( flags ), () => {
|
|
81
|
+
If( shadowFlagsSettle( flags, alphaShadows ), () => {
|
|
97
82
|
|
|
98
83
|
transmittance.assign( 0.0 );
|
|
99
84
|
Break();
|
|
@@ -111,14 +96,13 @@ export const traceShadowRay = Fn( ( [
|
|
|
111
96
|
// ---------------------------------------------------------------
|
|
112
97
|
const alphaCutout = tslBool( false ).toVar();
|
|
113
98
|
|
|
114
|
-
const alphaShadows = getAlphaShadowsUniform();
|
|
115
99
|
if ( alphaShadows ) If( alphaShadows.equal( int( 1 ) ), () => {
|
|
116
100
|
|
|
117
101
|
// Sample texture alpha once (shared by MASK and BLEND paths).
|
|
118
102
|
// Deferred UV: barycentrics in shadowHit.uv, triangle index in shadowHit.triangleIndex.
|
|
119
103
|
const texAlpha = float( 1.0 ).toVar();
|
|
120
104
|
|
|
121
|
-
if (
|
|
105
|
+
if ( shadowAlbedoMaps ) {
|
|
122
106
|
|
|
123
107
|
If( shadowMaterial.albedoMapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
124
108
|
|
|
@@ -129,7 +113,7 @@ export const traceShadowRay = Fn( ( [
|
|
|
129
113
|
const uvData2 = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 4 ).xy );
|
|
130
114
|
const hitUV = uvData1.xy.mul( baryW ).add( uvData1.zw.mul( baryU ) ).add( uvData2.mul( baryV ) );
|
|
131
115
|
const albedoUV = getTransformedUV( { uv: hitUV, transform: shadowMaterial.albedoTransform } );
|
|
132
|
-
texAlpha.assign( sampleBucket(
|
|
116
|
+
texAlpha.assign( sampleBucket( shadowAlbedoMaps, shadowMaterial.albedoMapIndex, albedoUV ).a );
|
|
133
117
|
|
|
134
118
|
} );
|
|
135
119
|
|
|
@@ -176,7 +160,12 @@ export const traceShadowRay = Fn( ( [
|
|
|
176
160
|
rayOrigin.assign( shadowHit.hitPoint.add( dir.mul( alphaEps ) ) );
|
|
177
161
|
remainingDist.subAssign( shadowHit.dst.add( alphaEps ) );
|
|
178
162
|
|
|
179
|
-
} ).ElseIf( shadowMaterial.transmission.greaterThan( 0.0 ), () => {
|
|
163
|
+
} ).ElseIf( shadowMaterial.transmission.greaterThan( 0.0 ), refractiveBlocks ? () => {
|
|
164
|
+
|
|
165
|
+
transmittance.assign( 0.0 );
|
|
166
|
+
Break();
|
|
167
|
+
|
|
168
|
+
} : () => {
|
|
180
169
|
|
|
181
170
|
// Deferred geometric-normal compute — refetch triangle positions and
|
|
182
171
|
// derive the normal here so opaque/alpha-cutout shadow hits don't pay
|
|
@@ -262,6 +251,11 @@ export const traceShadowRay = Fn( ( [
|
|
|
262
251
|
|
|
263
252
|
} );
|
|
264
253
|
|
|
254
|
+
export const traceShadowRay = /*@__PURE__*/ makeTraceShadowRay( false );
|
|
255
|
+
|
|
256
|
+
// For the bidirectional strategies: their light paths carry what refracts, so glass must block.
|
|
257
|
+
export const traceShadowRayRefractiveOpaque = /*@__PURE__*/ makeTraceShadowRay( true );
|
|
258
|
+
|
|
265
259
|
// ================================================================================
|
|
266
260
|
// RAY OFFSET CALCULATION
|
|
267
261
|
// ================================================================================
|
|
@@ -273,9 +267,14 @@ export const calculateRayOffset = Fn( ( [ hitPoint, normal ] ) => offsetRayOrigi
|
|
|
273
267
|
// LIGHT IMPORTANCE ESTIMATION
|
|
274
268
|
// ================================================================================
|
|
275
269
|
|
|
270
|
+
// A light's cosine for importance: either side for a surface that passes light through diffusely.
|
|
271
|
+
const importanceCosine = ( normal, direction, material ) => select(
|
|
272
|
+
material.diffuseTransmission.greaterThan( 0.0 ), abs( dot( normal, direction ) ), max( float( 0.0 ), dot( normal, direction ) ),
|
|
273
|
+
);
|
|
274
|
+
|
|
276
275
|
export const calculateDirectionalLightImportance = Fn( ( [ light, normal, material, bounceIndex ] ) => {
|
|
277
276
|
|
|
278
|
-
const NoL =
|
|
277
|
+
const NoL = importanceCosine( normal, light.direction, material );
|
|
279
278
|
const result = float( 0.0 ).toVar();
|
|
280
279
|
|
|
281
280
|
If( NoL.greaterThan( 0.0 ), () => {
|
|
@@ -312,7 +311,7 @@ export const estimateLightImportance = Fn( ( [ light, hitPoint, normal, material
|
|
|
312
311
|
const distSq = dist.mul( dist );
|
|
313
312
|
|
|
314
313
|
const lightDir = toLight.div( dist );
|
|
315
|
-
const NoL =
|
|
314
|
+
const NoL = importanceCosine( normal, lightDir, material );
|
|
316
315
|
const result = float( 0.0 ).toVar();
|
|
317
316
|
|
|
318
317
|
If( NoL.greaterThan( 0.0 ), () => {
|
|
@@ -366,7 +365,7 @@ export const calculatePointLightImportance = Fn( ( [ light, hitPoint, normal, ma
|
|
|
366
365
|
|
|
367
366
|
const dist = sqrt( distSq );
|
|
368
367
|
const lightDir = toLight.div( dist );
|
|
369
|
-
const NoL =
|
|
368
|
+
const NoL = importanceCosine( normal, lightDir, material );
|
|
370
369
|
|
|
371
370
|
If( NoL.greaterThan( 0.0 ), () => {
|
|
372
371
|
|
|
@@ -417,7 +416,7 @@ export const calculateSpotLightImportance = Fn( ( [ light, hitPoint, normal, mat
|
|
|
417
416
|
If( distSq.greaterThanEqual( 0.001 ), () => {
|
|
418
417
|
|
|
419
418
|
const lightDir = toLight.div( sqrt( distSq ) );
|
|
420
|
-
const NoL =
|
|
419
|
+
const NoL = importanceCosine( normal, lightDir, material );
|
|
421
420
|
|
|
422
421
|
If( NoL.greaterThan( 0.0 ), () => {
|
|
423
422
|
|
|
@@ -32,6 +32,8 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
32
32
|
brdfSampleDirection, brdfSamplePdf, brdfSampleValue,
|
|
33
33
|
// DirectionSample carries these so refraction throughput does not need a second sampler run.
|
|
34
34
|
isTransmission, brdfSampleColorWeight,
|
|
35
|
+
// Drawn through the surface by the diffuse transmission lobe: its cosine is taken below N.
|
|
36
|
+
isDiffuseTransmission,
|
|
35
37
|
rngState,
|
|
36
38
|
pixelCoord, resolution, frame, dimBase,
|
|
37
39
|
] ) => {
|
|
@@ -61,7 +63,7 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
61
63
|
//
|
|
62
64
|
// brdfSamplePdf is now the true mixture density (calculateBSDFSamplingPDF), so the
|
|
63
65
|
// estimator is just f·NoL/pdf and the MIS pairs share one function by construction.
|
|
64
|
-
const NoL = max( dot( N, brdfSampleDirection ), 0.0 ).toVar();
|
|
66
|
+
const NoL = select( isDiffuseTransmission, dot( N, brdfSampleDirection ).negate(), max( dot( N, brdfSampleDirection ), 0.0 ) ).toVar();
|
|
65
67
|
const pdf = max( brdfSamplePdf, MIN_PDF ).toVar();
|
|
66
68
|
|
|
67
69
|
r_direction.assign( brdfSampleDirection );
|