rayzee 7.7.0 → 7.9.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 +1 -1
- package/dist/assets/{BVHSubtreeWorker-D9GImjGj.js → BVHSubtreeWorker-sNzvxn66.js} +2 -2
- package/dist/assets/{BVHSubtreeWorker-D9GImjGj.js.map → BVHSubtreeWorker-sNzvxn66.js.map} +1 -1
- package/dist/assets/{BVHWorker-CNJ0UBQz.js → BVHWorker-CiVdFrwe.js} +2 -2
- package/dist/assets/{BVHWorker-CNJ0UBQz.js.map → BVHWorker-CiVdFrwe.js.map} +1 -1
- package/dist/rayzee.es.js +2057 -2035
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +55 -55
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +45 -1
- package/src/EngineEvents.js +1 -0
- package/src/Passes/OIDNDenoiser.js +25 -1
- package/src/PathTracerApp.js +36 -0
- package/src/Processor/AssetLoader.js +3 -0
- package/src/Processor/GeometryExtractor.js +35 -9
- package/src/Processor/PackedRayBuffer.js +15 -1
- package/src/Processor/QueueManager.js +13 -1
- package/src/Processor/SceneProcessor.js +176 -113
- package/src/Processor/ShaderBuilder.js +6 -24
- package/src/Processor/TextureCreator.js +16 -32
- package/src/Processor/Workers/BVHWorker.js +6 -1
- package/src/Stages/EdgeFilter.js +4 -3
- package/src/Stages/MotionVector.js +4 -4
- package/src/Stages/NormalDepth.js +20 -30
- package/src/Stages/PathTracer.js +20 -40
- package/src/TSL/DebugKernel.js +0 -2
- package/src/TSL/Debugger.js +1 -8
- package/src/TSL/Displacement.js +10 -10
- package/src/TSL/GenerateKernel.js +5 -1
- package/src/TSL/LightsDirect.js +8 -9
- package/src/TSL/LightsIndirect.js +15 -9
- package/src/TSL/LightsSampling.js +9 -19
- package/src/TSL/Random.js +11 -1
- package/src/TSL/ShadeKernel.js +1 -6
- package/src/TSL/TextureSampling.js +155 -34
- package/src/managers/EnvironmentManager.js +11 -0
- package/src/managers/MaterialDataManager.js +71 -39
- package/src/managers/RenderTargetManager.js +0 -522
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { Fn, vec3, vec4, float, int, uint, uvec2, uniform, normalize, mat3, storage, If,
|
|
2
|
-
|
|
2
|
+
textureStore, workgroupId, localId } from 'three/tsl';
|
|
3
3
|
import { RenderTarget, StorageTexture } from 'three/webgpu';
|
|
4
|
-
import { HalfFloatType, RGBAFormat, NearestFilter,
|
|
4
|
+
import { HalfFloatType, RGBAFormat, NearestFilter, Matrix4, Box2, Vector2 } from 'three';
|
|
5
5
|
import { RenderStage, StageExecutionMode } from '../Pipeline/RenderStage.js';
|
|
6
6
|
import { MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
|
|
7
7
|
import { Ray, HitInfo, RayTracingMaterial, UVCache } from '../TSL/Struct.js';
|
|
8
8
|
import { traverseBVH } from '../TSL/BVHTraversal.js';
|
|
9
9
|
import { getMaterial } from '../TSL/Common.js';
|
|
10
|
-
import { computeUVCache, processNormal, processBump } from '../TSL/TextureSampling.js';
|
|
10
|
+
import { computeUVCache, processNormal, processBump, buildBucketTextureNodes, refreshBucketTextureNodes, setMaterialBucketTextures } from '../TSL/TextureSampling.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* NormalDepth — primary-ray G-buffer for SVGF gates.
|
|
14
14
|
*
|
|
15
|
-
* RGB = geometric world normal · 0.5 + 0.5, A = linear ray distance (sky=
|
|
15
|
+
* RGB = geometric world normal · 0.5 + 0.5, A = linear ray distance (sky=65504, the
|
|
16
|
+
* max HalfFloat value — a finite sentinel so miss−miss depth diffs stay 0, not Inf−Inf=NaN).
|
|
16
17
|
* Geometric (not shading) normals because shading normals carry sub-pixel
|
|
17
18
|
* jitter that breaks the temporal gate's same-pixel-across-frames comparison.
|
|
18
19
|
* The path tracer's MRT already carries shading normals for OIDN; this stage
|
|
@@ -104,22 +105,10 @@ export class NormalDepth extends RenderStage {
|
|
|
104
105
|
this._computeNode = null;
|
|
105
106
|
this._computeBuilt = false;
|
|
106
107
|
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
this.
|
|
111
|
-
this._bumpMapsTex = texture( this._makePlaceholderArray() );
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
_makePlaceholderArray() {
|
|
116
|
-
|
|
117
|
-
const t = new DataArrayTexture( new Uint8Array( [ 128, 128, 255, 255 ] ), 1, 1, 1 );
|
|
118
|
-
t.minFilter = LinearFilter;
|
|
119
|
-
t.magFilter = LinearFilter;
|
|
120
|
-
t.generateMipmaps = false;
|
|
121
|
-
t.needsUpdate = true;
|
|
122
|
-
return t;
|
|
108
|
+
// Independent linear-pool bucket nodes for this pipeline (normal + bump live in the
|
|
109
|
+
// linear pool). Built lazily in _buildCompute from the path tracer's materialData;
|
|
110
|
+
// value-swapped on model load. processNormal/processBump runtime-guard on map indices.
|
|
111
|
+
this._linearBuckets = null;
|
|
123
112
|
|
|
124
113
|
}
|
|
125
114
|
|
|
@@ -185,10 +174,8 @@ export class NormalDepth extends RenderStage {
|
|
|
185
174
|
|
|
186
175
|
}
|
|
187
176
|
|
|
188
|
-
// In-place
|
|
189
|
-
|
|
190
|
-
if ( md?.normalMaps ) this._normalMapsTex.value = md.normalMaps;
|
|
191
|
-
if ( md?.bumpMaps ) this._bumpMapsTex.value = md.bumpMaps;
|
|
177
|
+
// In-place bucket swaps (model change) — graph closes over the nodes, only .value changes.
|
|
178
|
+
if ( this._linearBuckets ) refreshBucketTextureNodes( this._linearBuckets, pt.materialData?.linearBuckets );
|
|
192
179
|
|
|
193
180
|
this._lastTriAttr = pt.triangleStorageAttr || this._lastTriAttr;
|
|
194
181
|
this._lastBvhAttr = pt.bvhStorageAttr || this._lastBvhAttr;
|
|
@@ -203,8 +190,11 @@ export class NormalDepth extends RenderStage {
|
|
|
203
190
|
const triStorage = this._triStorageNode;
|
|
204
191
|
const bvhStorage = this._bvhStorageNode;
|
|
205
192
|
const matStorage = this._matStorageNode;
|
|
206
|
-
|
|
207
|
-
|
|
193
|
+
// Independent linear-pool bucket nodes for this pipeline (normal + bump). The sRGB pool
|
|
194
|
+
// is never sampled here, so it gets placeholders. Publish to the sampling module before
|
|
195
|
+
// the graph is built so processNormal/processBump bake in THESE (per-pipeline) nodes.
|
|
196
|
+
this._linearBuckets = buildBucketTextureNodes( this.pathTracer?.materialData?.linearBuckets );
|
|
197
|
+
setMaterialBucketTextures( buildBucketTextureNodes( null ), this._linearBuckets );
|
|
208
198
|
const camWorld = this.cameraWorldMatrix;
|
|
209
199
|
const camProjInv = this.cameraProjectionMatrixInverse;
|
|
210
200
|
const resW = this.resolutionWidth;
|
|
@@ -248,7 +238,7 @@ export class NormalDepth extends RenderStage {
|
|
|
248
238
|
|
|
249
239
|
const result = hit.didHit.select(
|
|
250
240
|
vec4( encodedNormal, depth ),
|
|
251
|
-
vec4( 0.0, 0.0, 0.0, float(
|
|
241
|
+
vec4( 0.0, 0.0, 0.0, float( 65504.0 ) )
|
|
252
242
|
);
|
|
253
243
|
|
|
254
244
|
textureStore(
|
|
@@ -266,14 +256,14 @@ export class NormalDepth extends RenderStage {
|
|
|
266
256
|
getMaterial( hit.materialIndex, matStorage )
|
|
267
257
|
).toVar();
|
|
268
258
|
const uvCache = UVCache.wrap( computeUVCache( hit.uv, material ) ).toVar();
|
|
269
|
-
const mapped = processNormal(
|
|
270
|
-
shadingNormal.assign( processBump(
|
|
259
|
+
const mapped = processNormal( hit.normal, material, uvCache ).toVar();
|
|
260
|
+
shadingNormal.assign( processBump( mapped, material, uvCache ) );
|
|
271
261
|
|
|
272
262
|
} );
|
|
273
263
|
|
|
274
264
|
const shadingResult = hit.didHit.select(
|
|
275
265
|
vec4( shadingNormal.mul( 0.5 ).add( 0.5 ), depth ),
|
|
276
|
-
vec4( 0.0, 0.0, 0.0, float(
|
|
266
|
+
vec4( 0.0, 0.0, 0.0, float( 65504.0 ) )
|
|
277
267
|
);
|
|
278
268
|
|
|
279
269
|
textureStore(
|
package/src/Stages/PathTracer.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { uniform, texture, storage } from 'three/tsl';
|
|
8
8
|
import { StorageInstancedBufferAttribute } from 'three/webgpu';
|
|
9
9
|
import { PathTracerStage } from './PathTracerStage.js';
|
|
10
|
-
import { PackedRayBuffer, GBUFFER_STRIDE } from '../Processor/PackedRayBuffer.js';
|
|
10
|
+
import { PackedRayBuffer, GBUFFER_STRIDE, freeStorageAttribute } from '../Processor/PackedRayBuffer.js';
|
|
11
11
|
import { QueueManager, COUNTER } from '../Processor/QueueManager.js';
|
|
12
12
|
import { VRAMTracker } from '../Processor/VRAMTracker.js';
|
|
13
13
|
import { KernelManager } from '../Processor/KernelManager.js';
|
|
@@ -17,6 +17,8 @@ import { buildShadeKernel, SHADE_WG_SIZE } from '../TSL/ShadeKernel.js';
|
|
|
17
17
|
import { buildCompactKernel, buildCompactSubgroupKernel, COMPACT_WG_SIZE } from '../TSL/CompactKernel.js';
|
|
18
18
|
import { buildFinalWriteKernel, FINALWRITE_WG_SIZE } from '../TSL/FinalWriteKernel.js';
|
|
19
19
|
import { buildDebugKernel, DEBUG_WG_SIZE } from '../TSL/DebugKernel.js';
|
|
20
|
+
import { setMaterialBucketTextures, buildBucketTextureNodes, refreshBucketTextureNodes } from '../TSL/TextureSampling.js';
|
|
21
|
+
import { setShadowAlbedoMaps } from '../TSL/LightsDirect.js';
|
|
20
22
|
import {
|
|
21
23
|
buildResetGlobalHistKernel, buildGlobalHistKernel, buildGlobalPrefixKernel, buildGlobalScatterKernel,
|
|
22
24
|
SORT_GLOBAL_WG_SIZE, SORT_GLOBAL_MAX_BINS,
|
|
@@ -126,8 +128,8 @@ export class PathTracer extends PathTracerStage {
|
|
|
126
128
|
if ( ! m ) return null;
|
|
127
129
|
return [
|
|
128
130
|
m.materialStorageAttr,
|
|
129
|
-
|
|
130
|
-
m.
|
|
131
|
+
...( m.srgbBuckets || [] ).filter( Boolean ),
|
|
132
|
+
...( m.linearBuckets || [] ).filter( Boolean ),
|
|
131
133
|
];
|
|
132
134
|
|
|
133
135
|
} );
|
|
@@ -449,13 +451,8 @@ export class PathTracer extends PathTracerStage {
|
|
|
449
451
|
|
|
450
452
|
const mat = this.materialData;
|
|
451
453
|
if ( ! mat ) return;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
if ( mat.bumpMaps && t.bumpMaps ) t.bumpMaps.value = mat.bumpMaps;
|
|
455
|
-
if ( mat.metalnessMaps && t.metalnessMaps ) t.metalnessMaps.value = mat.metalnessMaps;
|
|
456
|
-
if ( mat.roughnessMaps && t.roughnessMaps ) t.roughnessMaps.value = mat.roughnessMaps;
|
|
457
|
-
if ( mat.emissiveMaps && t.emissiveMaps ) t.emissiveMaps.value = mat.emissiveMaps;
|
|
458
|
-
if ( mat.displacementMaps && t.displacementMaps ) t.displacementMaps.value = mat.displacementMaps;
|
|
454
|
+
refreshBucketTextureNodes( t.srgbBuckets, mat.srgbBuckets );
|
|
455
|
+
refreshBucketTextureNodes( t.linearBuckets, mat.linearBuckets );
|
|
459
456
|
|
|
460
457
|
}
|
|
461
458
|
|
|
@@ -527,7 +524,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
527
524
|
|
|
528
525
|
if ( ! this._packedBuffers ) {
|
|
529
526
|
|
|
530
|
-
this._packedBuffers = new PackedRayBuffer( maxRays );
|
|
527
|
+
this._packedBuffers = new PackedRayBuffer( maxRays, this.renderer );
|
|
531
528
|
|
|
532
529
|
} else {
|
|
533
530
|
|
|
@@ -541,13 +538,14 @@ export class PathTracer extends PathTracerStage {
|
|
|
541
538
|
// per-pixel, written by Generate/Shade bounce-0 and read only by FinalWrite.
|
|
542
539
|
// 1.25× margin (same as the per-ray buffers) so it survives the in-place-resize range.
|
|
543
540
|
const gBufferVec4s = PackedRayBuffer.requiredCapacity( maxRays ) * GBUFFER_STRIDE;
|
|
541
|
+
freeStorageAttribute( this.renderer, this._gBufferAttr ); // free the outgoing G-buffer on capacity growth
|
|
544
542
|
this._gBufferAttr = new StorageInstancedBufferAttribute( new Uint32Array( gBufferVec4s * 4 ), 4 );
|
|
545
543
|
const gBufferRW = storage( this._gBufferAttr, 'uvec4' );
|
|
546
544
|
const gBufferRO = storage( this._gBufferAttr, 'uvec4' ).toReadOnly();
|
|
547
545
|
|
|
548
546
|
if ( ! this._queueManager ) {
|
|
549
547
|
|
|
550
|
-
this._queueManager = new QueueManager( this._packedBuffers.capacity );
|
|
548
|
+
this._queueManager = new QueueManager( this._packedBuffers.capacity, this.renderer );
|
|
551
549
|
|
|
552
550
|
} else {
|
|
553
551
|
|
|
@@ -661,26 +659,21 @@ export class PathTracer extends PathTracerStage {
|
|
|
661
659
|
// Independent texture nodes (never compiled elsewhere) avoid Three.js TextureNode caching across pipelines; refreshed via _refreshWfTextureNodes.
|
|
662
660
|
const _mat = this.materialData;
|
|
663
661
|
const _env = this.environment;
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
const
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
662
|
+
// Consolidated size-bucket nodes (K sRGB + K linear). Empty buckets get placeholders so
|
|
663
|
+
// every runtime branch references a valid node. Published to the sampling module before
|
|
664
|
+
// the Shade/Debug graphs are built so they bake in these (per-pipeline) nodes.
|
|
665
|
+
const freshSrgbBuckets = buildBucketTextureNodes( _mat.srgbBuckets );
|
|
666
|
+
const freshLinearBuckets = buildBucketTextureNodes( _mat.linearBuckets );
|
|
667
|
+
setMaterialBucketTextures( freshSrgbBuckets, freshLinearBuckets );
|
|
668
|
+
// Alpha-cutout shadow rays sample albedo (sRGB pool) — emitted into the shade graph now.
|
|
669
|
+
setShadowAlbedoMaps( freshSrgbBuckets );
|
|
672
670
|
const freshEnvTex = _env.environmentTexture ? texture( _env.environmentTexture ) : texNodes.envTex;
|
|
673
671
|
|
|
674
672
|
this._wfTexNodes = {
|
|
675
673
|
envTex: freshEnvTex,
|
|
676
674
|
envCDFTex: freshEnvCDF,
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
bumpMaps: freshBumpMaps,
|
|
680
|
-
metalnessMaps: freshMetalnessMaps,
|
|
681
|
-
roughnessMaps: freshRoughnessMaps,
|
|
682
|
-
emissiveMaps: freshEmissiveMaps,
|
|
683
|
-
displacementMaps: freshDisplacementMaps,
|
|
675
|
+
srgbBuckets: freshSrgbBuckets,
|
|
676
|
+
linearBuckets: freshLinearBuckets,
|
|
684
677
|
};
|
|
685
678
|
|
|
686
679
|
// Material-coherence sort gate (experiment): only worthwhile above a few materials.
|
|
@@ -761,13 +754,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
761
754
|
hitBufferRO: pb.hitBuffer.ro,
|
|
762
755
|
counters,
|
|
763
756
|
activeIndicesRO: this._sortMaterials ? qm.getSortedRO() : qm.getActiveReadRO(),
|
|
764
|
-
albedoMaps: freshAlbedoMaps,
|
|
765
|
-
normalMaps: freshNormalMaps,
|
|
766
|
-
bumpMaps: freshBumpMaps,
|
|
767
|
-
metalnessMaps: freshMetalnessMaps,
|
|
768
|
-
roughnessMaps: freshRoughnessMaps,
|
|
769
|
-
emissiveMaps: freshEmissiveMaps,
|
|
770
|
-
displacementMaps: freshDisplacementMaps,
|
|
771
757
|
envTexture: freshEnvTex,
|
|
772
758
|
environmentIntensity: this.environmentIntensity,
|
|
773
759
|
envMatrix: this.environmentMatrix,
|
|
@@ -926,12 +912,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
926
912
|
enableEnvironmentLight: this.enableEnvironment,
|
|
927
913
|
visMode: this.visMode,
|
|
928
914
|
debugVisScale: this.debugVisScale,
|
|
929
|
-
albedoMaps: freshAlbedoMaps,
|
|
930
|
-
normalMaps: freshNormalMaps,
|
|
931
|
-
bumpMaps: freshBumpMaps,
|
|
932
|
-
metalnessMaps: freshMetalnessMaps,
|
|
933
|
-
roughnessMaps: freshRoughnessMaps,
|
|
934
|
-
emissiveMaps: freshEmissiveMaps,
|
|
935
915
|
frame: this.frame,
|
|
936
916
|
} );
|
|
937
917
|
this._kernelManager.register( 'debug',
|
package/src/TSL/DebugKernel.js
CHANGED
|
@@ -30,7 +30,6 @@ export function buildDebugKernel( params ) {
|
|
|
30
30
|
bvhBuffer, triangleBuffer, materialBuffer,
|
|
31
31
|
envTexture, environmentMatrix, environmentIntensity, enableEnvironmentLight,
|
|
32
32
|
visMode, debugVisScale,
|
|
33
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
34
33
|
frame,
|
|
35
34
|
} = params;
|
|
36
35
|
|
|
@@ -73,7 +72,6 @@ export function buildDebugKernel( params ) {
|
|
|
73
72
|
envTexture, environmentMatrix, environmentIntensity, enableEnvironmentLight,
|
|
74
73
|
visMode, debugVisScale,
|
|
75
74
|
pixelCoord, resolution,
|
|
76
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
77
75
|
cameraProjectionMatrix, cameraViewMatrix,
|
|
78
76
|
frame,
|
|
79
77
|
) );
|
package/src/TSL/Debugger.js
CHANGED
|
@@ -71,9 +71,7 @@ export const TraceDebugMode = Fn( ( [
|
|
|
71
71
|
visMode, debugVisScale,
|
|
72
72
|
// Screen info
|
|
73
73
|
pixelCoord, resolution,
|
|
74
|
-
//
|
|
75
|
-
albedoMaps, normalMaps, bumpMaps,
|
|
76
|
-
metalnessMaps, roughnessMaps, emissiveMaps,
|
|
74
|
+
// Material textures are read from the module-level bucket nodes (setMaterialBucketTextures).
|
|
77
75
|
// Camera matrices (for depth debug mode)
|
|
78
76
|
cameraProjectionMatrix, cameraViewMatrix,
|
|
79
77
|
// Frame counter (for stochastic debug modes)
|
|
@@ -204,7 +202,6 @@ export const TraceDebugMode = Fn( ( [
|
|
|
204
202
|
|
|
205
203
|
// Sample all textures to get emissive
|
|
206
204
|
const matSamples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
207
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
208
205
|
material, hitInfo.uv, hitInfo.normal,
|
|
209
206
|
) ).toVar();
|
|
210
207
|
|
|
@@ -249,7 +246,6 @@ export const TraceDebugMode = Fn( ( [
|
|
|
249
246
|
|
|
250
247
|
// Get material-mapped normal (same as what's used in main shader)
|
|
251
248
|
const matSamples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
252
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
253
249
|
material, hitInfo.uv, hitInfo.normal,
|
|
254
250
|
) ).toVar();
|
|
255
251
|
|
|
@@ -296,7 +292,6 @@ export const TraceDebugMode = Fn( ( [
|
|
|
296
292
|
const material = RayTracingMaterial.wrap( getMaterial( hitInfo.materialIndex, materialBuffer ) ).toVar();
|
|
297
293
|
|
|
298
294
|
const matSamples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
299
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
300
295
|
material, hitInfo.uv, hitInfo.normal,
|
|
301
296
|
) ).toVar();
|
|
302
297
|
|
|
@@ -323,7 +318,6 @@ export const TraceDebugMode = Fn( ( [
|
|
|
323
318
|
// Get primary surface material
|
|
324
319
|
const material = RayTracingMaterial.wrap( getMaterial( hitInfo.materialIndex, materialBuffer ) ).toVar();
|
|
325
320
|
const matSamples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
326
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
327
321
|
material, hitInfo.uv, hitInfo.normal,
|
|
328
322
|
) ).toVar();
|
|
329
323
|
|
|
@@ -370,7 +364,6 @@ export const TraceDebugMode = Fn( ( [
|
|
|
370
364
|
// Bounce hit surface B — evaluate its contribution
|
|
371
365
|
const materialB = RayTracingMaterial.wrap( getMaterial( bounceHit.materialIndex, materialBuffer ) ).toVar();
|
|
372
366
|
const matSamplesB = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
373
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
374
367
|
materialB, bounceHit.uv, bounceHit.normal,
|
|
375
368
|
) ).toVar();
|
|
376
369
|
|
package/src/TSL/Displacement.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Fn, float, vec2, int, If, Loop, abs, normalize, dot, max
|
|
1
|
+
import { Fn, float, vec2, int, If, Loop, abs, normalize, dot, max } from 'three/tsl';
|
|
2
2
|
|
|
3
3
|
import { struct } from './patches.js';
|
|
4
4
|
import { getDatafromStorageBuffer } from './Common.js';
|
|
5
|
-
import { sampleDisplacementMap } from './TextureSampling.js';
|
|
5
|
+
import { sampleDisplacementMap, bucketTexelSize, getLinearBucketTextures } from './TextureSampling.js';
|
|
6
6
|
|
|
7
7
|
// Ray-displacement intersection configuration
|
|
8
8
|
const MAX_MARCH_STEPS = 32;
|
|
@@ -32,7 +32,7 @@ export const DisplacementResult = struct( {
|
|
|
32
32
|
* We find dt where h_ray(dt) = h_surf(dt).
|
|
33
33
|
*/
|
|
34
34
|
export const refineDisplacedIntersection = Fn( ( [
|
|
35
|
-
ray, hitInfo, triangleBuffer,
|
|
35
|
+
ray, hitInfo, triangleBuffer, material, bounceIndex
|
|
36
36
|
] ) => {
|
|
37
37
|
|
|
38
38
|
const resultHitPoint = hitInfo.hitPoint.toVar();
|
|
@@ -135,7 +135,7 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
135
135
|
|
|
136
136
|
// Displaced surface height
|
|
137
137
|
const heightSample = sampleDisplacementMap(
|
|
138
|
-
|
|
138
|
+
material.displacementMapIndex, marchUV, material.displacementTransform,
|
|
139
139
|
);
|
|
140
140
|
const surfaceHeight = heightSample.sub( 0.5 ).mul( scale );
|
|
141
141
|
|
|
@@ -167,7 +167,7 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
167
167
|
const midUV = hitInfo.uv.add( dUV_dt.mul( midDt ) );
|
|
168
168
|
const midRayHeight = midDt.mul( dh_ray_dt );
|
|
169
169
|
const midSample = sampleDisplacementMap(
|
|
170
|
-
|
|
170
|
+
material.displacementMapIndex, midUV, material.displacementTransform,
|
|
171
171
|
);
|
|
172
172
|
const midSurfHeight = midSample.sub( 0.5 ).mul( scale );
|
|
173
173
|
|
|
@@ -189,19 +189,19 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
189
189
|
const finalPoint = hitInfo.hitPoint.add( rayDir.mul( finalDt ) );
|
|
190
190
|
|
|
191
191
|
const finalHeight = sampleDisplacementMap(
|
|
192
|
-
|
|
192
|
+
material.displacementMapIndex, finalUV, material.displacementTransform,
|
|
193
193
|
);
|
|
194
194
|
const displacedHeight = finalHeight.sub( 0.5 ).mul( scale );
|
|
195
195
|
|
|
196
196
|
// Compute displaced normal from height-field gradients using UV tangent vectors.
|
|
197
|
-
// Finite-difference step = one texel of the
|
|
198
|
-
const texel =
|
|
197
|
+
// Finite-difference step = one texel of the SELECTED bucket array's real dimensions.
|
|
198
|
+
const texel = bucketTexelSize( getLinearBucketTextures(), material.displacementMapIndex ).toVar();
|
|
199
199
|
const hC = finalHeight;
|
|
200
200
|
const hU = sampleDisplacementMap(
|
|
201
|
-
|
|
201
|
+
material.displacementMapIndex, finalUV.add( vec2( texel.x, 0.0 ) ), material.displacementTransform,
|
|
202
202
|
);
|
|
203
203
|
const hV = sampleDisplacementMap(
|
|
204
|
-
|
|
204
|
+
material.displacementMapIndex, finalUV.add( vec2( 0.0, texel.y ) ), material.displacementTransform,
|
|
205
205
|
);
|
|
206
206
|
|
|
207
207
|
// Perturb normal using actual tangent/bitangent from UV parameterization
|
|
@@ -58,7 +58,11 @@ export function buildGenerateKernel( params ) {
|
|
|
58
58
|
const baseSeed = getDecorrelatedSeed( { pixelCoord, rayIndex: int( 0 ), frame } ).toVar();
|
|
59
59
|
const seed = pcgHash( { state: baseSeed } ).toVar();
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
// Sample index 1 (not 0) so the AA sub-pixel jitter draws a DIFFERENT STBN cell
|
|
62
|
+
// than the first-bounce BSDF sample (ShadeKernel uses sampleIndex 0). Every bounce
|
|
63
|
+
// samples at index 0, so index 1 is collision-free — this decorrelates the sub-pixel
|
|
64
|
+
// position from the first scatter direction (they were reading the identical cell).
|
|
65
|
+
const stratifiedJitter = getStratifiedSample( pixelCoord, int( 1 ), int( 1 ), seed, resolution, frame ).toVar();
|
|
62
66
|
|
|
63
67
|
const jitterScale = vec2( 2.0 ).div( resolution );
|
|
64
68
|
const jitter = stratifiedJitter.sub( 0.5 ).mul( jitterScale );
|
package/src/TSL/LightsDirect.js
CHANGED
|
@@ -22,28 +22,27 @@ import {
|
|
|
22
22
|
clamp,
|
|
23
23
|
smoothstep,
|
|
24
24
|
select,
|
|
25
|
-
texture,
|
|
26
25
|
} from 'three/tsl';
|
|
27
26
|
|
|
28
27
|
import { Ray, ShadowMaterial, HitInfo } from './Struct.js';
|
|
29
28
|
import { REC709_LUMINANCE_COEFFICIENTS, getShadowMaterial, getDatafromStorageBuffer } from './Common.js';
|
|
30
29
|
import { fresnelSchlickFloat, iorToFresnel0 } from './Fresnel.js';
|
|
31
30
|
import { calculateBeerLawAbsorption } from './MaterialTransmission.js';
|
|
32
|
-
import { getTransformedUV } from './TextureSampling.js';
|
|
31
|
+
import { getTransformedUV, sampleBucket } from './TextureSampling.js';
|
|
33
32
|
|
|
34
33
|
// Module-level state for alpha-cutout shadow testing.
|
|
35
|
-
// Set by
|
|
34
|
+
// Set by PathTracer before shade-graph construction.
|
|
36
35
|
let _shadowAlbedoMaps = null;
|
|
37
36
|
let _enableAlphaShadows = null;
|
|
38
37
|
|
|
39
38
|
/**
|
|
40
|
-
* Set the
|
|
41
|
-
* Must be called before the
|
|
42
|
-
* @param {
|
|
39
|
+
* Set the sRGB bucket texture node array for alpha-aware shadow rays (albedo alpha).
|
|
40
|
+
* Must be called before the shade graph is constructed.
|
|
41
|
+
* @param {Array} buckets - K sRGB bucket texture nodes
|
|
43
42
|
*/
|
|
44
|
-
export function setShadowAlbedoMaps(
|
|
43
|
+
export function setShadowAlbedoMaps( buckets ) {
|
|
45
44
|
|
|
46
|
-
_shadowAlbedoMaps =
|
|
45
|
+
_shadowAlbedoMaps = buckets;
|
|
47
46
|
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -137,7 +136,7 @@ export const traceShadowRay = Fn( ( [
|
|
|
137
136
|
const uvData2 = getDatafromStorageBuffer( triangleBuffer, shadowHit.triangleIndex, int( 7 ), TRI_STRIDE );
|
|
138
137
|
const hitUV = uvData1.xy.mul( baryW ).add( uvData1.zw.mul( baryU ) ).add( uvData2.xy.mul( baryV ) );
|
|
139
138
|
const albedoUV = getTransformedUV( { uv: hitUV, transform: shadowMaterial.albedoTransform } );
|
|
140
|
-
texAlpha.assign(
|
|
139
|
+
texAlpha.assign( sampleBucket( _shadowAlbedoMaps, shadowMaterial.albedoMapIndex, albedoUV ).a );
|
|
141
140
|
|
|
142
141
|
} );
|
|
143
142
|
|
|
@@ -290,6 +290,9 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
290
290
|
const sampleDir = vec3( 0.0 ).toVar();
|
|
291
291
|
const samplePdf = float( 0.0 ).toVar();
|
|
292
292
|
const sampleBrdfValue = vec3( 0.0 ).toVar();
|
|
293
|
+
// Transmission lobe uses this weight instead of a reflection-BRDF eval, which is
|
|
294
|
+
// invalid for the below-surface refraction direction.
|
|
295
|
+
const transColorWeight = vec3( 1.0 ).toVar();
|
|
293
296
|
|
|
294
297
|
// Execute selected strategy (chained If/ElseIf/Else for exclusive branches)
|
|
295
298
|
// Environment removed — handled via deterministic NEE in direct lighting
|
|
@@ -312,13 +315,16 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
312
315
|
|
|
313
316
|
// Strategy 3: Transmission
|
|
314
317
|
const entering = dot( V, N ).greaterThan( 0.0 );
|
|
315
|
-
// pathWavelength=0 — MIS evaluation reads only direction/PDF, no spectral tint
|
|
316
318
|
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission(
|
|
317
319
|
V, N, material.ior, material.roughness, entering, material.dispersion, sampleRand, rngState, float( 0.0 )
|
|
318
320
|
).toVar() );
|
|
319
321
|
sampleDir.assign( mtResult.direction );
|
|
320
322
|
samplePdf.assign( mtResult.pdf );
|
|
321
|
-
|
|
323
|
+
// evaluateMaterialResponse is reflection-only and returns a non-physical value for
|
|
324
|
+
// the below-surface refraction direction (its dots are floored to 0.001). Carry the
|
|
325
|
+
// sampler's spectral tint and apply an energy-consistent transmission throughput
|
|
326
|
+
// below (mirrors handleTransmission's material.color × colorWeight convention).
|
|
327
|
+
transColorWeight.assign( mtResult.colorWeight );
|
|
322
328
|
|
|
323
329
|
} ).Else( () => {
|
|
324
330
|
|
|
@@ -329,10 +335,7 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
329
335
|
|
|
330
336
|
} );
|
|
331
337
|
|
|
332
|
-
|
|
333
|
-
const rawNoL = dot( N, sampleDir ).toVar();
|
|
334
|
-
const NoL = max( rawNoL, 0.0 ).toVar();
|
|
335
|
-
const absNoL = abs( rawNoL );
|
|
338
|
+
const NoL = max( dot( N, sampleDir ), 0.0 ).toVar();
|
|
336
339
|
|
|
337
340
|
// Calculate combined PDF for MIS (material strategies only)
|
|
338
341
|
const combinedPdf = float( 0.0 ).toVar();
|
|
@@ -380,11 +383,14 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
380
383
|
// MIS weight calculation
|
|
381
384
|
const misWeight = samplePdf.div( combinedPdf ).toVar();
|
|
382
385
|
|
|
383
|
-
//
|
|
384
|
-
|
|
386
|
+
// Reflection lobes: f·NoL·mis/pdf. Transmission lobe: the sampler's energy-consistent
|
|
387
|
+
// weight (material tint × dispersion colorWeight × mis) — the reflection BRDF value is
|
|
388
|
+
// invalid below the surface, so it is not used for transmission.
|
|
389
|
+
const reflThroughput = sampleBrdfValue.mul( NoL ).mul( misWeight ).div( samplePdf );
|
|
390
|
+
const transThroughput = material.color.xyz.mul( transColorWeight ).mul( misWeight );
|
|
385
391
|
|
|
386
392
|
r_direction.assign( sampleDir );
|
|
387
|
-
r_throughput.assign(
|
|
393
|
+
r_throughput.assign( select( selectedStrategy.equal( int( 3 ) ), transThroughput, reflThroughput ) );
|
|
388
394
|
r_misWeight.assign( misWeight );
|
|
389
395
|
r_pdf.assign( samplePdf );
|
|
390
396
|
r_combinedPdf.assign( combinedPdf );
|
|
@@ -360,7 +360,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
360
360
|
If( totalLights.greaterThan( int( 0 ) ), () => {
|
|
361
361
|
|
|
362
362
|
const totalWeight = float( 0.0 ).toVar();
|
|
363
|
-
const lightIndex = int( 0 ).toVar();
|
|
364
363
|
|
|
365
364
|
// Reservoir state: winning light's type/index/importance.
|
|
366
365
|
const selectedType = int( - 1 ).toVar(); // 0=dir, 1=area, 2=point, 3=spot
|
|
@@ -375,7 +374,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
375
374
|
|
|
376
375
|
Loop( { start: int( 0 ), end: numDirectionalLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
377
376
|
|
|
378
|
-
If(
|
|
377
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
379
378
|
|
|
380
379
|
const light = DirectionalLight.wrap( getDirectionalLight( directionalLightsBuffer, i ) );
|
|
381
380
|
const importance = calculateDirectionalLightImportance( light, normal, material, bounceIndex ).toVar();
|
|
@@ -389,7 +388,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
389
388
|
selectedImportance.assign( importance );
|
|
390
389
|
|
|
391
390
|
} );
|
|
392
|
-
lightIndex.addAssign( 1 );
|
|
393
391
|
|
|
394
392
|
} );
|
|
395
393
|
|
|
@@ -401,7 +399,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
401
399
|
|
|
402
400
|
Loop( { start: int( 0 ), end: numAreaLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
403
401
|
|
|
404
|
-
If(
|
|
402
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
405
403
|
|
|
406
404
|
const light = AreaLight.wrap( getAreaLight( areaLightsBuffer, i ) );
|
|
407
405
|
const importance = select( light.intensity.greaterThan( 0.0 ), estimateLightImportance( light, rayOrigin, normal, material ), float( 0.0 ) ).toVar();
|
|
@@ -415,7 +413,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
415
413
|
selectedImportance.assign( importance );
|
|
416
414
|
|
|
417
415
|
} );
|
|
418
|
-
lightIndex.addAssign( 1 );
|
|
419
416
|
|
|
420
417
|
} );
|
|
421
418
|
|
|
@@ -427,7 +424,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
427
424
|
|
|
428
425
|
Loop( { start: int( 0 ), end: numPointLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
429
426
|
|
|
430
|
-
If(
|
|
427
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
431
428
|
|
|
432
429
|
const light = PointLight.wrap( getPointLight( pointLightsBuffer, i ) );
|
|
433
430
|
const importance = calculatePointLightImportance( light, rayOrigin, normal, material ).toVar();
|
|
@@ -441,7 +438,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
441
438
|
selectedImportance.assign( importance );
|
|
442
439
|
|
|
443
440
|
} );
|
|
444
|
-
lightIndex.addAssign( 1 );
|
|
445
441
|
|
|
446
442
|
} );
|
|
447
443
|
|
|
@@ -453,7 +449,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
453
449
|
|
|
454
450
|
Loop( { start: int( 0 ), end: numSpotLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
455
451
|
|
|
456
|
-
If(
|
|
452
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
457
453
|
|
|
458
454
|
const light = SpotLight.wrap( getSpotLight( spotLightsBuffer, i ) );
|
|
459
455
|
const importance = calculateSpotLightImportance( light, rayOrigin, normal, material ).toVar();
|
|
@@ -467,7 +463,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
467
463
|
selectedImportance.assign( importance );
|
|
468
464
|
|
|
469
465
|
} );
|
|
470
|
-
lightIndex.addAssign( 1 );
|
|
471
466
|
|
|
472
467
|
} );
|
|
473
468
|
|
|
@@ -762,7 +757,7 @@ export const calculateMaterialPDF = Fn( ( [ viewDir, lightDir, normal, material
|
|
|
762
757
|
} );
|
|
763
758
|
|
|
764
759
|
// Total light-selection weight at a shading point, replicating the NEE reservoir's
|
|
765
|
-
// importance accumulation (same per-type importance fns, same 16-light cap + order).
|
|
760
|
+
// importance accumulation (same per-type importance fns, same per-type 16-light cap + order).
|
|
766
761
|
// Used to reconstruct the exact NEE selection pdf for MIS on the BSDF-hit path —
|
|
767
762
|
// the analogue of Cycles' light_tree_pdf re-walk. Without it, the BSDF-hit MIS
|
|
768
763
|
// partner assumes uniform 1/N selection, which disagrees with the importance-
|
|
@@ -776,17 +771,15 @@ export const computeTotalLightImportance = Fn( ( [
|
|
|
776
771
|
] ) => {
|
|
777
772
|
|
|
778
773
|
const totalWeight = float( 0.0 ).toVar();
|
|
779
|
-
const lightIndex = int( 0 ).toVar();
|
|
780
774
|
|
|
781
775
|
If( numDirectionalLights.greaterThan( int( 0 ) ), () => {
|
|
782
776
|
|
|
783
777
|
Loop( { start: int( 0 ), end: numDirectionalLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
784
778
|
|
|
785
|
-
If(
|
|
779
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
786
780
|
|
|
787
781
|
const light = DirectionalLight.wrap( getDirectionalLight( directionalLightsBuffer, i ) );
|
|
788
782
|
totalWeight.addAssign( calculateDirectionalLightImportance( light, normal, material, bounceIndex ) );
|
|
789
|
-
lightIndex.addAssign( 1 );
|
|
790
783
|
|
|
791
784
|
} );
|
|
792
785
|
|
|
@@ -798,11 +791,10 @@ export const computeTotalLightImportance = Fn( ( [
|
|
|
798
791
|
|
|
799
792
|
Loop( { start: int( 0 ), end: numAreaLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
800
793
|
|
|
801
|
-
If(
|
|
794
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
802
795
|
|
|
803
796
|
const light = AreaLight.wrap( getAreaLight( areaLightsBuffer, i ) );
|
|
804
797
|
totalWeight.addAssign( select( light.intensity.greaterThan( 0.0 ), estimateLightImportance( light, rayOrigin, normal, material ), float( 0.0 ) ) );
|
|
805
|
-
lightIndex.addAssign( 1 );
|
|
806
798
|
|
|
807
799
|
} );
|
|
808
800
|
|
|
@@ -814,11 +806,10 @@ export const computeTotalLightImportance = Fn( ( [
|
|
|
814
806
|
|
|
815
807
|
Loop( { start: int( 0 ), end: numPointLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
816
808
|
|
|
817
|
-
If(
|
|
809
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
818
810
|
|
|
819
811
|
const light = PointLight.wrap( getPointLight( pointLightsBuffer, i ) );
|
|
820
812
|
totalWeight.addAssign( calculatePointLightImportance( light, rayOrigin, normal, material ) );
|
|
821
|
-
lightIndex.addAssign( 1 );
|
|
822
813
|
|
|
823
814
|
} );
|
|
824
815
|
|
|
@@ -830,11 +821,10 @@ export const computeTotalLightImportance = Fn( ( [
|
|
|
830
821
|
|
|
831
822
|
Loop( { start: int( 0 ), end: numSpotLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
832
823
|
|
|
833
|
-
If(
|
|
824
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
834
825
|
|
|
835
826
|
const light = SpotLight.wrap( getSpotLight( spotLightsBuffer, i ) );
|
|
836
827
|
totalWeight.addAssign( calculateSpotLightImportance( light, rayOrigin, normal, material ) );
|
|
837
|
-
lightIndex.addAssign( 1 );
|
|
838
828
|
|
|
839
829
|
} );
|
|
840
830
|
|
package/src/TSL/Random.js
CHANGED
|
@@ -187,7 +187,17 @@ const computeSTBNAtlasCoord = ( pixelCoords, sampleIndex, dimensionIndex, frame
|
|
|
187
187
|
export const sampleSTBN2D = ( pixelCoords, sampleIndex, dimensionPairIndex, frame ) => {
|
|
188
188
|
|
|
189
189
|
const coord = computeSTBNAtlasCoord( pixelCoords, sampleIndex, dimensionPairIndex, frame );
|
|
190
|
-
|
|
190
|
+
const raw = stbnVec2TextureNode.load( coord ).xy;
|
|
191
|
+
|
|
192
|
+
// The atlas has only 64 temporal slices, so frame N and N+64 read the same slice: the
|
|
193
|
+
// sample repeats and accumulation stops improving past 64 frames. Decorrelate across
|
|
194
|
+
// 64-frame cycles with a Cranley-Patterson rotation (toroidal shift) by an R2 offset
|
|
195
|
+
// keyed on the cycle index (frame >> 6). The offset is uniform per cycle, preserving
|
|
196
|
+
// spatial and within-window temporal blue noise; cycle 0's offset is 0, so frames
|
|
197
|
+
// 0-63 stay bit-identical. A toroidal shift of uniform samples stays uniform (unbiased).
|
|
198
|
+
const cycle = float( uint( frame ).shiftRight( uint( 6 ) ) );
|
|
199
|
+
const rotation = fract( vec2( R2_A1, R2_A2 ).mul( cycle ) );
|
|
200
|
+
return fract( raw.add( rotation ) );
|
|
191
201
|
|
|
192
202
|
};
|
|
193
203
|
|