rayzee 5.1.0 → 5.2.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 +2726 -2577
- 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 +1 -0
- package/src/Pipeline/RenderStage.js +0 -1
- package/src/Processor/AssetLoader.js +2 -5
- package/src/Processor/EquirectHDRInfo.js +42 -5
- package/src/Processor/SceneProcessor.js +2 -2
- package/src/Processor/ShaderBuilder.js +9 -0
- package/src/Processor/TextureCreator.js +25 -10
- package/src/RenderSettings.js +1 -0
- package/src/TSL/BVHTraversal.js +8 -2
- package/src/TSL/Common.js +62 -28
- package/src/TSL/EmissiveSampling.js +3 -4
- package/src/TSL/LightsDirect.js +105 -6
- package/src/TSL/PathTracerCore.js +3 -1
- package/src/TSL/Struct.js +16 -0
- package/src/managers/EnvironmentManager.js +1 -1
- package/src/managers/MaterialDataManager.js +138 -150
- package/src/managers/UniformManager.js +1 -0
package/package.json
CHANGED
package/src/EngineDefaults.js
CHANGED
|
@@ -80,6 +80,7 @@ export const ENGINE_DEFAULTS = {
|
|
|
80
80
|
renderLimitMode: 'frames',
|
|
81
81
|
renderTimeLimit: 30,
|
|
82
82
|
renderMode: 0,
|
|
83
|
+
enableAlphaShadows: false,
|
|
83
84
|
tiles: 3,
|
|
84
85
|
tilesHelper: false,
|
|
85
86
|
showLightHelper: false,
|
|
@@ -337,6 +338,80 @@ export const TRIANGLE_DATA_LAYOUT = {
|
|
|
337
338
|
UV_C_MAT_OFFSET: 28
|
|
338
339
|
};
|
|
339
340
|
|
|
341
|
+
// Material data layout constants — single source of truth for material buffer offsets.
|
|
342
|
+
// Shared between CPU writers (TextureCreator, MaterialDataManager) and GPU readers (Common.js getMaterial).
|
|
343
|
+
export const MATERIAL_DATA_LAYOUT = {
|
|
344
|
+
|
|
345
|
+
SLOTS_PER_MATERIAL: 27, // vec4 slots per material
|
|
346
|
+
FLOATS_PER_MATERIAL: 108, // total floats per material (27 × 4)
|
|
347
|
+
|
|
348
|
+
// ── Flat float offsets (CPU side) ────────────────────────────────
|
|
349
|
+
// Used as: data[ materialIndex * FLOATS_PER_MATERIAL + offset ]
|
|
350
|
+
// Ordered for cache-line coherence: shadow/culling → BxDF core → maps → extended → transforms
|
|
351
|
+
|
|
352
|
+
// Slot 0: ior + transmission + thickness + emissiveIntensity [shadow]
|
|
353
|
+
IOR: 0, TRANSMISSION: 1, THICKNESS: 2, EMISSIVE_INTENSITY: 3,
|
|
354
|
+
// Slot 1: attenuationColor.rgb + attenuationDistance [shadow]
|
|
355
|
+
ATTENUATION_COLOR: 4, ATTENUATION_DISTANCE: 7,
|
|
356
|
+
// Slot 2: opacity + side + transparent + alphaTest [shadow + culling]
|
|
357
|
+
OPACITY: 8, SIDE: 9, TRANSPARENT: 10, ALPHA_TEST: 11,
|
|
358
|
+
// Slot 3: alphaMode + depthWrite + normalScale [shadow]
|
|
359
|
+
ALPHA_MODE: 12, DEPTH_WRITE: 13, NORMAL_SCALE: 14,
|
|
360
|
+
// Slot 4: color.rgb + metalness [BxDF core]
|
|
361
|
+
COLOR: 16, METALNESS: 19,
|
|
362
|
+
// Slot 5: emissive.rgb + roughness [BxDF core]
|
|
363
|
+
EMISSIVE: 20, ROUGHNESS: 23,
|
|
364
|
+
// Slot 6: map indices (albedo, normal, roughness, metalness) [maps]
|
|
365
|
+
ALBEDO_MAP_INDEX: 24, NORMAL_MAP_INDEX: 25, ROUGHNESS_MAP_INDEX: 26, METALNESS_MAP_INDEX: 27,
|
|
366
|
+
// Slot 7: map indices (emissive, bump) + clearcoat [maps]
|
|
367
|
+
EMISSIVE_MAP_INDEX: 28, BUMP_MAP_INDEX: 29, CLEARCOAT: 30, CLEARCOAT_ROUGHNESS: 31,
|
|
368
|
+
// Slot 8: dispersion + visible + sheen + sheenRoughness [extended BxDF]
|
|
369
|
+
DISPERSION: 32, VISIBLE: 33, SHEEN: 34, SHEEN_ROUGHNESS: 35,
|
|
370
|
+
// Slot 9: sheenColor.rgb + (reserved) [extended BxDF]
|
|
371
|
+
SHEEN_COLOR: 36,
|
|
372
|
+
// Slot 10: specularIntensity + specularColor.rgb [extended BxDF]
|
|
373
|
+
SPECULAR_INTENSITY: 40, SPECULAR_COLOR: 41,
|
|
374
|
+
// Slot 11: iridescence + iridescenceIOR + iridescenceThicknessRange [extended BxDF]
|
|
375
|
+
IRIDESCENCE: 44, IRIDESCENCE_IOR: 45, IRIDESCENCE_THICKNESS_RANGE: 46,
|
|
376
|
+
// Slot 12: bumpScale + displacementScale + displacementMapIndex + (padding)
|
|
377
|
+
BUMP_SCALE: 48, DISPLACEMENT_SCALE: 49, DISPLACEMENT_MAP_INDEX: 50,
|
|
378
|
+
|
|
379
|
+
// ── Transform float offsets (8 floats each: 7 matrix values + 1 padding) ──
|
|
380
|
+
ALBEDO_TRANSFORM: 52,
|
|
381
|
+
NORMAL_TRANSFORM: 60,
|
|
382
|
+
ROUGHNESS_TRANSFORM: 68,
|
|
383
|
+
METALNESS_TRANSFORM: 76,
|
|
384
|
+
EMISSIVE_TRANSFORM: 84,
|
|
385
|
+
BUMP_TRANSFORM: 92,
|
|
386
|
+
DISPLACEMENT_TRANSFORM: 100,
|
|
387
|
+
|
|
388
|
+
// ── Vec4 slot indices (GPU/TSL side) ─────────────────────────────
|
|
389
|
+
// Used with getDatafromStorageBuffer( buf, matIdx, int(slot), int(SLOTS_PER_MATERIAL) )
|
|
390
|
+
SLOT: {
|
|
391
|
+
IOR_TRANSMISSION: 0, // [shadow] ior, transmission, thickness, emissiveIntensity
|
|
392
|
+
ATTENUATION: 1, // [shadow] attenuationColor, attenuationDistance
|
|
393
|
+
OPACITY_ALPHA: 2, // [shadow+culling] opacity, side, transparent, alphaTest
|
|
394
|
+
ALPHA_MODE: 3, // [shadow] alphaMode, depthWrite, normalScale
|
|
395
|
+
COLOR_METALNESS: 4, // [BxDF] color.rgb, metalness
|
|
396
|
+
EMISSIVE_ROUGHNESS: 5, // [BxDF] emissive.rgb, roughness
|
|
397
|
+
MAP_INDICES_A: 6, // [maps] albedo, normal, roughness, metalness
|
|
398
|
+
MAP_INDICES_B: 7, // [maps] emissive, bump, clearcoat, clearcoatRoughness
|
|
399
|
+
DISPERSION_SHEEN: 8, // [extended] dispersion, visible, sheen, sheenRoughness
|
|
400
|
+
SHEEN_COLOR: 9, // [extended] sheenColor, reserved
|
|
401
|
+
SPECULAR: 10, // [extended] specularIntensity, specularColor
|
|
402
|
+
IRIDESCENCE: 11, // [extended] iridescence, iridescenceIOR, iridescenceThicknessRange
|
|
403
|
+
BUMP_DISPLACEMENT: 12, // bumpScale, displacementScale, displacementMapIndex
|
|
404
|
+
ALBEDO_TRANSFORM_A: 13, ALBEDO_TRANSFORM_B: 14,
|
|
405
|
+
NORMAL_TRANSFORM_A: 15, NORMAL_TRANSFORM_B: 16,
|
|
406
|
+
ROUGHNESS_TRANSFORM_A: 17, ROUGHNESS_TRANSFORM_B: 18,
|
|
407
|
+
METALNESS_TRANSFORM_A: 19, METALNESS_TRANSFORM_B: 20,
|
|
408
|
+
EMISSIVE_TRANSFORM_A: 21, EMISSIVE_TRANSFORM_B: 22,
|
|
409
|
+
BUMP_TRANSFORM_A: 23, BUMP_TRANSFORM_B: 24,
|
|
410
|
+
DISPLACEMENT_TRANSFORM_A: 25, DISPLACEMENT_TRANSFORM_B: 26,
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
};
|
|
414
|
+
|
|
340
415
|
// BVH node leaf markers
|
|
341
416
|
export const BVH_LEAF_MARKERS = {
|
|
342
417
|
TRIANGLE_LEAF: - 1, // Leaf containing triangle references
|
|
@@ -364,14 +439,14 @@ export const DEFAULT_TEXTURE_MATRIX = [ 0, 0, 1, 1, 0, 0, 0, 1 ];
|
|
|
364
439
|
// Render mode configurations
|
|
365
440
|
export const FINAL_RENDER_CONFIG = {
|
|
366
441
|
maxSamples: 30, bounces: 20, transmissiveBounces: 8, samplesPerPixel: 1,
|
|
367
|
-
renderMode: 1, tiles: 3, tilesHelper: false,
|
|
442
|
+
renderMode: 1, enableAlphaShadows: true, tiles: 3, tilesHelper: false,
|
|
368
443
|
enableOIDN: true, oidnQuality: 'balance',
|
|
369
444
|
interactionModeEnabled: false,
|
|
370
445
|
};
|
|
371
446
|
|
|
372
447
|
export const PREVIEW_RENDER_CONFIG = {
|
|
373
448
|
maxSamples: ENGINE_DEFAULTS.maxSamples, bounces: ENGINE_DEFAULTS.bounces,
|
|
374
|
-
samplesPerPixel: ENGINE_DEFAULTS.samplesPerPixel, renderMode: ENGINE_DEFAULTS.renderMode,
|
|
449
|
+
samplesPerPixel: ENGINE_DEFAULTS.samplesPerPixel, renderMode: ENGINE_DEFAULTS.renderMode, enableAlphaShadows: ENGINE_DEFAULTS.enableAlphaShadows,
|
|
375
450
|
transmissiveBounces: ENGINE_DEFAULTS.transmissiveBounces,
|
|
376
451
|
tiles: ENGINE_DEFAULTS.tiles, tilesHelper: ENGINE_DEFAULTS.tilesHelper,
|
|
377
452
|
enableOIDN: false, oidnQuality: 'fast',
|
package/src/PathTracerApp.js
CHANGED
|
@@ -730,6 +730,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
730
730
|
}, { silent: true } );
|
|
731
731
|
|
|
732
732
|
this.stages.pathTracer?.setUniform( 'renderMode', parseInt( config.renderMode ) );
|
|
733
|
+
this.stages.pathTracer?.setUniform( 'enableAlphaShadows', config.enableAlphaShadows ?? false );
|
|
733
734
|
this.stages.pathTracer?.tileManager?.setTileCount( config.tiles );
|
|
734
735
|
|
|
735
736
|
const tileHelper = this.overlayManager?.getHelper( 'tiles' );
|
|
@@ -142,7 +142,6 @@ export class RenderStage {
|
|
|
142
142
|
|
|
143
143
|
const renderMode = context.getState( 'renderMode' ) || 0;
|
|
144
144
|
const tileRenderingComplete = context.getState( 'tileRenderingComplete' );
|
|
145
|
-
const frame = context.getState( 'frame' ) || 0;
|
|
146
145
|
|
|
147
146
|
switch ( this.executionMode ) {
|
|
148
147
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box3, Vector3, RectAreaLight, Color, FloatType, LinearFilter, EquirectangularReflectionMapping,
|
|
1
|
+
import { Box3, Vector3, RectAreaLight, Color, FloatType, LinearFilter, EquirectangularReflectionMapping,
|
|
2
2
|
TextureLoader, Mesh, MeshStandardMaterial, MeshPhysicalMaterial, CircleGeometry, Points, PointsMaterial, LoadingManager, EventDispatcher
|
|
3
3
|
} from 'three';
|
|
4
4
|
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
@@ -171,8 +171,6 @@ export class AssetLoader extends EventDispatcher {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
texture.generateMipmaps = true;
|
|
174
|
-
// texture.minFilter = LinearMipmapLinearFilter;
|
|
175
|
-
// texture.magFilter = LinearFilter;
|
|
176
174
|
|
|
177
175
|
this.applyEnvironmentToScene( texture );
|
|
178
176
|
this.dispatchEvent( { type: 'load', texture } );
|
|
@@ -353,7 +351,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
353
351
|
|
|
354
352
|
}
|
|
355
353
|
|
|
356
|
-
async findAndLoadModelFromZip( zip
|
|
354
|
+
async findAndLoadModelFromZip( zip ) {
|
|
357
355
|
|
|
358
356
|
const mainModelFiles = [
|
|
359
357
|
'scene.gltf', 'scene.glb', 'model.gltf', 'model.glb',
|
|
@@ -459,7 +457,6 @@ export class AssetLoader extends EventDispatcher {
|
|
|
459
457
|
if ( extension === 'gltf' ) {
|
|
460
458
|
|
|
461
459
|
const gltfContent = strFromU8( fileContent );
|
|
462
|
-
const gltfJson = JSON.parse( gltfContent );
|
|
463
460
|
const manager = new LoadingManager();
|
|
464
461
|
const gltfDir = filePath.split( '/' ).slice( 0, - 1 ).join( '/' );
|
|
465
462
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DataUtils, HalfFloatType, FloatType } from 'three';
|
|
1
|
+
import { DataUtils, HalfFloatType, FloatType, SRGBColorSpace } from 'three';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Binary search to find the closest index
|
|
@@ -38,18 +38,35 @@ function colorToLuminance( r, g, b ) {
|
|
|
38
38
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* sRGB to linear conversion (IEC 61966-2-1 transfer function)
|
|
43
|
+
*/
|
|
44
|
+
function sRGBToLinear( c ) {
|
|
45
|
+
|
|
46
|
+
return c <= 0.04045 ? c / 12.92 : ( ( c + 0.055 ) / 1.055 ) ** 2.4;
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
/**
|
|
42
51
|
* Extract Float32 RGBA pixel data from an environment map.
|
|
43
|
-
* Handles HalfFloat/integer type conversion
|
|
52
|
+
* Handles HalfFloat/integer type conversion, canvas extraction for
|
|
53
|
+
* non-DataTexture images (JPG/PNG), sRGB-to-linear conversion, and Y-flip.
|
|
44
54
|
* @returns {{ floatData: Float32Array, width: number, height: number }}
|
|
45
55
|
*/
|
|
46
56
|
export function extractFloatData( envMap ) {
|
|
47
57
|
|
|
48
|
-
const { width, height
|
|
58
|
+
const { width, height } = envMap.image;
|
|
59
|
+
let data = envMap.image.data;
|
|
60
|
+
let needsSRGBToLinear = false;
|
|
49
61
|
|
|
62
|
+
// No CPU-accessible data — extract from HTMLImageElement / ImageBitmap via canvas
|
|
50
63
|
if ( ! data ) {
|
|
51
64
|
|
|
52
|
-
|
|
65
|
+
const canvas = new OffscreenCanvas( width, height );
|
|
66
|
+
const ctx = canvas.getContext( '2d' );
|
|
67
|
+
ctx.drawImage( envMap.image, 0, 0, width, height );
|
|
68
|
+
data = ctx.getImageData( 0, 0, width, height ).data;
|
|
69
|
+
needsSRGBToLinear = true;
|
|
53
70
|
|
|
54
71
|
}
|
|
55
72
|
|
|
@@ -72,7 +89,7 @@ export function extractFloatData( envMap ) {
|
|
|
72
89
|
|
|
73
90
|
} else {
|
|
74
91
|
|
|
75
|
-
// Integer types (Uint8, Int16, etc.)
|
|
92
|
+
// Integer types (Uint8, Uint8Clamped, Int16, etc.)
|
|
76
93
|
let maxIntValue;
|
|
77
94
|
if ( data instanceof Int8Array || data instanceof Int16Array || data instanceof Int32Array ) {
|
|
78
95
|
|
|
@@ -93,6 +110,26 @@ export function extractFloatData( envMap ) {
|
|
|
93
110
|
|
|
94
111
|
}
|
|
95
112
|
|
|
113
|
+
// Also flag sRGB conversion for DataTextures explicitly marked as sRGB
|
|
114
|
+
if ( ! needsSRGBToLinear && envMap.colorSpace === SRGBColorSpace ) {
|
|
115
|
+
|
|
116
|
+
needsSRGBToLinear = true;
|
|
117
|
+
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Convert sRGB to linear so CDF luminance matches GPU-sampled linear values
|
|
121
|
+
if ( needsSRGBToLinear ) {
|
|
122
|
+
|
|
123
|
+
for ( let i = 0, l = floatData.length; i < l; i += 4 ) {
|
|
124
|
+
|
|
125
|
+
floatData[ i ] = sRGBToLinear( floatData[ i ] );
|
|
126
|
+
floatData[ i + 1 ] = sRGBToLinear( floatData[ i + 1 ] );
|
|
127
|
+
floatData[ i + 2 ] = sRGBToLinear( floatData[ i + 2 ] );
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
}
|
|
132
|
+
|
|
96
133
|
// Remove Y-flip for CDF computation
|
|
97
134
|
if ( envMap.flipY ) {
|
|
98
135
|
|
|
@@ -820,8 +820,8 @@ export class SceneProcessor {
|
|
|
820
820
|
|
|
821
821
|
// Factory method for creating any additional scene elements
|
|
822
822
|
// Currently returns an empty array by default
|
|
823
|
-
const white = new Color( 0xffffff );
|
|
824
|
-
const black = new Color( 0x000000 );
|
|
823
|
+
// const white = new Color( 0xffffff );
|
|
824
|
+
// const black = new Color( 0x000000 );
|
|
825
825
|
return [
|
|
826
826
|
// { position: new Vector3( - 4, 2, 0 ), radius: 0.8, material: { color: white, emissive: black, emissiveIntensity: 0, roughness: 1.0 } },
|
|
827
827
|
// { position: new Vector3( - 1.5, 2, 0 ), radius: 0.8, material: { color: white, emissive: black, emissiveIntensity: 0, roughness: 1.0 } },
|
|
@@ -17,6 +17,7 @@ import { TextureNode } from 'three/webgpu';
|
|
|
17
17
|
import { LinearFilter, DataArrayTexture } from 'three';
|
|
18
18
|
import { pathTracerMain } from '../TSL/PathTracer.js';
|
|
19
19
|
import { setMeshVisibilityBuffer } from '../TSL/BVHTraversal.js';
|
|
20
|
+
import { setShadowAlbedoMaps, setAlphaShadowsUniform } from '../TSL/LightsDirect.js';
|
|
20
21
|
import { BuildTimer } from './BuildTimer.js';
|
|
21
22
|
|
|
22
23
|
const WG_SIZE = 8;
|
|
@@ -192,6 +193,9 @@ export class ShaderBuilder {
|
|
|
192
193
|
// Set per-mesh visibility buffer (module-level in BVHTraversal.js, read during graph construction)
|
|
193
194
|
setMeshVisibilityBuffer( stage.meshVisibilityStorageNode );
|
|
194
195
|
|
|
196
|
+
// Set alpha-shadow uniform (module-level in LightsDirect.js, read at runtime)
|
|
197
|
+
setAlphaShadowsUniform( stage.uniforms.get( 'enableAlphaShadows' ) );
|
|
198
|
+
|
|
195
199
|
const envTex = texture( stage.environment.environmentTexture );
|
|
196
200
|
|
|
197
201
|
// Adaptive sampling texture
|
|
@@ -228,6 +232,11 @@ export class ShaderBuilder {
|
|
|
228
232
|
const emissiveMapsTex = mat.emissiveMaps ? texture( mat.emissiveMaps ) : createArrayPlaceholder();
|
|
229
233
|
const displacementMapsTex = mat.displacementMaps ? texture( mat.displacementMaps ) : createArrayPlaceholder();
|
|
230
234
|
|
|
235
|
+
// Set albedo texture array for alpha-aware shadow rays (module-level in LightsDirect.js).
|
|
236
|
+
// Always pass the texture node (real or placeholder) so alpha-cutout code is emitted
|
|
237
|
+
// into the shader at graph construction time. Runtime albedoMapIndex >= 0 guards sampling.
|
|
238
|
+
setShadowAlbedoMaps( albedoMapsTex );
|
|
239
|
+
|
|
231
240
|
const result = {
|
|
232
241
|
triStorage, bvhStorage, matStorage, emissiveTriStorage, lightBVHStorage,
|
|
233
242
|
envTex, adaptiveSamplingTex, marginalCDFStorage, conditionalCDFStorage,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DataArrayTexture, RGBAFormat, LinearFilter, UnsignedByteType, SRGBColorSpace } from "three";
|
|
2
|
-
import { TEXTURE_CONSTANTS, MEMORY_CONSTANTS, DEFAULT_TEXTURE_MATRIX } from '../EngineDefaults.js';
|
|
2
|
+
import { TEXTURE_CONSTANTS, MEMORY_CONSTANTS, DEFAULT_TEXTURE_MATRIX, MATERIAL_DATA_LAYOUT } from '../EngineDefaults.js';
|
|
3
3
|
|
|
4
4
|
// Canvas pooling for efficient reuse of canvas elements
|
|
5
5
|
class CanvasPool {
|
|
@@ -858,9 +858,9 @@ export class TextureCreator {
|
|
|
858
858
|
*/
|
|
859
859
|
createMaterialRawData( materials ) {
|
|
860
860
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
const dataLengthPerMaterial =
|
|
861
|
+
// Layout is defined by MATERIAL_DATA_LAYOUT in EngineDefaults.js.
|
|
862
|
+
// The inline array below must match that layout exactly (positional order = canonical layout).
|
|
863
|
+
const dataLengthPerMaterial = MATERIAL_DATA_LAYOUT.FLOATS_PER_MATERIAL;
|
|
864
864
|
const totalMaterials = materials.length;
|
|
865
865
|
|
|
866
866
|
const size = totalMaterials * dataLengthPerMaterial;
|
|
@@ -879,19 +879,34 @@ export class TextureCreator {
|
|
|
879
879
|
const bumpMapMatrices = mat.bumpMapMatrices ?? DEFAULT_TEXTURE_MATRIX;
|
|
880
880
|
const displacementMapMatrices = mat.displacementMapMatrices ?? DEFAULT_TEXTURE_MATRIX;
|
|
881
881
|
|
|
882
|
+
// Slot order: shadow/culling → BxDF core → maps → extended → displacement → transforms
|
|
883
|
+
// Must match MATERIAL_DATA_LAYOUT in EngineDefaults.js exactly.
|
|
882
884
|
const materialData = [
|
|
883
|
-
|
|
884
|
-
mat.emissive.r, mat.emissive.g, mat.emissive.b, mat.roughness,
|
|
885
|
+
// Slot 0: shadow core (ior, transmission, thickness, emissiveIntensity)
|
|
885
886
|
mat.ior, mat.transmission, mat.thickness, mat.emissiveIntensity,
|
|
887
|
+
// Slot 1: shadow (attenuationColor, attenuationDistance)
|
|
886
888
|
mat.attenuationColor.r, mat.attenuationColor.g, mat.attenuationColor.b, mat.attenuationDistance,
|
|
889
|
+
// Slot 2: shadow + culling (opacity, side, transparent, alphaTest)
|
|
890
|
+
mat.opacity, mat.side, mat.transparent, mat.alphaTest,
|
|
891
|
+
// Slot 3: shadow (alphaMode, depthWrite, normalScale)
|
|
892
|
+
mat.alphaMode, mat.depthWrite, mat.normalScale?.x ?? 1, mat.normalScale?.y ?? 1,
|
|
893
|
+
// Slot 4: BxDF core (color, metalness)
|
|
894
|
+
mat.color.r, mat.color.g, mat.color.b, mat.metalness,
|
|
895
|
+
// Slot 5: BxDF core (emissive, roughness)
|
|
896
|
+
mat.emissive.r, mat.emissive.g, mat.emissive.b, mat.roughness,
|
|
897
|
+
// Slot 6: map indices A (albedo, normal, roughness, metalness)
|
|
898
|
+
mat.map, mat.normalMap, mat.roughnessMap, mat.metalnessMap,
|
|
899
|
+
// Slot 7: map indices B (emissive, bump, clearcoat, clearcoatRoughness)
|
|
900
|
+
mat.emissiveMap, mat.bumpMap, mat.clearcoat, mat.clearcoatRoughness,
|
|
901
|
+
// Slot 8: extended BxDF (dispersion, visible, sheen, sheenRoughness)
|
|
887
902
|
mat.dispersion, mat.visible, mat.sheen, mat.sheenRoughness,
|
|
903
|
+
// Slot 9: extended BxDF (sheenColor, reserved)
|
|
888
904
|
mat.sheenColor.r, mat.sheenColor.g, mat.sheenColor.b, 1,
|
|
905
|
+
// Slot 10: extended BxDF (specularIntensity, specularColor)
|
|
889
906
|
mat.specularIntensity, mat.specularColor.r, mat.specularColor.g, mat.specularColor.b,
|
|
907
|
+
// Slot 11: extended BxDF (iridescence)
|
|
890
908
|
mat.iridescence, mat.iridescenceIOR, mat.iridescenceThicknessRange[ 0 ], mat.iridescenceThicknessRange[ 1 ],
|
|
891
|
-
|
|
892
|
-
mat.emissiveMap, mat.bumpMap, mat.clearcoat, mat.clearcoatRoughness,
|
|
893
|
-
mat.opacity, mat.side, mat.transparent, mat.alphaTest,
|
|
894
|
-
mat.alphaMode, mat.depthWrite, mat.normalScale?.x ?? 1, mat.normalScale?.y ?? 1,
|
|
909
|
+
// Slot 12: displacement
|
|
895
910
|
mat.bumpScale, mat.displacementScale, mat.displacementMap, 0,
|
|
896
911
|
mapMatrix[ 0 ], mapMatrix[ 1 ], mapMatrix[ 2 ], mapMatrix[ 3 ],
|
|
897
912
|
mapMatrix[ 4 ], mapMatrix[ 5 ], mapMatrix[ 6 ], 1,
|
package/src/RenderSettings.js
CHANGED
|
@@ -31,6 +31,7 @@ const SETTING_ROUTES = {
|
|
|
31
31
|
anamorphicRatio: { uniform: 'anamorphicRatio', reset: true },
|
|
32
32
|
samplingTechnique: { uniform: 'samplingTechnique', reset: true },
|
|
33
33
|
fireflyThreshold: { uniform: 'fireflyThreshold', reset: true },
|
|
34
|
+
enableAlphaShadows: { uniform: 'enableAlphaShadows', reset: true },
|
|
34
35
|
enableEmissiveTriangleSampling: { uniform: 'enableEmissiveTriangleSampling', reset: true },
|
|
35
36
|
emissiveBoost: { uniform: 'emissiveBoost', reset: true },
|
|
36
37
|
visMode: { uniform: 'visMode', reset: true },
|
package/src/TSL/BVHTraversal.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from 'three/tsl';
|
|
27
27
|
|
|
28
28
|
import { Ray, HitInfo } from './Struct.js';
|
|
29
|
-
import { getDatafromStorageBuffer, MATERIAL_SLOTS } from './Common.js';
|
|
29
|
+
import { getDatafromStorageBuffer, MATERIAL_SLOTS, MATERIAL_SLOT } from './Common.js';
|
|
30
30
|
import { RandomPointInCircle } from './Random.js';
|
|
31
31
|
|
|
32
32
|
// ================================================================================
|
|
@@ -136,7 +136,7 @@ const fastRayAABBDst = wgslFn( `
|
|
|
136
136
|
// Per-mesh visibility handled at BLAS-pointer level; material visibility always 1.
|
|
137
137
|
export const passesSideCulling = Fn( ( [ materialIndex, rayDirection, normal, materialBuffer ] ) => {
|
|
138
138
|
|
|
139
|
-
const sideData = getDatafromStorageBuffer( materialBuffer, materialIndex, int(
|
|
139
|
+
const sideData = getDatafromStorageBuffer( materialBuffer, materialIndex, int( MATERIAL_SLOT.OPACITY_ALPHA ), int( MATERIAL_SLOTS ) );
|
|
140
140
|
const side = int( sideData.g );
|
|
141
141
|
const rayDotNormal = rayDirection.dot( normal );
|
|
142
142
|
const doubleSide = side.equal( int( 2 ) );
|
|
@@ -451,6 +451,12 @@ export const traverseBVHShadow = Fn( ( [
|
|
|
451
451
|
closestHit.hitPoint.assign( ray.origin.add( ray.direction.mul( triResult.x ) ) );
|
|
452
452
|
closestHit.normal.assign( normalize( cross( pB.sub( pA ), pC.sub( pA ) ) ) );
|
|
453
453
|
|
|
454
|
+
// Store barycentrics + triangle index for deferred UV computation.
|
|
455
|
+
// Actual UV interpolation happens in traceShadowRay only when
|
|
456
|
+
// the material needs alpha testing — zero overhead for opaque hits.
|
|
457
|
+
closestHit.uv.assign( vec2( triResult.y, triResult.z ) );
|
|
458
|
+
closestHit.triangleIndex.assign( triIndex );
|
|
459
|
+
|
|
454
460
|
// Shadow ray only needs any hit — skip remaining triangles in leaf
|
|
455
461
|
Break();
|
|
456
462
|
|
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
|
|
|
@@ -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 ) ) );
|