rayzee 5.1.1 → 5.3.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/dist/rayzee.es.js +2795 -2587
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +49 -49
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +77 -2
- package/src/PathTracerApp.js +2 -0
- package/src/Processor/AssetLoader.js +31 -0
- package/src/Processor/GeometryExtractor.js +22 -67
- package/src/Processor/ShaderBuilder.js +9 -0
- package/src/Processor/TextureCreator.js +153 -17
- package/src/RenderSettings.js +1 -0
- package/src/TSL/BVHTraversal.js +9 -3
- package/src/TSL/Common.js +62 -28
- package/src/TSL/Debugger.js +1 -2
- package/src/TSL/EmissiveSampling.js +3 -4
- package/src/TSL/LightsCore.js +1 -1
- package/src/TSL/LightsDirect.js +112 -15
- package/src/TSL/LightsSampling.js +2 -3
- package/src/TSL/PathTracerCore.js +6 -5
- package/src/TSL/Struct.js +16 -0
- package/src/managers/MaterialDataManager.js +138 -150
- package/src/managers/UniformManager.js +1 -0
package/src/TSL/Common.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
MaterialClassification,
|
|
6
6
|
MISStrategy,
|
|
7
7
|
RayTracingMaterial,
|
|
8
|
+
ShadowMaterial,
|
|
8
9
|
} from './Struct.js';
|
|
9
10
|
|
|
10
11
|
export const PI = 3.14159;
|
|
@@ -16,7 +17,11 @@ export const MIN_CLEARCOAT_ROUGHNESS = 0.089;
|
|
|
16
17
|
export const MAX_ROUGHNESS = 1.0;
|
|
17
18
|
export const MIN_PDF = 0.001;
|
|
18
19
|
export const REC709_LUMINANCE_COEFFICIENTS = vec3( 0.2126, 0.7152, 0.0722 );
|
|
19
|
-
|
|
20
|
+
import { MATERIAL_DATA_LAYOUT } from '../EngineDefaults.js';
|
|
21
|
+
|
|
22
|
+
export const MATERIAL_SLOTS = MATERIAL_DATA_LAYOUT.SLOTS_PER_MATERIAL;
|
|
23
|
+
export const MATERIAL_SLOT = MATERIAL_DATA_LAYOUT.SLOT;
|
|
24
|
+
const S = MATERIAL_SLOT;
|
|
20
25
|
|
|
21
26
|
// XYZ to sRGB color space conversion matrix
|
|
22
27
|
export const XYZ_TO_REC709 = mat3(
|
|
@@ -313,33 +318,33 @@ export const arrayToMat3 = wgslFn( `
|
|
|
313
318
|
|
|
314
319
|
export const getMaterial = Fn( ( [ materialIndex, materialBuffer ] ) => {
|
|
315
320
|
|
|
316
|
-
const data0 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
317
|
-
const data1 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
318
|
-
const data2 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
319
|
-
const data3 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
320
|
-
const data4 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
321
|
-
const data5 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
322
|
-
const data6 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
323
|
-
const data7 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
324
|
-
const data8 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
325
|
-
const data9 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
326
|
-
const data10 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
327
|
-
const data11 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
328
|
-
const data12 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
329
|
-
const data13 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
330
|
-
const data14 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
331
|
-
const data15 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
332
|
-
const data16 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
333
|
-
const data17 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
334
|
-
const data18 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
335
|
-
const data19 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
336
|
-
const data20 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
337
|
-
const data21 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
338
|
-
const data22 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
339
|
-
const data23 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
340
|
-
const data24 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
341
|
-
const data25 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
342
|
-
const data26 = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
321
|
+
const data0 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.COLOR_METALNESS ), int( MATERIAL_SLOTS ) ).toVar();
|
|
322
|
+
const data1 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.EMISSIVE_ROUGHNESS ), int( MATERIAL_SLOTS ) ).toVar();
|
|
323
|
+
const data2 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.IOR_TRANSMISSION ), int( MATERIAL_SLOTS ) ).toVar();
|
|
324
|
+
const data3 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ATTENUATION ), int( MATERIAL_SLOTS ) ).toVar();
|
|
325
|
+
const data4 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.DISPERSION_SHEEN ), int( MATERIAL_SLOTS ) ).toVar();
|
|
326
|
+
const data5 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.SHEEN_COLOR ), int( MATERIAL_SLOTS ) ).toVar();
|
|
327
|
+
const data6 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.SPECULAR ), int( MATERIAL_SLOTS ) ).toVar();
|
|
328
|
+
const data7 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.IRIDESCENCE ), int( MATERIAL_SLOTS ) ).toVar();
|
|
329
|
+
const data8 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.MAP_INDICES_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
330
|
+
const data9 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.MAP_INDICES_B ), int( MATERIAL_SLOTS ) ).toVar();
|
|
331
|
+
const data10 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.OPACITY_ALPHA ), int( MATERIAL_SLOTS ) ).toVar();
|
|
332
|
+
const data11 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ALPHA_MODE ), int( MATERIAL_SLOTS ) ).toVar();
|
|
333
|
+
const data12 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.BUMP_DISPLACEMENT ), int( MATERIAL_SLOTS ) ).toVar();
|
|
334
|
+
const data13 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ALBEDO_TRANSFORM_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
335
|
+
const data14 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ALBEDO_TRANSFORM_B ), int( MATERIAL_SLOTS ) ).toVar();
|
|
336
|
+
const data15 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.NORMAL_TRANSFORM_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
337
|
+
const data16 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.NORMAL_TRANSFORM_B ), int( MATERIAL_SLOTS ) ).toVar();
|
|
338
|
+
const data17 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ROUGHNESS_TRANSFORM_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
339
|
+
const data18 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ROUGHNESS_TRANSFORM_B ), int( MATERIAL_SLOTS ) ).toVar();
|
|
340
|
+
const data19 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.METALNESS_TRANSFORM_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
341
|
+
const data20 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.METALNESS_TRANSFORM_B ), int( MATERIAL_SLOTS ) ).toVar();
|
|
342
|
+
const data21 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.EMISSIVE_TRANSFORM_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
343
|
+
const data22 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.EMISSIVE_TRANSFORM_B ), int( MATERIAL_SLOTS ) ).toVar();
|
|
344
|
+
const data23 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.BUMP_TRANSFORM_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
345
|
+
const data24 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.BUMP_TRANSFORM_B ), int( MATERIAL_SLOTS ) ).toVar();
|
|
346
|
+
const data25 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.DISPLACEMENT_TRANSFORM_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
347
|
+
const data26 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.DISPLACEMENT_TRANSFORM_B ), int( MATERIAL_SLOTS ) ).toVar();
|
|
343
348
|
|
|
344
349
|
return RayTracingMaterial( {
|
|
345
350
|
color: vec4( data0.rgb, 1.0 ),
|
|
@@ -390,6 +395,35 @@ export const getMaterial = Fn( ( [ materialIndex, materialBuffer ] ) => {
|
|
|
390
395
|
|
|
391
396
|
} );
|
|
392
397
|
|
|
398
|
+
// ── Shadow material thin reader (7 slot reads instead of 27) ─────────────
|
|
399
|
+
// Only fetches fields needed by traceShadowRay: alpha, transmission, attenuation, albedo transform.
|
|
400
|
+
|
|
401
|
+
export const getShadowMaterial = Fn( ( [ materialIndex, materialBuffer ] ) => {
|
|
402
|
+
|
|
403
|
+
const data2 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.IOR_TRANSMISSION ), int( MATERIAL_SLOTS ) ).toVar();
|
|
404
|
+
const data3 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ATTENUATION ), int( MATERIAL_SLOTS ) ).toVar();
|
|
405
|
+
const data8 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.MAP_INDICES_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
406
|
+
const data10 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.OPACITY_ALPHA ), int( MATERIAL_SLOTS ) ).toVar();
|
|
407
|
+
const data11 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ALPHA_MODE ), int( MATERIAL_SLOTS ) ).toVar();
|
|
408
|
+
const data13 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ALBEDO_TRANSFORM_A ), int( MATERIAL_SLOTS ) ).toVar();
|
|
409
|
+
const data14 = getDatafromStorageBuffer( materialBuffer, materialIndex, int( S.ALBEDO_TRANSFORM_B ), int( MATERIAL_SLOTS ) ).toVar();
|
|
410
|
+
|
|
411
|
+
return ShadowMaterial( {
|
|
412
|
+
color: vec4( 1.0 ), // Shadow path never samples full textures; color.a is always 1.0
|
|
413
|
+
ior: data2.r,
|
|
414
|
+
transmission: data2.g,
|
|
415
|
+
attenuationColor: data3.rgb,
|
|
416
|
+
attenuationDistance: data3.a,
|
|
417
|
+
albedoMapIndex: int( data8.r ),
|
|
418
|
+
opacity: data10.r,
|
|
419
|
+
transparent: data10.b,
|
|
420
|
+
alphaTest: data10.a,
|
|
421
|
+
alphaMode: int( data11.r ),
|
|
422
|
+
albedoTransform: arrayToMat3( { data1: data13, data2: data14 } ),
|
|
423
|
+
} );
|
|
424
|
+
|
|
425
|
+
} );
|
|
426
|
+
|
|
393
427
|
// ── Edge-stopping weight (normal + depth) ──────────────────────────────────
|
|
394
428
|
// Used by ASVGF and SSRC for temporal/spatial reprojection edge-stopping.
|
|
395
429
|
|
package/src/TSL/Debugger.js
CHANGED
|
@@ -356,8 +356,7 @@ export const TraceDebugMode = Fn( ( [
|
|
|
356
356
|
const bounceDir = cosineWeightedSample( { N: normalA, xi } ).toVar();
|
|
357
357
|
|
|
358
358
|
// Trace secondary ray from the hit point (offset along normal to avoid self-intersection)
|
|
359
|
-
const
|
|
360
|
-
const bounceOrigin = hitInfo.hitPoint.add( normalA.mul( debugEps ) ).toVar();
|
|
359
|
+
const bounceOrigin = hitInfo.hitPoint.add( normalA.mul( 0.001 ) ).toVar();
|
|
361
360
|
const bounceRay = Ray( { origin: bounceOrigin, direction: bounceDir } );
|
|
362
361
|
|
|
363
362
|
const bounceHit = HitInfo.wrap( traverseBVH(
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
} from 'three/tsl';
|
|
29
29
|
|
|
30
30
|
import { struct } from './structProxy.js';
|
|
31
|
-
import { MIN_PDF, getDatafromStorageBuffer, powerHeuristic } from './Common.js';
|
|
31
|
+
import { MIN_PDF, getDatafromStorageBuffer, powerHeuristic, MATERIAL_SLOTS, MATERIAL_SLOT } from './Common.js';
|
|
32
32
|
import { RandomValue } from './Random.js';
|
|
33
33
|
import { calculateMaterialPDF } from './LightsSampling.js';
|
|
34
34
|
|
|
@@ -326,9 +326,8 @@ export const calculateEmissiveLightPdf = Fn( ( [
|
|
|
326
326
|
const area = triangleArea( triData.v0, triData.v1, triData.v2 );
|
|
327
327
|
|
|
328
328
|
// Targeted material read: only fetch emissive data (2 vec4s instead of full 27)
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
const matData2 = getDatafromStorageBuffer( materialBuffer, triData.materialIndex, int( 2 ), MATERIAL_SLOTS );
|
|
329
|
+
const matData1 = getDatafromStorageBuffer( materialBuffer, triData.materialIndex, int( MATERIAL_SLOT.EMISSIVE_ROUGHNESS ), MATERIAL_SLOTS );
|
|
330
|
+
const matData2 = getDatafromStorageBuffer( materialBuffer, triData.materialIndex, int( MATERIAL_SLOT.IOR_TRANSMISSION ), MATERIAL_SLOTS );
|
|
332
331
|
const avgEmissive = matData1.x.add( matData1.y ).add( matData1.z ).div( 3.0 );
|
|
333
332
|
const power = max( avgEmissive.mul( matData2.a ).mul( area ), float( 1e-10 ) );
|
|
334
333
|
const selectionPdf = power.div( max( emissiveTotalPower, float( 1e-10 ) ) );
|
package/src/TSL/LightsCore.js
CHANGED
|
@@ -257,7 +257,7 @@ export const intersectAreaLight = Fn( ( [ light, rayOrigin, rayDirection ] ) =>
|
|
|
257
257
|
const t = dot( light.position.sub( rayOrigin ), normal ).mul( invDenom ).toVar();
|
|
258
258
|
|
|
259
259
|
// Skip intersections behind the ray
|
|
260
|
-
If( t.greaterThan(
|
|
260
|
+
If( t.greaterThan( 0.001 ), () => {
|
|
261
261
|
|
|
262
262
|
// Optimized rectangle test using vector rejection
|
|
263
263
|
const hitPoint = rayOrigin.add( rayDirection.mul( t ) );
|
package/src/TSL/LightsDirect.js
CHANGED
|
@@ -25,10 +25,11 @@ import {
|
|
|
25
25
|
clamp,
|
|
26
26
|
smoothstep,
|
|
27
27
|
select,
|
|
28
|
+
texture,
|
|
28
29
|
} from 'three/tsl';
|
|
29
30
|
|
|
30
|
-
import { Ray,
|
|
31
|
-
import { PI, TWO_PI, EPSILON, REC709_LUMINANCE_COEFFICIENTS, powerHeuristic,
|
|
31
|
+
import { Ray, ShadowMaterial, HitInfo, DirectionSample, MaterialCache } from './Struct.js';
|
|
32
|
+
import { PI, TWO_PI, EPSILON, REC709_LUMINANCE_COEFFICIENTS, powerHeuristic, getShadowMaterial, getDatafromStorageBuffer } from './Common.js';
|
|
32
33
|
import { fresnelSchlickFloat } from './Fresnel.js';
|
|
33
34
|
import { iorToFresnel0 } from './Fresnel.js';
|
|
34
35
|
import {
|
|
@@ -37,6 +38,33 @@ import {
|
|
|
37
38
|
} from './LightsCore.js';
|
|
38
39
|
import { calculateBeerLawAbsorption, calculateShadowTransmittance } from './MaterialTransmission.js';
|
|
39
40
|
import { RandomValue } from './Random.js';
|
|
41
|
+
import { getTransformedUV } from './TextureSampling.js';
|
|
42
|
+
|
|
43
|
+
// Module-level state for alpha-cutout shadow testing.
|
|
44
|
+
// Set by ShaderBuilder before graph construction (same pattern as _meshVisibilityBuffer in BVHTraversal.js).
|
|
45
|
+
let _shadowAlbedoMaps = null;
|
|
46
|
+
let _enableAlphaShadows = null;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set the albedo texture array node for alpha-aware shadow rays.
|
|
50
|
+
* Must be called before the shader graph is constructed.
|
|
51
|
+
* @param {TextureNode} maps - TSL texture node for the albedo array
|
|
52
|
+
*/
|
|
53
|
+
export function setShadowAlbedoMaps( maps ) {
|
|
54
|
+
|
|
55
|
+
_shadowAlbedoMaps = maps;
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Set the runtime uniform node that toggles alpha-cutout shadows.
|
|
61
|
+
* @param {UniformNode} node - TSL int uniform (0 = disabled, 1 = enabled)
|
|
62
|
+
*/
|
|
63
|
+
export function setAlphaShadowsUniform( node ) {
|
|
64
|
+
|
|
65
|
+
_enableAlphaShadows = node;
|
|
66
|
+
|
|
67
|
+
}
|
|
40
68
|
|
|
41
69
|
// ================================================================================
|
|
42
70
|
// SHADOW RAY MATERIAL TRANSPARENCY
|
|
@@ -102,11 +130,82 @@ export const traceShadowRay = Fn( ( [
|
|
|
102
130
|
|
|
103
131
|
} );
|
|
104
132
|
|
|
105
|
-
// Fetch material for the hit surface
|
|
106
|
-
const shadowMaterial =
|
|
133
|
+
// Fetch material for the hit surface (thin reader: 7 slots instead of 27)
|
|
134
|
+
const shadowMaterial = ShadowMaterial.wrap( getShadowMaterial( shadowHit.materialIndex, materialBuffer ) );
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------
|
|
137
|
+
// Alpha-cutout handling (MASK / BLEND with albedo texture alpha)
|
|
138
|
+
// Gated by runtime uniform + alphaMode check — zero overhead for opaque materials.
|
|
139
|
+
// UV computation deferred here from BVH traversal: barycentrics stored in shadowHit.uv,
|
|
140
|
+
// triangle index in shadowHit.triangleIndex. Actual UV interpolation only when needed.
|
|
141
|
+
// ---------------------------------------------------------------
|
|
142
|
+
const alphaCutout = tslBool( false ).toVar();
|
|
143
|
+
|
|
144
|
+
if ( _enableAlphaShadows ) If( _enableAlphaShadows.equal( int( 1 ) ), () => {
|
|
145
|
+
|
|
146
|
+
// Sample texture alpha once (shared by MASK and BLEND paths).
|
|
147
|
+
// Deferred UV: barycentrics in shadowHit.uv, triangle index in shadowHit.triangleIndex.
|
|
148
|
+
const texAlpha = float( 1.0 ).toVar();
|
|
149
|
+
|
|
150
|
+
if ( _shadowAlbedoMaps ) {
|
|
151
|
+
|
|
152
|
+
If( shadowMaterial.albedoMapIndex.greaterThanEqual( int( 0 ) ), () => {
|
|
153
|
+
|
|
154
|
+
const baryU = shadowHit.uv.x;
|
|
155
|
+
const baryV = shadowHit.uv.y;
|
|
156
|
+
const baryW = float( 1.0 ).sub( baryU ).sub( baryV );
|
|
157
|
+
const TRI_STRIDE = int( 8 );
|
|
158
|
+
const uvData1 = getDatafromStorageBuffer( triangleBuffer, shadowHit.triangleIndex, int( 6 ), TRI_STRIDE );
|
|
159
|
+
const uvData2 = getDatafromStorageBuffer( triangleBuffer, shadowHit.triangleIndex, int( 7 ), TRI_STRIDE );
|
|
160
|
+
const hitUV = uvData1.xy.mul( baryW ).add( uvData1.zw.mul( baryU ) ).add( uvData2.xy.mul( baryV ) );
|
|
161
|
+
const albedoUV = getTransformedUV( { uv: hitUV, transform: shadowMaterial.albedoTransform } );
|
|
162
|
+
texAlpha.assign( texture( _shadowAlbedoMaps, albedoUV ).depth( int( shadowMaterial.albedoMapIndex ) ).a );
|
|
163
|
+
|
|
164
|
+
} );
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
If( shadowMaterial.alphaMode.equal( int( 1 ) ), () => {
|
|
169
|
+
|
|
170
|
+
// MASK mode: binary alpha cutout
|
|
171
|
+
const effectiveAlpha = shadowMaterial.color.a.mul( texAlpha );
|
|
172
|
+
const cutoff = select( shadowMaterial.alphaTest.greaterThan( 0.0 ), shadowMaterial.alphaTest, float( 0.5 ) );
|
|
173
|
+
If( effectiveAlpha.lessThan( cutoff ), () => {
|
|
174
|
+
|
|
175
|
+
alphaCutout.assign( true );
|
|
176
|
+
|
|
177
|
+
} );
|
|
178
|
+
|
|
179
|
+
} ).ElseIf( shadowMaterial.alphaMode.equal( int( 2 ) ), () => {
|
|
180
|
+
|
|
181
|
+
// BLEND mode: modulate transmittance by alpha
|
|
182
|
+
const blendAlpha = clamp( shadowMaterial.color.a.mul( shadowMaterial.opacity ).mul( texAlpha ), 0.0, 1.0 );
|
|
183
|
+
transmittance.mulAssign( float( 1.0 ).sub( blendAlpha ) );
|
|
184
|
+
|
|
185
|
+
If( transmittance.lessThan( 0.005 ), () => {
|
|
186
|
+
|
|
187
|
+
transmittance.assign( 0.0 );
|
|
188
|
+
Break();
|
|
189
|
+
|
|
190
|
+
} );
|
|
191
|
+
|
|
192
|
+
alphaCutout.assign( true );
|
|
193
|
+
|
|
194
|
+
} );
|
|
195
|
+
|
|
196
|
+
} );
|
|
197
|
+
|
|
198
|
+
// ---------------------------------------------------------------
|
|
199
|
+
// Surface interaction: alpha-skip, transmission, transparent, or opaque
|
|
200
|
+
// ---------------------------------------------------------------
|
|
201
|
+
If( alphaCutout, () => {
|
|
202
|
+
|
|
203
|
+
// Alpha-transparent surface — advance ray past it
|
|
204
|
+
const alphaEps = max( float( 1e-5 ), length( shadowHit.hitPoint ).mul( 1e-6 ) );
|
|
205
|
+
rayOrigin.assign( shadowHit.hitPoint.add( dir.mul( alphaEps ) ) );
|
|
206
|
+
remainingDist.subAssign( shadowHit.dst.add( alphaEps ) );
|
|
107
207
|
|
|
108
|
-
|
|
109
|
-
If( shadowMaterial.transmission.greaterThan( 0.0 ), () => {
|
|
208
|
+
} ).ElseIf( shadowMaterial.transmission.greaterThan( 0.0 ), () => {
|
|
110
209
|
|
|
111
210
|
const entering = dot( dir, shadowHit.normal ).lessThan( 0.0 );
|
|
112
211
|
const N = select( entering, shadowHit.normal, shadowHit.normal.negate() );
|
|
@@ -142,9 +241,8 @@ export const traceShadowRay = Fn( ( [
|
|
|
142
241
|
} );
|
|
143
242
|
|
|
144
243
|
// Continue ray past transmissive surface
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
remainingDist.subAssign( shadowHit.dst.add( transEps ) );
|
|
244
|
+
rayOrigin.assign( shadowHit.hitPoint.add( dir.mul( 0.001 ) ) );
|
|
245
|
+
remainingDist.subAssign( shadowHit.dst.add( 0.001 ) );
|
|
148
246
|
|
|
149
247
|
} ).ElseIf( shadowMaterial.transparent, () => {
|
|
150
248
|
|
|
@@ -159,9 +257,8 @@ export const traceShadowRay = Fn( ( [
|
|
|
159
257
|
} );
|
|
160
258
|
|
|
161
259
|
// Continue ray past transparent surface
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
remainingDist.subAssign( shadowHit.dst.add( alphaEps ) );
|
|
260
|
+
rayOrigin.assign( shadowHit.hitPoint.add( dir.mul( 0.001 ) ) );
|
|
261
|
+
remainingDist.subAssign( shadowHit.dst.add( 0.001 ) );
|
|
165
262
|
|
|
166
263
|
} ).Else( () => {
|
|
167
264
|
|
|
@@ -259,7 +356,7 @@ export const estimateLightImportance = Fn( ( [ light, hitPoint, normal, material
|
|
|
259
356
|
|
|
260
357
|
If( lightFacing.greaterThan( 0.0 ), () => {
|
|
261
358
|
|
|
262
|
-
const solidAngle = light.area.div( max( distSq,
|
|
359
|
+
const solidAngle = light.area.div( max( distSq, 0.1 ) );
|
|
263
360
|
const power = light.intensity.mul( dot( light.color, REC709_LUMINANCE_COEFFICIENTS ) ).mul( light.area );
|
|
264
361
|
|
|
265
362
|
// Material-aware weighting
|
|
@@ -661,7 +758,7 @@ export const calculatePointLightContribution = Fn( ( [
|
|
|
661
758
|
const rayOffset = calculateRayOffset( hitPoint, normal, material );
|
|
662
759
|
const rayOrigin = hitPoint.add( rayOffset );
|
|
663
760
|
|
|
664
|
-
const visibility = traceShadowRayFn( rayOrigin, lightDir, distance.
|
|
761
|
+
const visibility = traceShadowRayFn( rayOrigin, lightDir, distance.sub( 0.001 ), rngState );
|
|
665
762
|
|
|
666
763
|
If( visibility.greaterThan( 0.0 ), () => {
|
|
667
764
|
|
|
@@ -713,7 +810,7 @@ export const calculateSpotLightContribution = Fn( ( [
|
|
|
713
810
|
const rayOffset = calculateRayOffset( hitPoint, normal, material );
|
|
714
811
|
const rayOrigin = hitPoint.add( rayOffset );
|
|
715
812
|
|
|
716
|
-
const visibility = traceShadowRayFn( rayOrigin, lightDir, distance.
|
|
813
|
+
const visibility = traceShadowRayFn( rayOrigin, lightDir, distance.sub( 0.001 ), rngState );
|
|
717
814
|
|
|
718
815
|
If( visibility.greaterThan( 0.0 ), () => {
|
|
719
816
|
|
|
@@ -71,7 +71,6 @@ import {
|
|
|
71
71
|
calculatePointLightImportance,
|
|
72
72
|
calculateSpotLightImportance,
|
|
73
73
|
traceShadowRay,
|
|
74
|
-
calculateRayOffset,
|
|
75
74
|
} from './LightsDirect.js';
|
|
76
75
|
|
|
77
76
|
import { traverseBVHShadow } from './BVHTraversal.js';
|
|
@@ -941,7 +940,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
941
940
|
] ) => {
|
|
942
941
|
|
|
943
942
|
const totalContribution = vec3( 0.0 ).toVar();
|
|
944
|
-
const rayOrigin = hitPoint.add(
|
|
943
|
+
const rayOrigin = hitPoint.add( hitNormal.mul( 0.001 ) ).toVar();
|
|
945
944
|
|
|
946
945
|
// Early exit for highly emissive surfaces
|
|
947
946
|
If( material.emissiveIntensity.lessThanEqual( 10.0 ), () => {
|
|
@@ -1042,7 +1041,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
1042
1041
|
|
|
1043
1042
|
If( NoL.greaterThan( 0.0 ).and( lightImportance.mul( NoL ).greaterThan( importanceThreshold ) ).and( isDirectionValid( { direction: lightSample.direction, surfaceNormal: hitNormal } ) ), () => {
|
|
1044
1043
|
|
|
1045
|
-
const shadowDistance = min( lightSample.distance.
|
|
1044
|
+
const shadowDistance = min( lightSample.distance.sub( 0.001 ), float( 1000.0 ) ).toVar();
|
|
1046
1045
|
const visibility = traceShadowRay(
|
|
1047
1046
|
rayOrigin, lightSample.direction, shadowDistance, rngState,
|
|
1048
1047
|
traverseBVHShadow,
|
|
@@ -747,7 +747,9 @@ export const Trace = Fn( ( [
|
|
|
747
747
|
|
|
748
748
|
} );
|
|
749
749
|
|
|
750
|
-
// Get material
|
|
750
|
+
// Get full material (27 reads). Lazy transform loading was tested but regressed
|
|
751
|
+
// textured scenes due to identity-construct + conditional-assign overhead.
|
|
752
|
+
// Shadow rays use getShadowMaterial() (7 reads) — the real bandwidth win.
|
|
751
753
|
const material = RayTracingMaterial.wrap( getMaterial( hitInfo.materialIndex, materialBuffer ) ).toVar();
|
|
752
754
|
|
|
753
755
|
// Tessellation-free displacement — refine intersection with ray-height field marching
|
|
@@ -880,8 +882,7 @@ export const Trace = Fn( ( [
|
|
|
880
882
|
// For transmission: offset along the old ray direction to push through the surface
|
|
881
883
|
const reflectOffsetDir = select( interaction.entering, N, N.negate() );
|
|
882
884
|
const offsetDir = select( interaction.didReflect, reflectOffsetDir, rayDirection );
|
|
883
|
-
|
|
884
|
-
rayOrigin.assign( hitInfo.hitPoint.add( offsetDir.mul( bounceEps ) ) );
|
|
885
|
+
rayOrigin.assign( hitInfo.hitPoint.add( offsetDir.mul( 0.001 ) ) );
|
|
885
886
|
rayDirection.assign( interaction.direction );
|
|
886
887
|
|
|
887
888
|
stateIsPrimaryRay.assign( tslBool( false ) );
|
|
@@ -1055,7 +1056,7 @@ export const Trace = Fn( ( [
|
|
|
1055
1056
|
|
|
1056
1057
|
const rayOffset = calculateRayOffset( hitInfo.hitPoint, N, material );
|
|
1057
1058
|
const rayOrigin = hitInfo.hitPoint.add( rayOffset );
|
|
1058
|
-
const shadowDist = emissiveSample.distance.
|
|
1059
|
+
const shadowDist = emissiveSample.distance.sub( 0.001 );
|
|
1059
1060
|
const visibility = traceShadowRayWrapped( rayOrigin, emissiveSample.direction, shadowDist, rngState );
|
|
1060
1061
|
|
|
1061
1062
|
If( visibility.greaterThan( 0.0 ), () => {
|
|
@@ -1138,7 +1139,7 @@ export const Trace = Fn( ( [
|
|
|
1138
1139
|
throughput.mulAssign( indirectResult.throughput );
|
|
1139
1140
|
|
|
1140
1141
|
// Prepare for next bounce
|
|
1141
|
-
rayOrigin.assign( hitInfo.hitPoint.add(
|
|
1142
|
+
rayOrigin.assign( hitInfo.hitPoint.add( N.mul( 0.001 ) ) );
|
|
1142
1143
|
rayDirection.assign( indirectResult.direction );
|
|
1143
1144
|
prevBouncePdf.assign( indirectResult.combinedPdf );
|
|
1144
1145
|
|
package/src/TSL/Struct.js
CHANGED
|
@@ -52,6 +52,22 @@ export const RayTracingMaterial = struct( {
|
|
|
52
52
|
iridescenceThicknessRange: 'vec2',
|
|
53
53
|
} );
|
|
54
54
|
|
|
55
|
+
// Lightweight material for shadow ray evaluation — only the fields needed
|
|
56
|
+
// by traceShadowRay (alpha, transmission, transparency, attenuation).
|
|
57
|
+
export const ShadowMaterial = struct( {
|
|
58
|
+
color: 'vec4',
|
|
59
|
+
ior: 'float',
|
|
60
|
+
transmission: 'float',
|
|
61
|
+
attenuationColor: 'vec3',
|
|
62
|
+
attenuationDistance: 'float',
|
|
63
|
+
albedoMapIndex: 'int',
|
|
64
|
+
opacity: 'float',
|
|
65
|
+
transparent: 'bool',
|
|
66
|
+
alphaTest: 'float',
|
|
67
|
+
alphaMode: 'int',
|
|
68
|
+
albedoTransform: 'mat3',
|
|
69
|
+
} );
|
|
70
|
+
|
|
55
71
|
export const Sphere = struct( {
|
|
56
72
|
position: 'vec3',
|
|
57
73
|
radius: 'float',
|