rayzee 7.6.0 → 7.8.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/rayzee.es.js +1971 -1971
- 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/Processor/SceneProcessor.js +165 -112
- package/src/Processor/ShaderBuilder.js +6 -27
- package/src/Stages/NormalDepth.js +16 -27
- package/src/Stages/PathTracer.js +16 -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/FinalWriteKernel.js +5 -2
- package/src/TSL/LightsDirect.js +8 -9
- package/src/TSL/ShadeKernel.js +1 -6
- package/src/TSL/TextureSampling.js +155 -34
- package/src/managers/MaterialDataManager.js +71 -39
package/src/Stages/PathTracer.js
CHANGED
|
@@ -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
|
} );
|
|
@@ -200,7 +202,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
200
202
|
if ( this.shaderBuilder.prevColorTexNode ) {
|
|
201
203
|
|
|
202
204
|
this.shaderBuilder.prevColorTexNode.value = readTextures.color;
|
|
203
|
-
this.shaderBuilder.prevNormalDepthTexNode.value = readTextures.normalDepth;
|
|
204
205
|
this.shaderBuilder.prevAlbedoTexNode.value = readTextures.albedo;
|
|
205
206
|
|
|
206
207
|
}
|
|
@@ -450,13 +451,8 @@ export class PathTracer extends PathTracerStage {
|
|
|
450
451
|
|
|
451
452
|
const mat = this.materialData;
|
|
452
453
|
if ( ! mat ) return;
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
if ( mat.bumpMaps && t.bumpMaps ) t.bumpMaps.value = mat.bumpMaps;
|
|
456
|
-
if ( mat.metalnessMaps && t.metalnessMaps ) t.metalnessMaps.value = mat.metalnessMaps;
|
|
457
|
-
if ( mat.roughnessMaps && t.roughnessMaps ) t.roughnessMaps.value = mat.roughnessMaps;
|
|
458
|
-
if ( mat.emissiveMaps && t.emissiveMaps ) t.emissiveMaps.value = mat.emissiveMaps;
|
|
459
|
-
if ( mat.displacementMaps && t.displacementMaps ) t.displacementMaps.value = mat.displacementMaps;
|
|
454
|
+
refreshBucketTextureNodes( t.srgbBuckets, mat.srgbBuckets );
|
|
455
|
+
refreshBucketTextureNodes( t.linearBuckets, mat.linearBuckets );
|
|
460
456
|
|
|
461
457
|
}
|
|
462
458
|
|
|
@@ -570,7 +566,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
570
566
|
this._wfMaxRayCount.value = maxRays;
|
|
571
567
|
|
|
572
568
|
const prevColor = this.shaderBuilder.prevColorTexNode;
|
|
573
|
-
const prevND = this.shaderBuilder.prevNormalDepthTexNode;
|
|
574
569
|
const prevAlbedo = this.shaderBuilder.prevAlbedoTexNode;
|
|
575
570
|
const writeTex = this.storageTextures.getWriteTextures();
|
|
576
571
|
|
|
@@ -663,26 +658,21 @@ export class PathTracer extends PathTracerStage {
|
|
|
663
658
|
// Independent texture nodes (never compiled elsewhere) avoid Three.js TextureNode caching across pipelines; refreshed via _refreshWfTextureNodes.
|
|
664
659
|
const _mat = this.materialData;
|
|
665
660
|
const _env = this.environment;
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
const
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
661
|
+
// Consolidated size-bucket nodes (K sRGB + K linear). Empty buckets get placeholders so
|
|
662
|
+
// every runtime branch references a valid node. Published to the sampling module before
|
|
663
|
+
// the Shade/Debug graphs are built so they bake in these (per-pipeline) nodes.
|
|
664
|
+
const freshSrgbBuckets = buildBucketTextureNodes( _mat.srgbBuckets );
|
|
665
|
+
const freshLinearBuckets = buildBucketTextureNodes( _mat.linearBuckets );
|
|
666
|
+
setMaterialBucketTextures( freshSrgbBuckets, freshLinearBuckets );
|
|
667
|
+
// Alpha-cutout shadow rays sample albedo (sRGB pool) — emitted into the shade graph now.
|
|
668
|
+
setShadowAlbedoMaps( freshSrgbBuckets );
|
|
674
669
|
const freshEnvTex = _env.environmentTexture ? texture( _env.environmentTexture ) : texNodes.envTex;
|
|
675
670
|
|
|
676
671
|
this._wfTexNodes = {
|
|
677
672
|
envTex: freshEnvTex,
|
|
678
673
|
envCDFTex: freshEnvCDF,
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
bumpMaps: freshBumpMaps,
|
|
682
|
-
metalnessMaps: freshMetalnessMaps,
|
|
683
|
-
roughnessMaps: freshRoughnessMaps,
|
|
684
|
-
emissiveMaps: freshEmissiveMaps,
|
|
685
|
-
displacementMaps: freshDisplacementMaps,
|
|
674
|
+
srgbBuckets: freshSrgbBuckets,
|
|
675
|
+
linearBuckets: freshLinearBuckets,
|
|
686
676
|
};
|
|
687
677
|
|
|
688
678
|
// Material-coherence sort gate (experiment): only worthwhile above a few materials.
|
|
@@ -763,13 +753,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
763
753
|
hitBufferRO: pb.hitBuffer.ro,
|
|
764
754
|
counters,
|
|
765
755
|
activeIndicesRO: this._sortMaterials ? qm.getSortedRO() : qm.getActiveReadRO(),
|
|
766
|
-
albedoMaps: freshAlbedoMaps,
|
|
767
|
-
normalMaps: freshNormalMaps,
|
|
768
|
-
bumpMaps: freshBumpMaps,
|
|
769
|
-
metalnessMaps: freshMetalnessMaps,
|
|
770
|
-
roughnessMaps: freshRoughnessMaps,
|
|
771
|
-
emissiveMaps: freshEmissiveMaps,
|
|
772
|
-
displacementMaps: freshDisplacementMaps,
|
|
773
756
|
envTexture: freshEnvTex,
|
|
774
757
|
environmentIntensity: this.environmentIntensity,
|
|
775
758
|
envMatrix: this.environmentMatrix,
|
|
@@ -885,7 +868,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
885
868
|
cameraIsMoving: this.cameraIsMoving,
|
|
886
869
|
transparentBackground: this.transparentBackground,
|
|
887
870
|
prevAccumTexture: prevColor,
|
|
888
|
-
prevNormalDepthTexture: prevND,
|
|
889
871
|
prevAlbedoTexture: prevAlbedo,
|
|
890
872
|
renderWidth: this._wfRenderWidth,
|
|
891
873
|
renderHeight: this._wfRenderHeight,
|
|
@@ -929,12 +911,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
929
911
|
enableEnvironmentLight: this.enableEnvironment,
|
|
930
912
|
visMode: this.visMode,
|
|
931
913
|
debugVisScale: this.debugVisScale,
|
|
932
|
-
albedoMaps: freshAlbedoMaps,
|
|
933
|
-
normalMaps: freshNormalMaps,
|
|
934
|
-
bumpMaps: freshBumpMaps,
|
|
935
|
-
metalnessMaps: freshMetalnessMaps,
|
|
936
|
-
roughnessMaps: freshRoughnessMaps,
|
|
937
|
-
emissiveMaps: freshEmissiveMaps,
|
|
938
914
|
frame: this.frame,
|
|
939
915
|
} );
|
|
940
916
|
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
|
|
@@ -32,7 +32,7 @@ export function buildFinalWriteKernel( params ) {
|
|
|
32
32
|
resolution, frame,
|
|
33
33
|
enableAccumulation, hasPreviousAccumulated, accumulationAlpha, cameraIsMoving,
|
|
34
34
|
transparentBackground,
|
|
35
|
-
prevAccumTexture,
|
|
35
|
+
prevAccumTexture, prevAlbedoTexture,
|
|
36
36
|
renderWidth, renderHeight,
|
|
37
37
|
visMode,
|
|
38
38
|
// Aux MRT (normalDepth + albedo) feeds only the denoiser/OIDN. Gated by a live uniform (1 = denoiser
|
|
@@ -80,7 +80,10 @@ export function buildFinalWriteKernel( params ) {
|
|
|
80
80
|
finalColor.assign( mix( prevAccumSample.xyz, sampleColor.xyz, accumulationAlpha ) );
|
|
81
81
|
If( auxOn, () => {
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
// Albedo averages cleanly (it's a colour). The NORMAL must NOT: averaging jittered
|
|
84
|
+
// unit normals collapses toward the flat geometric mean (worse at distance), which made
|
|
85
|
+
// OIDN smooth bump detail away. Keep this frame's point-sampled normal — it varies with
|
|
86
|
+
// the bump, which is exactly what OIDN's edge-stop needs to preserve it.
|
|
84
87
|
finalAlbedo.assign( mix( texture( prevAlbedoTexture, prevUV, 0 ).xyz, finalAlbedo, accumulationAlpha ) );
|
|
85
88
|
|
|
86
89
|
} );
|
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
|
|
package/src/TSL/ShadeKernel.js
CHANGED
|
@@ -74,9 +74,6 @@ export function buildShadeKernel( params ) {
|
|
|
74
74
|
rayBufferRW, rngBufferRW, hitBufferRO, gBufferRW,
|
|
75
75
|
counters,
|
|
76
76
|
activeIndicesRO,
|
|
77
|
-
albedoMaps, normalMaps, bumpMaps,
|
|
78
|
-
metalnessMaps, roughnessMaps, emissiveMaps,
|
|
79
|
-
displacementMaps,
|
|
80
77
|
envTexture, environmentIntensity, envMatrix,
|
|
81
78
|
enableEnvironmentLight, useEnvMapIS,
|
|
82
79
|
groundProjectionEnabled, groundProjectionRadius, groundProjectionHeight, groundProjectionLevel,
|
|
@@ -533,7 +530,7 @@ export function buildShadeKernel( params ) {
|
|
|
533
530
|
boxTests: int( 0 ), triTests: int( 0 ),
|
|
534
531
|
} );
|
|
535
532
|
const dispResult = DisplacementResult.wrap( refineDisplacedIntersection(
|
|
536
|
-
dispRay, dispHit, triangleBuffer,
|
|
533
|
+
dispRay, dispHit, triangleBuffer, material, bounceIndex,
|
|
537
534
|
) ).toVar();
|
|
538
535
|
samplingUV.assign( dispResult.uv );
|
|
539
536
|
displacedNormal.assign( dispResult.normal );
|
|
@@ -543,8 +540,6 @@ export function buildShadeKernel( params ) {
|
|
|
543
540
|
);
|
|
544
541
|
|
|
545
542
|
const matSamples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
546
|
-
albedoMaps, normalMaps, bumpMaps,
|
|
547
|
-
metalnessMaps, roughnessMaps, emissiveMaps,
|
|
548
543
|
material, samplingUV, N,
|
|
549
544
|
) ).toVar();
|
|
550
545
|
|
|
@@ -1,9 +1,129 @@
|
|
|
1
1
|
import { Fn, wgslFn, float, vec2, vec3, vec4, int, If, normalize, cross, abs, mix, clamp, texture, textureSize } from 'three/tsl';
|
|
2
|
+
import { DataArrayTexture, LinearFilter } from 'three';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
UVCache,
|
|
5
6
|
MaterialSamples,
|
|
6
7
|
} from './Struct.js';
|
|
8
|
+
import { TEXTURE_CONSTANTS } from '../EngineDefaults.js';
|
|
9
|
+
|
|
10
|
+
// ================================================================================
|
|
11
|
+
// CONSOLIDATED SIZE-BUCKETED MATERIAL TEXTURES
|
|
12
|
+
// ================================================================================
|
|
13
|
+
// Material maps are packed into two colorSpace pools (sRGB: albedo+emissive; linear:
|
|
14
|
+
// normal/bump/roughness/metalness/displacement), each split into MATERIAL_BUCKET_COUNT
|
|
15
|
+
// longest-edge size buckets so a small map no longer pays a large neighbour's footprint.
|
|
16
|
+
// A map's stored index encodes (bucket, layer) as bucket * BUCKET_LAYER_STRIDE + layer.
|
|
17
|
+
//
|
|
18
|
+
// The bucket texture nodes live at module level (same pattern as gobo/IES/shadowAlbedo):
|
|
19
|
+
// each stage sets them via setMaterialBucketTextures() right before building its graph,
|
|
20
|
+
// so each pipeline bakes in its own fresh nodes (avoiding cross-pipeline TextureNode
|
|
21
|
+
// caching, just like the per-type nodes did before).
|
|
22
|
+
|
|
23
|
+
const _STRIDE = TEXTURE_CONSTANTS.BUCKET_LAYER_STRIDE;
|
|
24
|
+
|
|
25
|
+
// Array<MATERIAL_BUCKET_COUNT> of texture nodes (never null — empty buckets get placeholders).
|
|
26
|
+
let _srgbBuckets = null;
|
|
27
|
+
let _linearBuckets = null;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Set the bucket texture node arrays read by the sampling functions. Call before building
|
|
31
|
+
* any graph that samples material textures (Shade / NormalDepth / Debug kernels).
|
|
32
|
+
* @param {Array} srgb sRGB pool nodes (albedo + emissive)
|
|
33
|
+
* @param {Array} linear linear pool nodes (normal/bump/roughness/metalness/displacement)
|
|
34
|
+
*/
|
|
35
|
+
export function setMaterialBucketTextures( srgb, linear ) {
|
|
36
|
+
|
|
37
|
+
_srgbBuckets = srgb;
|
|
38
|
+
_linearBuckets = linear;
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Run `fn(node, layer)` inside a runtime branch that selects the bucket node a packed
|
|
43
|
+
// index points to. Emits one If/ElseIf arm per bucket; only the matching arm executes.
|
|
44
|
+
// Caller must guard packedIndex >= 0.
|
|
45
|
+
const withBucket = ( buckets, packedIndex, fn ) => {
|
|
46
|
+
|
|
47
|
+
const bucket = packedIndex.div( int( _STRIDE ) ).toVar();
|
|
48
|
+
const layer = packedIndex.sub( bucket.mul( int( _STRIDE ) ) ).toVar();
|
|
49
|
+
let chain = If( bucket.equal( int( 0 ) ), () => fn( buckets[ 0 ], layer ) );
|
|
50
|
+
for ( let i = 1; i < buckets.length; i ++ ) {
|
|
51
|
+
|
|
52
|
+
chain = chain.ElseIf( bucket.equal( int( i ) ), () => fn( buckets[ i ], layer ) );
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return chain;
|
|
57
|
+
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Sample the bucket a packed index points to. Caller must guard packedIndex >= 0.
|
|
61
|
+
export const sampleBucket = ( buckets, packedIndex, uv ) => {
|
|
62
|
+
|
|
63
|
+
const result = vec4( 0.0 ).toVar();
|
|
64
|
+
withBucket( buckets, packedIndex, ( node, layer ) => result.assign( texture( node, uv ).depth( layer ) ) );
|
|
65
|
+
return result;
|
|
66
|
+
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Per-axis texel size (1/dims) of the bucket a packed displacement/bump index points to,
|
|
70
|
+
// for finite-difference derivative steps. Caller must guard packedIndex >= 0.
|
|
71
|
+
export const bucketTexelSize = ( buckets, packedIndex ) => {
|
|
72
|
+
|
|
73
|
+
const ts = vec2( 1.0 ).toVar();
|
|
74
|
+
withBucket( buckets, packedIndex, node => ts.assign( vec2( 1.0 ).div( vec2( textureSize( node ) ) ) ) );
|
|
75
|
+
return ts;
|
|
76
|
+
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// The linear-pool node set (displacement lives here) — for Displacement.js texel sizing.
|
|
80
|
+
export function getLinearBucketTextures() {
|
|
81
|
+
|
|
82
|
+
return _linearBuckets;
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 1×1 white placeholder array so empty-bucket branches always reference a valid node.
|
|
87
|
+
export function makeBucketPlaceholder() {
|
|
88
|
+
|
|
89
|
+
const t = new DataArrayTexture( new Uint8Array( [ 255, 255, 255, 255 ] ), 1, 1, 1 );
|
|
90
|
+
t.minFilter = LinearFilter;
|
|
91
|
+
t.magFilter = LinearFilter;
|
|
92
|
+
t.generateMipmaps = false;
|
|
93
|
+
t.needsUpdate = true;
|
|
94
|
+
return t;
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Build MATERIAL_BUCKET_COUNT texture nodes from a bucket array (DataArrayTexture | null per
|
|
99
|
+
// bucket). Null buckets get a fresh placeholder. Each caller builds its OWN nodes so each
|
|
100
|
+
// pipeline bakes in independent TextureNodes (no cross-pipeline caching).
|
|
101
|
+
export function buildBucketTextureNodes( bucketArrays ) {
|
|
102
|
+
|
|
103
|
+
const K = TEXTURE_CONSTANTS.MATERIAL_BUCKET_COUNT;
|
|
104
|
+
const nodes = [];
|
|
105
|
+
for ( let i = 0; i < K; i ++ ) {
|
|
106
|
+
|
|
107
|
+
const arr = bucketArrays && bucketArrays[ i ];
|
|
108
|
+
nodes.push( texture( arr || makeBucketPlaceholder() ) );
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return nodes;
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Update in-place the .value of bucket nodes from a fresh bucket array (model change).
|
|
117
|
+
export function refreshBucketTextureNodes( nodes, bucketArrays ) {
|
|
118
|
+
|
|
119
|
+
if ( ! nodes || ! bucketArrays ) return;
|
|
120
|
+
for ( let i = 0; i < nodes.length; i ++ ) {
|
|
121
|
+
|
|
122
|
+
if ( bucketArrays[ i ] && nodes[ i ] ) nodes[ i ].value = bucketArrays[ i ];
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
}
|
|
7
127
|
|
|
8
128
|
// ================================================================================
|
|
9
129
|
// FAST UTILITY FUNCTIONS
|
|
@@ -201,14 +321,14 @@ export const computeUVCache = Fn( ( [ baseUV, material ] ) => {
|
|
|
201
321
|
// PROCESSING FUNCTIONS
|
|
202
322
|
// ================================================================================
|
|
203
323
|
|
|
204
|
-
export const processAlbedo = Fn( ( [
|
|
324
|
+
export const processAlbedo = Fn( ( [ material, uvCache ] ) => {
|
|
205
325
|
|
|
206
326
|
const result = material.color.toVar();
|
|
207
327
|
|
|
208
328
|
If( material.albedoMapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
209
329
|
|
|
210
|
-
const albedoSample =
|
|
211
|
-
// sRGB→linear handled by GPU hardware (
|
|
330
|
+
const albedoSample = sampleBucket( _srgbBuckets, material.albedoMapIndex, uvCache.albedoUV ).toVar();
|
|
331
|
+
// sRGB→linear handled by GPU hardware (sRGB bucket arrays carry SRGBColorSpace → rgba8unorm-srgb)
|
|
212
332
|
result.assign( vec4( material.color.rgb.mul( albedoSample.rgb ), material.color.a.mul( albedoSample.a ) ) );
|
|
213
333
|
|
|
214
334
|
} );
|
|
@@ -217,15 +337,15 @@ export const processAlbedo = Fn( ( [ albedoMaps, material, uvCache ] ) => {
|
|
|
217
337
|
|
|
218
338
|
} );
|
|
219
339
|
|
|
220
|
-
export const processMetalnessRoughness = Fn( ( [
|
|
340
|
+
export const processMetalnessRoughness = Fn( ( [ material, uvCache ] ) => {
|
|
221
341
|
|
|
222
342
|
const metalness = material.metalness.toVar();
|
|
223
343
|
const roughness = material.roughness.toVar();
|
|
224
344
|
|
|
225
345
|
If( material.metalnessMapIndex.greaterThanEqual( int( 0 ) ).and( material.metalnessMapIndex.equal( material.roughnessMapIndex ) ), () => {
|
|
226
346
|
|
|
227
|
-
// Same
|
|
228
|
-
const sample =
|
|
347
|
+
// Same packed index → same bucket layer (e.g. ORM) → sample once.
|
|
348
|
+
const sample = sampleBucket( _linearBuckets, material.metalnessMapIndex, uvCache.metalnessUV );
|
|
229
349
|
metalness.assign( material.metalness.mul( sample.b ) );
|
|
230
350
|
roughness.assign( material.roughness.mul( sample.g ) );
|
|
231
351
|
|
|
@@ -233,14 +353,14 @@ export const processMetalnessRoughness = Fn( ( [ metalnessMaps, roughnessMaps, m
|
|
|
233
353
|
|
|
234
354
|
If( material.metalnessMapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
235
355
|
|
|
236
|
-
const metSample =
|
|
356
|
+
const metSample = sampleBucket( _linearBuckets, material.metalnessMapIndex, uvCache.metalnessUV );
|
|
237
357
|
metalness.assign( material.metalness.mul( metSample.b ) );
|
|
238
358
|
|
|
239
359
|
} );
|
|
240
360
|
|
|
241
361
|
If( material.roughnessMapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
242
362
|
|
|
243
|
-
const rghSample =
|
|
363
|
+
const rghSample = sampleBucket( _linearBuckets, material.roughnessMapIndex, uvCache.roughnessUV );
|
|
244
364
|
roughness.assign( material.roughness.mul( rghSample.g ) );
|
|
245
365
|
|
|
246
366
|
} );
|
|
@@ -251,13 +371,13 @@ export const processMetalnessRoughness = Fn( ( [ metalnessMaps, roughnessMaps, m
|
|
|
251
371
|
|
|
252
372
|
} );
|
|
253
373
|
|
|
254
|
-
export const processNormal = Fn( ( [
|
|
374
|
+
export const processNormal = Fn( ( [ geometryNormal, material, uvCache ] ) => {
|
|
255
375
|
|
|
256
376
|
const result = geometryNormal.toVar();
|
|
257
377
|
|
|
258
378
|
If( material.normalMapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
259
379
|
|
|
260
|
-
const normalSample =
|
|
380
|
+
const normalSample = sampleBucket( _linearBuckets, material.normalMapIndex, uvCache.normalUV );
|
|
261
381
|
const normalMap = normalSample.xyz.mul( 2.0 ).sub( 1.0 ).toVar();
|
|
262
382
|
normalMap.x.mulAssign( material.normalScale.x );
|
|
263
383
|
normalMap.y.assign( normalMap.y.negate().mul( material.normalScale.x ) );
|
|
@@ -277,21 +397,25 @@ export const processNormal = Fn( ( [ normalMaps, geometryNormal, material, uvCac
|
|
|
277
397
|
|
|
278
398
|
} );
|
|
279
399
|
|
|
280
|
-
export const processBump = Fn( ( [
|
|
400
|
+
export const processBump = Fn( ( [ currentNormal, material, uvCache ] ) => {
|
|
281
401
|
|
|
282
402
|
const result = currentNormal.toVar();
|
|
283
403
|
|
|
284
404
|
If( material.bumpMapIndex.greaterThanEqual( int( 0 ) ).and( material.bumpScale.greaterThan( 0.0 ) ), () => {
|
|
285
405
|
|
|
286
|
-
//
|
|
287
|
-
|
|
406
|
+
// Taps + texel size come from the SELECTED bucket node (its real dimensions), so the
|
|
407
|
+
// finite-difference step is correct per bucket — done inside one bucket-branch.
|
|
408
|
+
const bumpNormal = vec3( 0.0, 0.0, 1.0 ).toVar();
|
|
409
|
+
withBucket( _linearBuckets, material.bumpMapIndex, ( node, layer ) => {
|
|
288
410
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
411
|
+
const texelSize = vec2( 1.0 ).div( vec2( textureSize( node ) ) ).toVar();
|
|
412
|
+
const h_c = texture( node, uvCache.bumpUV ).depth( layer ).r;
|
|
413
|
+
const h_u = texture( node, vec2( uvCache.bumpUV.x.add( texelSize.x ), uvCache.bumpUV.y ) ).depth( layer ).r;
|
|
414
|
+
const h_v = texture( node, vec2( uvCache.bumpUV.x, uvCache.bumpUV.y.add( texelSize.y ) ) ).depth( layer ).r;
|
|
415
|
+
const gradient = vec2( h_u.sub( h_c ), h_v.sub( h_c ) ).mul( material.bumpScale );
|
|
416
|
+
bumpNormal.assign( normalize( vec3( gradient.x.negate(), gradient.y.negate(), 1.0 ) ) );
|
|
292
417
|
|
|
293
|
-
|
|
294
|
-
const bumpNormal = normalize( vec3( gradient.x.negate(), gradient.y.negate(), 1.0 ) );
|
|
418
|
+
} );
|
|
295
419
|
|
|
296
420
|
// Build TBN matrix using current normal
|
|
297
421
|
const up = abs( currentNormal.z ).lessThan( 0.999 ).select( vec3( 0.0, 0.0, 1.0 ), vec3( 1.0, 0.0, 0.0 ) );
|
|
@@ -307,14 +431,14 @@ export const processBump = Fn( ( [ bumpMaps, currentNormal, material, uvCache ]
|
|
|
307
431
|
|
|
308
432
|
} );
|
|
309
433
|
|
|
310
|
-
export const processEmissive = Fn( ( [
|
|
434
|
+
export const processEmissive = Fn( ( [ material, uvCache ] ) => {
|
|
311
435
|
|
|
312
436
|
const emissionBase = material.emissive.mul( material.emissiveIntensity ).toVar();
|
|
313
437
|
|
|
314
438
|
If( material.emissiveMapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
315
439
|
|
|
316
|
-
const emissiveSample =
|
|
317
|
-
// sRGB→linear handled by GPU hardware (
|
|
440
|
+
const emissiveSample = sampleBucket( _srgbBuckets, material.emissiveMapIndex, uvCache.emissiveUV ).toVar();
|
|
441
|
+
// sRGB→linear handled by GPU hardware (sRGB bucket arrays carry SRGBColorSpace → rgba8unorm-srgb)
|
|
318
442
|
emissionBase.assign( emissionBase.mul( emissiveSample.rgb ) );
|
|
319
443
|
|
|
320
444
|
} );
|
|
@@ -327,10 +451,7 @@ export const processEmissive = Fn( ( [ emissiveMaps, material, uvCache ] ) => {
|
|
|
327
451
|
// MAIN BATCHED SAMPLING FUNCTION
|
|
328
452
|
// ================================================================================
|
|
329
453
|
|
|
330
|
-
export const sampleAllMaterialTextures = Fn( ( [
|
|
331
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
332
|
-
material, uv, geometryNormal
|
|
333
|
-
] ) => {
|
|
454
|
+
export const sampleAllMaterialTextures = Fn( ( [ material, uv, geometryNormal ] ) => {
|
|
334
455
|
|
|
335
456
|
const albedo = vec4( 0.0 ).toVar();
|
|
336
457
|
const emissive = vec3( 0.0 ).toVar();
|
|
@@ -351,17 +472,17 @@ export const sampleAllMaterialTextures = Fn( ( [
|
|
|
351
472
|
// Compute optimized UV cache with redundancy detection
|
|
352
473
|
const uvCache = UVCache.wrap( computeUVCache( uv, material ) ).toVar();
|
|
353
474
|
|
|
354
|
-
// Process samples
|
|
355
|
-
albedo.assign( processAlbedo(
|
|
475
|
+
// Process samples (bucket nodes read from module-level state)
|
|
476
|
+
albedo.assign( processAlbedo( material, uvCache ) );
|
|
356
477
|
|
|
357
|
-
const metalRough = processMetalnessRoughness(
|
|
478
|
+
const metalRough = processMetalnessRoughness( material, uvCache );
|
|
358
479
|
metalness.assign( metalRough.x );
|
|
359
480
|
roughness.assign( metalRough.y );
|
|
360
481
|
|
|
361
|
-
const currentNormal = processNormal(
|
|
362
|
-
normal.assign( processBump(
|
|
482
|
+
const currentNormal = processNormal( geometryNormal, material, uvCache ).toVar();
|
|
483
|
+
normal.assign( processBump( currentNormal, material, uvCache ) );
|
|
363
484
|
|
|
364
|
-
emissive.assign( processEmissive(
|
|
485
|
+
emissive.assign( processEmissive( material, uvCache ) );
|
|
365
486
|
|
|
366
487
|
} );
|
|
367
488
|
|
|
@@ -369,15 +490,15 @@ export const sampleAllMaterialTextures = Fn( ( [
|
|
|
369
490
|
|
|
370
491
|
} );
|
|
371
492
|
|
|
372
|
-
// Sample displacement map at given UV coordinates
|
|
373
|
-
export const sampleDisplacementMap = Fn( ( [
|
|
493
|
+
// Sample displacement map (linear pool) at given UV coordinates.
|
|
494
|
+
export const sampleDisplacementMap = Fn( ( [ displacementMapIndex, uv, transform ] ) => {
|
|
374
495
|
|
|
375
496
|
const result = float( 0.0 ).toVar();
|
|
376
497
|
|
|
377
498
|
If( displacementMapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
378
499
|
|
|
379
500
|
const transformedUV = getTransformedUV( { uv, transform } );
|
|
380
|
-
result.assign(
|
|
501
|
+
result.assign( sampleBucket( _linearBuckets, displacementMapIndex, transformedUV ).r );
|
|
381
502
|
|
|
382
503
|
} );
|
|
383
504
|
|