rayzee 7.20.1 → 7.20.3
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 +3942 -3087
- package/dist/rayzee.umd.js +87 -117
- package/package.json +3 -2
- package/src/EngineDefaults.js +1 -1
- package/src/Processor/BVHBuilder.js +12 -3
- package/src/Processor/ParallelBVHBuilder.js +3 -0
- package/src/TSL/Clearcoat.js +29 -40
- package/src/TSL/EmissiveSampling.js +8 -7
- package/src/TSL/LightBVHSampling.js +16 -12
- package/src/TSL/LightsCore.js +0 -2
- package/src/TSL/LightsIndirect.js +36 -355
- package/src/TSL/LightsSampling.js +28 -72
- package/src/TSL/MaterialEvaluation.js +44 -71
- package/src/TSL/MaterialProperties.js +209 -176
- package/src/TSL/MaterialTransmission.js +17 -32
- package/src/TSL/PathTracerCore.js +49 -64
- package/src/TSL/Random.js +202 -132
- package/src/TSL/ShadeKernel.js +50 -39
- package/src/TSL/Struct.js +6 -28
- package/src/TSL/Subsurface.js +13 -8
- package/dist/assets/AIUpscalerWorker-ByQj53uK.js.map +0 -1
- package/dist/assets/BVHRefitWorker-WezTgpI5.js.map +0 -1
- package/dist/assets/BVHSubtreeWorker-B98NTgvc.js.map +0 -1
- package/dist/assets/BVHWorker-H923xA44.js.map +0 -1
- package/dist/assets/CDFWorker-CSxzmFhe.js.map +0 -1
- package/dist/assets/TexturesWorker-Dm6RnwKh.js.map +0 -1
- package/dist/rayzee.es.js.map +0 -1
- package/dist/rayzee.umd.js.map +0 -1
package/src/TSL/ShadeKernel.js
CHANGED
|
@@ -27,7 +27,7 @@ import { sampleChromaticCollision, sampleHenyeyGreenstein, subsurfaceCoefficient
|
|
|
27
27
|
import { calculateIndirectLighting } from './LightsIndirect.js';
|
|
28
28
|
import { IndirectLightingResult, sampleCone } from './LightsCore.js';
|
|
29
29
|
import { regularizePathContribution, generateSampledDirection, computeNDCDepth, handleRussianRoulette } from './PathTracerCore.js';
|
|
30
|
-
import {
|
|
30
|
+
import { evaluateDFG } from './MaterialProperties.js';
|
|
31
31
|
import { dielectricF0 } from './Fresnel.js';
|
|
32
32
|
import { sampleClearcoat, ClearcoatResult } from './Clearcoat.js';
|
|
33
33
|
import { refineDisplacedIntersection, DisplacementResult } from './Displacement.js';
|
|
@@ -40,14 +40,13 @@ import {
|
|
|
40
40
|
MaterialSamples,
|
|
41
41
|
ExtMapResult,
|
|
42
42
|
DirectionSample,
|
|
43
|
-
ImportanceSamplingInfo,
|
|
44
43
|
MaterialClassification,
|
|
45
44
|
BRDFWeights,
|
|
46
45
|
MaterialCache,
|
|
47
46
|
DirectLightingDual,
|
|
48
47
|
DFGResult,
|
|
49
48
|
} from './Struct.js';
|
|
50
|
-
import {
|
|
49
|
+
import { getRandomSample, getRandomSample1D, getRandomSample2D, SAMPLER_DIMS_PER_BOUNCE, SAMPLER_DIM_AUX_BASE } from './Random.js';
|
|
51
50
|
import { RAY_FLAG, COUNTER } from '../Processor/QueueManager.js';
|
|
52
51
|
import {
|
|
53
52
|
readRayOrigin, readRayDirection, readRayBounceFlags, readRayThroughput, readRayPdf,
|
|
@@ -114,17 +113,17 @@ export function buildShadeKernel( params ) {
|
|
|
114
113
|
// miss branch and the shadow catcher so their blur stays in lockstep (no horizon seam). normalize() the
|
|
115
114
|
// center direction — ground projection can return a non-unit vector, which would skew sampleCone's basis.
|
|
116
115
|
// Clamp the tap count to ≥1 so a 0 forced via the engine API can't produce a 0/0 NaN backdrop.
|
|
117
|
-
const sampleEnvBlurred = ( centerDir, halfAngle, samples, rng ) => {
|
|
116
|
+
const sampleEnvBlurred = ( centerDir, halfAngle, samples, rng, pixelCoord ) => {
|
|
118
117
|
|
|
119
118
|
const axis = normalize( centerDir ).toVar();
|
|
120
119
|
const n = max( samples, int( 1 ) ).toVar();
|
|
121
120
|
const acc = vec3( 0.0 ).toVar();
|
|
122
|
-
Loop( { start: int( 0 ), end: n, type: 'int', condition: '<' }, () => {
|
|
121
|
+
Loop( { start: int( 0 ), end: n, type: 'int', condition: '<' }, ( { i } ) => {
|
|
123
122
|
|
|
124
|
-
// per
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
const jDir = sampleCone( axis, halfAngle,
|
|
123
|
+
// Tap count varies per pixel, so these cannot sit in a per-bounce block without
|
|
124
|
+
// running over into the next bounce's dimensions — they get the AUX range instead.
|
|
125
|
+
const jXi = getRandomSample2D( pixelCoord, int( 0 ), int( SAMPLER_DIM_AUX_BASE ).add( i ), rng, resolution, frame ).toVar();
|
|
126
|
+
const jDir = sampleCone( axis, halfAngle, jXi ).toVar();
|
|
128
127
|
acc.addAssign( sampleEnvironment( {
|
|
129
128
|
tex: envTexture,
|
|
130
129
|
samp: sampler( envTexture ),
|
|
@@ -189,6 +188,19 @@ export function buildShadeKernel( params ) {
|
|
|
189
188
|
const pixelIndex = rayID;
|
|
190
189
|
const rngState = rngBufferRW.element( rayID ).toVar();
|
|
191
190
|
|
|
191
|
+
// STBN keyed on (GLOBAL pixel, dimension, frame). pixelIndex is the LOCAL path slot; the global pixel
|
|
192
|
+
// = chunkRowBase·W + localSlot, so the blue-noise pattern stays spatially aligned across row-band chunks
|
|
193
|
+
// (and matches Generate's per-pixel RNG seed). chunkRowBase is 0 in the single-chunk case.
|
|
194
|
+
// Hoisted here rather than at the BSDF draw because the ground catcher runs NEE far earlier.
|
|
195
|
+
const _resX = int( resolution.x ).toVar();
|
|
196
|
+
const _globalPixel = int( pixelIndex ).add( chunkRowBase.mul( _resX ) );
|
|
197
|
+
const _pixelCoord = vec2(
|
|
198
|
+
float( _globalPixel.mod( _resX ) ).add( 0.5 ),
|
|
199
|
+
float( _globalPixel.div( _resX ) ).add( 0.5 ),
|
|
200
|
+
);
|
|
201
|
+
// This bounce's block of sampler dimensions — see the budget table in Random.js.
|
|
202
|
+
const dimBase = int( currentBounce ).mul( int( SAMPLER_DIMS_PER_BOUNCE ) );
|
|
203
|
+
|
|
192
204
|
// DDFA see-through aux tint carried across smooth glass/mirror; committed into the OIDN/ASVGF
|
|
193
205
|
// albedo guide at the first diffuse-enough surface (or env). Mutated locally, persisted on every
|
|
194
206
|
// deferring continue (a missed persist = stale tint). 1 = untinted (direct hit → today's albedo).
|
|
@@ -229,11 +241,9 @@ export function buildShadeKernel( params ) {
|
|
|
229
241
|
const planeMat = RayTracingMaterial.wrap( diffuseGroundMaterial() ).toVar();
|
|
230
242
|
|
|
231
243
|
// Cosine-weighted hemisphere BRDF sample about the plane normal (0,1,0); the helper
|
|
232
|
-
// puts cosθ along N, so bDir.y == cosθ.
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
const u2 = RandomValue( rngState ).toVar();
|
|
236
|
-
const bDir = cosineWeightedSample( planeN, vec2( u1, u2 ) ).toVar();
|
|
244
|
+
// puts cosθ along N, so bDir.y == cosθ.
|
|
245
|
+
const bXi = getRandomSample2D( _pixelCoord, int( 0 ), dimBase.add( int( 7 ) ), rngState, resolution, frame ).toVar();
|
|
246
|
+
const bDir = cosineWeightedSample( planeN, bXi ).toVar();
|
|
237
247
|
const bPdf = bDir.y.mul( PI_INV ).toVar(); // cosθ / π
|
|
238
248
|
const bVal = vec3( PI_INV ); // albedo(1) / π
|
|
239
249
|
|
|
@@ -243,6 +253,7 @@ export function buildShadeKernel( params ) {
|
|
|
243
253
|
planePoint, planeN, planeMat, planeV,
|
|
244
254
|
bDir, bPdf, bVal,
|
|
245
255
|
bounceIndex, rngState,
|
|
256
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
246
257
|
directionalLightsBuffer, numDirectionalLights,
|
|
247
258
|
areaLightsBuffer, numAreaLights,
|
|
248
259
|
pointLightsBuffer, numPointLights,
|
|
@@ -289,7 +300,7 @@ export function buildShadeKernel( params ) {
|
|
|
289
300
|
|
|
290
301
|
If( backgroundBlurriness.greaterThan( 0.0 ), () => {
|
|
291
302
|
|
|
292
|
-
envBehind.assign( sampleEnvBlurred( catcherEnvDir, backgroundBlurriness.mul( 1.3 ), backgroundBlurSamples, rngState ) );
|
|
303
|
+
envBehind.assign( sampleEnvBlurred( catcherEnvDir, backgroundBlurriness.mul( 1.3 ), backgroundBlurSamples, rngState, _pixelCoord ) );
|
|
293
304
|
|
|
294
305
|
} ).Else( () => {
|
|
295
306
|
|
|
@@ -373,7 +384,7 @@ export function buildShadeKernel( params ) {
|
|
|
373
384
|
const envColor = vec3( 0.0 ).toVar();
|
|
374
385
|
If( isBackdropView.and( backgroundBlurriness.greaterThan( 0.0 ) ), () => {
|
|
375
386
|
|
|
376
|
-
envColor.assign( sampleEnvBlurred( envDir, backgroundBlurriness.mul( 1.3 ), backgroundBlurSamples, rngState ) );
|
|
387
|
+
envColor.assign( sampleEnvBlurred( envDir, backgroundBlurriness.mul( 1.3 ), backgroundBlurSamples, rngState, _pixelCoord ) );
|
|
377
388
|
|
|
378
389
|
} ).Else( () => {
|
|
379
390
|
|
|
@@ -519,13 +530,14 @@ export function buildShadeKernel( params ) {
|
|
|
519
530
|
const mSigmaT = mSigmaA.add( mSigmaS );
|
|
520
531
|
const coll = CollisionSample.wrap( sampleChromaticCollision(
|
|
521
532
|
mSigmaT, mSigmaS, throughput, hitDist, rngState,
|
|
533
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
522
534
|
) ).toVar();
|
|
523
535
|
throughput.mulAssign( coll.weight );
|
|
524
536
|
|
|
525
537
|
If( coll.didScatter, () => {
|
|
526
538
|
|
|
527
539
|
// scatter via Henyey-Greenstein, continue as a free bounce off the sssSteps budget
|
|
528
|
-
const xi2 =
|
|
540
|
+
const xi2 = getRandomSample2D( _pixelCoord, int( 0 ), dimBase.add( int( 6 ) ), rngState, resolution, frame );
|
|
529
541
|
const scatterPoint = origin.add( direction.mul( coll.t ) );
|
|
530
542
|
const newDir = sampleHenyeyGreenstein( direction, mG, xi2 ).toVar();
|
|
531
543
|
sssSteps.addAssign( 1 );
|
|
@@ -533,7 +545,7 @@ export function buildShadeKernel( params ) {
|
|
|
533
545
|
// terminate walk: step cap or Russian roulette
|
|
534
546
|
const rrP = clamp( max( max( throughput.x, throughput.y ), throughput.z ), 0.02, 1.0 ).toVar();
|
|
535
547
|
const terminate = sssSteps.greaterThanEqual( maxSubsurfaceSteps )
|
|
536
|
-
.or(
|
|
548
|
+
.or( getRandomSample1D( _pixelCoord, int( 0 ), dimBase.add( int( 12 ) ), rngState, resolution, frame ).greaterThan( rrP ) ).toVar();
|
|
537
549
|
|
|
538
550
|
If( terminate, () => {
|
|
539
551
|
|
|
@@ -728,6 +740,7 @@ export function buildShadeKernel( params ) {
|
|
|
728
740
|
const currentRay = Ray( { origin, direction } );
|
|
729
741
|
const interaction = MaterialInteractionResult.wrap( handleMaterialTransparency(
|
|
730
742
|
currentRay, N, material, rngState,
|
|
743
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
731
744
|
int( transTraversals ),
|
|
732
745
|
currentMediumIOR, previousMediumIOR,
|
|
733
746
|
pathWavelength,
|
|
@@ -1047,32 +1060,24 @@ export function buildShadeKernel( params ) {
|
|
|
1047
1060
|
material.clearcoat, material.emissive, material.subsurface,
|
|
1048
1061
|
) ).toVar();
|
|
1049
1062
|
|
|
1050
|
-
// STBN keyed on (GLOBAL pixel, bounceIndex, frame). pixelIndex is the LOCAL path slot; the global pixel
|
|
1051
|
-
// = chunkRowBase·W + localSlot, so the blue-noise pattern stays spatially aligned across row-band chunks
|
|
1052
|
-
// (and matches Generate's per-pixel RNG seed). chunkRowBase is 0 in the single-chunk case.
|
|
1053
|
-
const _resX = int( resolution.x ).toVar();
|
|
1054
|
-
const _globalPixel = int( pixelIndex ).add( chunkRowBase.mul( _resX ) );
|
|
1055
|
-
const _pixelCoord = vec2(
|
|
1056
|
-
float( _globalPixel.mod( _resX ) ).add( 0.5 ),
|
|
1057
|
-
float( _globalPixel.div( _resX ) ).add( 0.5 ),
|
|
1058
|
-
);
|
|
1059
1063
|
const xi = getRandomSample( _pixelCoord, int( 0 ), bounceIndex, rngState, int( - 1 ), resolution, frame ).toVar();
|
|
1064
|
+
const lobeXi = getRandomSample1D( _pixelCoord, int( 0 ), dimBase.add( int( 2 ) ), rngState, resolution, frame ).toVar();
|
|
1060
1065
|
const emptyWeights = BRDFWeights( {
|
|
1061
1066
|
specular: float( 0.0 ), diffuse: float( 0.0 ), sheen: float( 0.0 ),
|
|
1062
1067
|
clearcoat: float( 0.0 ), transmission: float( 0.0 ), iridescence: float( 0.0 ),
|
|
1063
1068
|
} );
|
|
1064
|
-
|
|
1069
|
+
|
|
1070
|
+
// Unused (weightsComputed / materialCacheCached are false) but must satisfy the signature.
|
|
1065
1071
|
const emptyCache = MaterialCache( {
|
|
1066
|
-
|
|
1067
|
-
diffuseColor: vec3( 0.0 ), isPurelyDiffuse: false,
|
|
1068
|
-
alpha: float( 0.0 ), k: float( 0.0 ), alpha2: float( 0.0 ),
|
|
1069
|
-
invRoughness: float( 0.5 ), metalFactor: float( 0.5 ),
|
|
1072
|
+
invRoughness: float( 0.0 ), metalFactor: float( 0.5 ),
|
|
1070
1073
|
iorFactor: float( 0.67 ), maxSheenColor: float( 0.0 ),
|
|
1071
1074
|
} );
|
|
1072
1075
|
|
|
1073
1076
|
const brdfDir = vec3( 0.0 ).toVar();
|
|
1074
1077
|
const brdfValue = vec3( 0.0 ).toVar();
|
|
1075
1078
|
const brdfPdf = float( 0.0 ).toVar();
|
|
1079
|
+
const brdfIsTransmission = tslBool( false ).toVar();
|
|
1080
|
+
const brdfColorWeight = vec3( 1.0 ).toVar();
|
|
1076
1081
|
|
|
1077
1082
|
If( material.clearcoat.greaterThan( 0.0 ), () => {
|
|
1078
1083
|
|
|
@@ -1084,6 +1089,7 @@ export function buildShadeKernel( params ) {
|
|
|
1084
1089
|
} );
|
|
1085
1090
|
const ccResult = ClearcoatResult.wrap( sampleClearcoat(
|
|
1086
1091
|
ccRay, ccHit, material, xi, rngState,
|
|
1092
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
1087
1093
|
) );
|
|
1088
1094
|
brdfDir.assign( ccResult.L );
|
|
1089
1095
|
brdfValue.assign( ccResult.brdf );
|
|
@@ -1092,7 +1098,8 @@ export function buildShadeKernel( params ) {
|
|
|
1092
1098
|
} ).Else( () => {
|
|
1093
1099
|
|
|
1094
1100
|
const bs = DirectionSample.wrap( generateSampledDirection(
|
|
1095
|
-
V, N, material, xi, rngState,
|
|
1101
|
+
V, N, material, xi, lobeXi, rngState,
|
|
1102
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
1096
1103
|
mc,
|
|
1097
1104
|
false, emptyWeights,
|
|
1098
1105
|
false, emptyCache,
|
|
@@ -1100,6 +1107,8 @@ export function buildShadeKernel( params ) {
|
|
|
1100
1107
|
brdfDir.assign( bs.direction );
|
|
1101
1108
|
brdfValue.assign( bs.value );
|
|
1102
1109
|
brdfPdf.assign( bs.pdf );
|
|
1110
|
+
brdfIsTransmission.assign( bs.isTransmission );
|
|
1111
|
+
brdfColorWeight.assign( bs.colorWeight );
|
|
1103
1112
|
|
|
1104
1113
|
} );
|
|
1105
1114
|
|
|
@@ -1107,6 +1116,7 @@ export function buildShadeKernel( params ) {
|
|
|
1107
1116
|
hitPoint, N, material, V,
|
|
1108
1117
|
brdfDir, brdfPdf, brdfValue,
|
|
1109
1118
|
bounceIndex, rngState,
|
|
1119
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
1110
1120
|
directionalLightsBuffer, numDirectionalLights,
|
|
1111
1121
|
areaLightsBuffer, numAreaLights,
|
|
1112
1122
|
pointLightsBuffer, numPointLights,
|
|
@@ -1156,6 +1166,7 @@ export function buildShadeKernel( params ) {
|
|
|
1156
1166
|
const emissiveSample = EmissiveSample.wrap( sampleLightBVHTriangle(
|
|
1157
1167
|
hitPoint, N,
|
|
1158
1168
|
rngState,
|
|
1169
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
1159
1170
|
lightBuffer,
|
|
1160
1171
|
lightBuffer,
|
|
1161
1172
|
emissiveVec4Offset,
|
|
@@ -1216,6 +1227,7 @@ export function buildShadeKernel( params ) {
|
|
|
1216
1227
|
const emissiveLight = calculateEmissiveTriangleContribution(
|
|
1217
1228
|
hitPoint, N, V, material,
|
|
1218
1229
|
bounceIndex, rngState,
|
|
1230
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
1219
1231
|
emissiveBoost,
|
|
1220
1232
|
lightBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower,
|
|
1221
1233
|
triangleBuffer,
|
|
@@ -1244,14 +1256,12 @@ export function buildShadeKernel( params ) {
|
|
|
1244
1256
|
// per-term (env / emissive-hit / direct-light / emissive-NEE), matching the megakernel which never
|
|
1245
1257
|
// re-suppresses the running radiance.
|
|
1246
1258
|
|
|
1247
|
-
const samplingInfo = ImportanceSamplingInfo.wrap( getImportanceSamplingInfo(
|
|
1248
|
-
material, bounceIndex, mc,
|
|
1249
|
-
) ).toVar();
|
|
1250
|
-
|
|
1251
1259
|
const indirectResult = IndirectLightingResult.wrap( calculateIndirectLighting(
|
|
1252
|
-
|
|
1260
|
+
N, material,
|
|
1253
1261
|
brdfDir, brdfPdf, brdfValue,
|
|
1254
|
-
|
|
1262
|
+
brdfIsTransmission, brdfColorWeight,
|
|
1263
|
+
rngState,
|
|
1264
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
1255
1265
|
) ).toVar();
|
|
1256
1266
|
|
|
1257
1267
|
const bounceDir = indirectResult.direction.toVar();
|
|
@@ -1265,6 +1275,7 @@ export function buildShadeKernel( params ) {
|
|
|
1265
1275
|
// terminate. Subsumes the old compensated low-throughput kill (#12). Unbiased; just terminates smarter.
|
|
1266
1276
|
const rrSurvival = handleRussianRoulette(
|
|
1267
1277
|
bounceIndex, throughput, mc, bounceDir, rngState,
|
|
1278
|
+
_pixelCoord, resolution, frame, dimBase,
|
|
1268
1279
|
enableEnvironmentLight, useEnvMapIS,
|
|
1269
1280
|
).toVar();
|
|
1270
1281
|
If( rrSurvival.lessThanEqual( 0.0 ), () => {
|
package/src/TSL/Struct.js
CHANGED
|
@@ -151,6 +151,11 @@ export const DirectionSample = struct( {
|
|
|
151
151
|
direction: 'vec3',
|
|
152
152
|
value: 'vec3',
|
|
153
153
|
pdf: 'float',
|
|
154
|
+
// Refraction went below the surface, so `value` (a reflection-only BRDF eval) is not usable
|
|
155
|
+
// and the consumer must take the transmission throughput path instead.
|
|
156
|
+
isTransmission: 'bool',
|
|
157
|
+
// Spectral tint from the transmission sampler; vec3(1) for every reflection lobe.
|
|
158
|
+
colorWeight: 'vec3',
|
|
154
159
|
} );
|
|
155
160
|
|
|
156
161
|
export const BRDFWeights = struct( {
|
|
@@ -162,12 +167,6 @@ export const BRDFWeights = struct( {
|
|
|
162
167
|
iridescence: 'float',
|
|
163
168
|
} );
|
|
164
169
|
|
|
165
|
-
export const ImportanceSamplingInfo = struct( {
|
|
166
|
-
diffuseImportance: 'float',
|
|
167
|
-
specularImportance: 'float',
|
|
168
|
-
transmissionImportance: 'float',
|
|
169
|
-
clearcoatImportance: 'float',
|
|
170
|
-
} );
|
|
171
170
|
|
|
172
171
|
// Anisotropy tangent frame in world space (rotated ONB). Shared by the sampler and
|
|
173
172
|
// the eval/PDF so both derive the identical frame — see anisoTangentFrame().
|
|
@@ -234,35 +233,14 @@ export const UVCache = struct( {
|
|
|
234
233
|
// Fields are split into two groups:
|
|
235
234
|
// 1. BRDF evaluation: F0, NoV, diffuseColor, isPurelyDiffuse, alpha, k, alpha2
|
|
236
235
|
// 2. BRDF weight calc: invRoughness, metalFactor, iorFactor, maxSheenColor
|
|
236
|
+
// Shared inputs to calculateBRDFWeights, which is the only reader.
|
|
237
237
|
export const MaterialCache = struct( {
|
|
238
|
-
F0: 'vec3', // Base reflectance
|
|
239
|
-
NoV: 'float', // Normal dot View
|
|
240
|
-
diffuseColor: 'vec3', // Precomputed diffuse color (only for isPurelyDiffuse fast path)
|
|
241
|
-
isPurelyDiffuse: 'bool', // Optimized path flag
|
|
242
|
-
alpha: 'float', // roughness squared
|
|
243
|
-
k: 'float', // Geometry term constant
|
|
244
|
-
alpha2: 'float', // roughness to the fourth power
|
|
245
|
-
// BRDF weight calculation: precomputed shared values
|
|
246
238
|
invRoughness: 'float', // 1.0 - roughness
|
|
247
239
|
metalFactor: 'float', // 0.5 + 0.5 * metalness
|
|
248
240
|
iorFactor: 'float', // min(2.0 / ior, 1.0)
|
|
249
241
|
maxSheenColor: 'float', // max component of sheen color
|
|
250
242
|
} );
|
|
251
243
|
|
|
252
|
-
export const SamplingStrategyWeights = struct( {
|
|
253
|
-
envWeight: 'float',
|
|
254
|
-
specularWeight: 'float',
|
|
255
|
-
diffuseWeight: 'float',
|
|
256
|
-
transmissionWeight: 'float',
|
|
257
|
-
clearcoatWeight: 'float',
|
|
258
|
-
totalWeight: 'float',
|
|
259
|
-
|
|
260
|
-
useEnv: 'bool',
|
|
261
|
-
useSpecular: 'bool',
|
|
262
|
-
useDiffuse: 'bool',
|
|
263
|
-
useTransmission: 'bool',
|
|
264
|
-
useClearcoat: 'bool',
|
|
265
|
-
} );
|
|
266
244
|
|
|
267
245
|
// IMPROVEMENT: Dynamic MIS strategy based on material properties
|
|
268
246
|
export const MISStrategy = struct( {
|
package/src/TSL/Subsurface.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import {
|
|
10
10
|
Fn,
|
|
11
11
|
float,
|
|
12
|
-
|
|
12
|
+
int,
|
|
13
13
|
vec3,
|
|
14
14
|
If,
|
|
15
15
|
select,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
|
|
30
30
|
import { struct } from './patches.js';
|
|
31
31
|
import { TWO_PI, EPSILON, constructTBN } from './Common.js';
|
|
32
|
-
import {
|
|
32
|
+
import { getRandomSample1D, getRandomSample2D } from './Random.js';
|
|
33
33
|
import { iorToFresnel0, fresnelSchlickFloat } from './Fresnel.js';
|
|
34
34
|
import { ImportanceSampleGGX } from './MaterialSampling.js';
|
|
35
35
|
|
|
@@ -95,13 +95,16 @@ export const sampleHenyeyGreenstein = Fn( ( [ wi, g, xi ] ) => {
|
|
|
95
95
|
// Per-channel sigma_t can't be represented by one scalar distance. Pick a channel
|
|
96
96
|
// ∝ throughput, sample t against it, and weight by the balance-heuristic combined pdf
|
|
97
97
|
// p̄ = Σ pmf_c·p_c — the shared scalar p̄ is what suppresses color fireflies.
|
|
98
|
-
export const sampleChromaticCollision = Fn( ( [
|
|
98
|
+
export const sampleChromaticCollision = Fn( ( [
|
|
99
|
+
sigmaT, sigmaS, beta, surfaceDist, rngState,
|
|
100
|
+
pixelCoord, resolution, frame, dimBase,
|
|
101
|
+
] ) => {
|
|
99
102
|
|
|
100
103
|
const w = max( beta, vec3( 1e-4 ) ); // floor so no channel goes unsampled
|
|
101
104
|
const pmf = w.div( w.x.add( w.y ).add( w.z ) ).toVar();
|
|
102
105
|
|
|
103
106
|
// .toVar() pins the single RNG draw (else it re-executes per comparison → state drift).
|
|
104
|
-
const u =
|
|
107
|
+
const u = getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 10 ) ), rngState, resolution, frame ).toVar();
|
|
105
108
|
const cSigmaT = float( 0.0 ).toVar();
|
|
106
109
|
If( u.lessThan( pmf.x ), () => {
|
|
107
110
|
|
|
@@ -117,7 +120,7 @@ export const sampleChromaticCollision = Fn( ( [ sigmaT, sigmaS, beta, surfaceDis
|
|
|
117
120
|
|
|
118
121
|
} );
|
|
119
122
|
|
|
120
|
-
const xi =
|
|
123
|
+
const xi = getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 9 ) ), rngState, resolution, frame ).toVar();
|
|
121
124
|
const t = log( max( float( 1.0 ).sub( xi ), 1e-6 ) ).negate().div( max( cSigmaT, 1e-6 ) ).toVar();
|
|
122
125
|
|
|
123
126
|
const didScatter = t.lessThan( surfaceDist ).toVar();
|
|
@@ -167,7 +170,9 @@ export const subsurfaceCoefficients = Fn( ( [ subsurfaceColor, subsurfaceRadius,
|
|
|
167
170
|
// Dielectric interface driven by material.ior: reflect (Fresnel/TIR) or refract across
|
|
168
171
|
// the boundary. No color tint — the scattering color lives in sigma_s.
|
|
169
172
|
export const handleSubsurfaceEntry = Fn( ( [
|
|
170
|
-
rayDir, normal, material, entering, rngState,
|
|
173
|
+
rayDir, normal, material, entering, rngState,
|
|
174
|
+
pixelCoord, resolution, frame, dimBase,
|
|
175
|
+
currentMediumIOR, previousMediumIOR,
|
|
171
176
|
] ) => {
|
|
172
177
|
|
|
173
178
|
const result = SubsurfaceEntryResult( {
|
|
@@ -188,13 +193,13 @@ export const handleSubsurfaceEntry = Fn( ( [
|
|
|
188
193
|
const Fr = select( tir, float( 1.0 ), fresnelSchlickFloat( cosThetaI, F0 ) ).toVar();
|
|
189
194
|
const reflectProb = clamp( Fr, 0.02, 0.98 ).toVar();
|
|
190
195
|
|
|
191
|
-
const doReflect = tir.or(
|
|
196
|
+
const doReflect = tir.or( getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 11 ) ), rngState, resolution, frame ).lessThan( reflectProb ) ).toVar();
|
|
192
197
|
result.didReflect.assign( doReflect );
|
|
193
198
|
|
|
194
199
|
If( doReflect, () => {
|
|
195
200
|
|
|
196
201
|
// GGX-sampled reflection: a perfect mirror here makes SSS surfaces read as polished ceramic.
|
|
197
|
-
const xiR =
|
|
202
|
+
const xiR = getRandomSample2D( pixelCoord, int( 0 ), dimBase.add( int( 5 ) ), rngState, resolution, frame );
|
|
198
203
|
const H = ImportanceSampleGGX( { N, roughness: material.roughness, Xi: xiR } );
|
|
199
204
|
const reflDir = reflect( rayDir, H ).toVar();
|
|
200
205
|
If( dot( reflDir, N ).lessThanEqual( 0.0 ), () => {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AIUpscalerWorker-ByQj53uK.js","names":[],"sources":["../../src/Processor/Workers/AIUpscalerWorker.js"],"sourcesContent":["/**\n * Web Worker for AI Upscaler inference.\n * Handles ONNX model loading and tile-based inference off the main thread.\n *\n * Messages:\n * Main → Worker:\n * { type: 'load', url, sessionOptions } — load/switch model\n * { type: 'infer', tileData, width, height, id } — run inference on a tile\n * { type: 'dispose' } — release session\n *\n * Worker → Main:\n * { type: 'loaded', backend }\n * { type: 'inferred', outputData, id }\n * { type: 'error', message, id? }\n */\n\n// Asset config supplied via 'load' message from the main thread.\n// Defaults match the upstream Rayzee deployment; override via configureAssets().\nlet _assetConfig = {\n\tortRuntimeUrl: 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/ort.webgpu.bundle.min.mjs',\n\tortWasmPaths: 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/',\n\tcacheNamespace: 'rayzee',\n};\n\nlet ort = null;\n\nasync function getOrt() {\n\n\tif ( ort ) return ort;\n\n\tort = await import( /* @vite-ignore */ _assetConfig.ortRuntimeUrl );\n\n\t// WASM paths for CDN delivery — WebGPU EP still uses WASM for lightweight shape ops\n\tort.env.wasm.wasmPaths = _assetConfig.ortWasmPaths;\n\tort.env.logLevel = 'error';\n\n\treturn ort;\n\n}\n\n// IndexedDB names are namespaced so multiple consumers on the same origin don't collide.\nfunction getIdbName() {\n\n\treturn `${_assetConfig.cacheNamespace}:ai-upscaler-models`;\n\n}\n\nconst IDB_STORE = 'models';\n\nlet session = null;\nlet currentModelUrl = null;\n\n// ─── IndexedDB Model Cache ───────────────────────────────────────────────────\n\nfunction openDB() {\n\n\treturn new Promise( ( resolve, reject ) => {\n\n\t\tconst req = indexedDB.open( getIdbName(), 1 );\n\t\treq.onupgradeneeded = () => req.result.createObjectStore( IDB_STORE );\n\t\treq.onsuccess = () => resolve( req.result );\n\t\treq.onerror = () => reject( req.error );\n\n\t} );\n\n}\n\nasync function getCachedModel( url ) {\n\n\ttry {\n\n\t\tconst db = await openDB();\n\t\treturn await new Promise( ( resolve, reject ) => {\n\n\t\t\tconst tx = db.transaction( IDB_STORE, 'readonly' );\n\t\t\tconst req = tx.objectStore( IDB_STORE ).get( url );\n\t\t\treq.onsuccess = () => resolve( req.result || null );\n\t\t\treq.onerror = () => reject( req.error );\n\n\t\t} );\n\n\t} catch {\n\n\t\treturn null;\n\n\t}\n\n}\n\nasync function cacheModel( url, buffer ) {\n\n\ttry {\n\n\t\tconst db = await openDB();\n\t\tawait new Promise( ( resolve, reject ) => {\n\n\t\t\tconst tx = db.transaction( IDB_STORE, 'readwrite' );\n\t\t\ttx.objectStore( IDB_STORE ).put( buffer, url );\n\t\t\ttx.oncomplete = () => resolve();\n\t\t\ttx.onerror = () => reject( tx.error );\n\n\t\t} );\n\n\t} catch {\n\n\t\t// Cache write failure is non-fatal\n\t}\n\n}\n\n// ─── Model Loading ───────────────────────────────────────────────────────────\n\nasync function fetchModel( url ) {\n\n\t// Try IndexedDB cache first\n\tconst cached = await getCachedModel( url );\n\tif ( cached ) {\n\n\t\tconsole.log( `AI Upscaler Worker: model loaded from cache (${( cached.byteLength / 1024 / 1024 ).toFixed( 1 )}MB)` );\n\t\treturn cached;\n\n\t}\n\n\t// Network fetch + cache\n\tconst response = await fetch( url );\n\tif ( ! response.ok ) throw new Error( `Failed to fetch model: ${response.status}` );\n\tconst buffer = await response.arrayBuffer();\n\n\t// Cache in background (don't block session creation)\n\tcacheModel( url, buffer.slice( 0 ) );\n\n\treturn buffer;\n\n}\n\nasync function loadModel( url, sessionOptions ) {\n\n\tif ( session && currentModelUrl === url ) {\n\n\t\tconst backend = 'webgpu';\n\t\tself.postMessage( { type: 'loaded', backend } );\n\t\treturn;\n\n\t}\n\n\t// Dispose previous session\n\tif ( session ) {\n\n\t\tawait session.release();\n\t\tsession = null;\n\n\t}\n\n\tconst [ modelBuffer, ortLib ] = await Promise.all( [ fetchModel( url ), getOrt() ] );\n\n\tsession = await ortLib.InferenceSession.create( modelBuffer, sessionOptions );\n\tcurrentModelUrl = url;\n\n\t// Detect GPU and recommend tile size based on device type\n\tlet tileSize = 512; // default\n\ttry {\n\n\t\tconst adapter = await navigator.gpu?.requestAdapter();\n\t\tconst info = await adapter?.requestAdapterInfo?.() || adapter?.info;\n\t\tconst isMobile = /apple|swiftshader|llvmpipe/i.test( info?.vendor || '' )\n\t\t\t|| /apple|swiftshader/i.test( info?.architecture || '' );\n\t\tconst isIntegrated = info?.device?.toLowerCase?.()?.includes( 'integrated' )\n\t\t\t|| /intel.*iris|intel.*uhd|intel.*hd|amd.*vega|radeon.*graphics/i.test( info?.description || '' );\n\n\t\tif ( isMobile ) {\n\n\t\t\ttileSize = 128;\n\n\t\t} else if ( isIntegrated ) {\n\n\t\t\ttileSize = 256;\n\n\t\t} else {\n\n\t\t\ttileSize = 512;\n\n\t\t}\n\n\t\tconsole.log( `AI Upscaler Worker: GPU=\"${info?.description || info?.device || 'unknown'}\", tileSize=${tileSize}` );\n\n\t} catch { /* fallback to default */ }\n\n\tconst sizeMB = ( modelBuffer.byteLength / 1024 / 1024 ).toFixed( 1 );\n\tconsole.log( `AI Upscaler Worker: model loaded (${sizeMB}MB), backend: webgpu` );\n\n\tself.postMessage( { type: 'loaded', backend: 'webgpu', tileSize } );\n\n}\n\nasync function inferTile( tileData, width, height, id ) {\n\n\tconst ortLib = await getOrt();\n\tconst inputName = session.inputNames[ 0 ];\n\tconst outputName = session.outputNames[ 0 ];\n\tconst inputTensor = new ortLib.Tensor( 'float32', tileData, [ 1, 3, height, width ] );\n\n\tconst results = await session.run( { [ inputName ]: inputTensor } );\n\tconst outputData = results[ outputName ].data;\n\n\t// Transfer the output buffer (zero-copy)\n\tself.postMessage( { type: 'inferred', outputData, id }, [ outputData.buffer ] );\n\n}\n\nself.onmessage = async ( e ) => {\n\n\tconst { type } = e.data;\n\n\ttry {\n\n\t\tif ( type === 'load' ) {\n\n\t\t\t// Apply asset overrides from main thread before any network or cache access\n\t\t\tif ( e.data.ortRuntimeUrl ) _assetConfig.ortRuntimeUrl = e.data.ortRuntimeUrl;\n\t\t\tif ( e.data.ortWasmPaths ) _assetConfig.ortWasmPaths = e.data.ortWasmPaths;\n\t\t\tif ( e.data.cacheNamespace ) _assetConfig.cacheNamespace = e.data.cacheNamespace;\n\t\t\tawait loadModel( e.data.url, e.data.sessionOptions );\n\n\t\t} else if ( type === 'infer' ) {\n\n\t\t\tawait inferTile( e.data.tileData, e.data.width, e.data.height, e.data.id );\n\n\t\t} else if ( type === 'dispose' ) {\n\n\t\t\tif ( session ) {\n\n\t\t\t\tawait session.release();\n\t\t\t\tsession = null;\n\t\t\t\tcurrentModelUrl = null;\n\n\t\t\t}\n\n\t\t}\n\n\t} catch ( error ) {\n\n\t\tself.postMessage( { type: 'error', message: error.message, id: e.data?.id } );\n\n\t}\n\n};\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"BVHRefitWorker-WezTgpI5.js","names":["FLOATS_PER_NODE"],"sources":["../../src/Processor/BVHRefitter.js","../../src/Processor/Workers/BVHRefitWorker.js"],"sourcesContent":["/**\n * BVHRefitter — Fast O(N) bottom-up BVH AABB refit for animated geometry.\n *\n * When mesh topology stays the same but vertex positions change (skeletal animation,\n * morph targets), this avoids the full O(N log N) SAH rebuild by recomputing only\n * the bounding boxes in the existing tree structure.\n *\n * Designed to run in both main thread and Web Worker contexts.\n */\n\n// Inline copy of layout constants (source of truth: EngineDefaults.js).\n// Cannot import because this runs inside Web Workers where window is not defined.\nconst TRIANGLE_DATA_LAYOUT = {\n\tFLOATS_PER_TRIANGLE: 32,\n\tPOSITION_A_OFFSET: 0,\n\tPOSITION_B_OFFSET: 4,\n\tPOSITION_C_OFFSET: 8,\n\tNORMAL_A_OFFSET: 12,\n\tNORMAL_B_OFFSET: 16,\n\tNORMAL_C_OFFSET: 20,\n};\n\nconst FPT = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;\nconst FLOATS_PER_NODE = 16; // 4 vec4s per BVH node\nconst LEAF_MARKER = - 1;\nconst BLAS_POINTER_MARKER = - 2;\n\nexport class BVHRefitter {\n\n\tconstructor() {\n\n\t\t// Reusable bounds buffer — cached across refit calls to avoid allocation per frame.\n\t\t// Resized only when nodeCount changes (i.e., new scene loaded).\n\t\tthis._bounds = null;\n\t\tthis._boundsNodeCount = 0;\n\n\t}\n\n\t/**\n\t * Update triangle positions in the BVH-reordered triangle array.\n\t * Iterates in BVH order (sequential writes, random reads) for cache efficiency.\n\t *\n\t * @param {Float32Array} triangleData - BVH-reordered triangle array (mutated in place)\n\t * @param {Float32Array} newPositions - 9 floats per triangle in ORIGINAL mesh order\n\t * @param {Uint32Array} bvhToOriginal - Map from BVH-order index to original tri index\n\t */\n\tupdateTrianglePositions( triangleData, newPositions, bvhToOriginal ) {\n\n\t\tconst triCount = bvhToOriginal.length;\n\n\t\tfor ( let bvhIdx = 0; bvhIdx < triCount; bvhIdx ++ ) {\n\n\t\t\tconst orig = bvhToOriginal[ bvhIdx ];\n\t\t\tconst dstOff = bvhIdx * FPT; // sequential writes\n\t\t\tconst srcOff = orig * 9;\n\n\t\t\tconst ax = newPositions[ srcOff ];\n\t\t\tconst ay = newPositions[ srcOff + 1 ];\n\t\t\tconst az = newPositions[ srcOff + 2 ];\n\t\t\tconst bx = newPositions[ srcOff + 3 ];\n\t\t\tconst by = newPositions[ srcOff + 4 ];\n\t\t\tconst bz = newPositions[ srcOff + 5 ];\n\t\t\tconst cx = newPositions[ srcOff + 6 ];\n\t\t\tconst cy = newPositions[ srcOff + 7 ];\n\t\t\tconst cz = newPositions[ srcOff + 8 ];\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET ] = ax;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 1 ] = ay;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 2 ] = az;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET ] = bx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 1 ] = by;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 2 ] = bz;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET ] = cx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 1 ] = cy;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 2 ] = cz;\n\n\t\t\t// Compute unnormalized face normal from cross product.\n\t\t\t// Skip sqrt normalization — the path tracer shader normalizes during shading.\n\t\t\tconst abx = bx - ax, aby = by - ay, abz = bz - az;\n\t\t\tconst acx = cx - ax, acy = cy - ay, acz = cz - az;\n\t\t\tconst nx = aby * acz - abz * acy;\n\t\t\tconst ny = abz * acx - abx * acz;\n\t\t\tconst nz = abx * acy - aby * acx;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET ] = nx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 1 ] = ny;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 2 ] = nz;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_B_OFFSET ] = nx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_B_OFFSET + 1 ] = ny;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_B_OFFSET + 2 ] = nz;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET ] = nx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 1 ] = ny;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 2 ] = nz;\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Refit a BLAS sub-range within the combined BVH buffer.\n\t * Same algorithm as refit() but scoped to nodes [startNode, startNode + count).\n\t *\n\t * @param {Float32Array} bvhData - Combined BVH array (TLAS + all BLASes)\n\t * @param {Float32Array} triangleData - Global triangle data\n\t * @param {number} startNode - First node index of this BLAS in bvhData\n\t * @param {number} nodeCount - Number of nodes in this BLAS\n\t */\n\trefitRange( bvhData, triangleData, startNode, nodeCount ) {\n\n\t\t// Grow-only bounds buffer to avoid reallocation on mixed-size BLASes\n\t\tif ( nodeCount > this._boundsNodeCount ) {\n\n\t\t\tthis._bounds = new Float32Array( nodeCount * 6 );\n\t\t\tthis._boundsNodeCount = nodeCount;\n\n\t\t}\n\n\t\tconst bounds = this._bounds;\n\t\tconst endNode = startNode + nodeCount;\n\n\t\tfor ( let i = endNode - 1; i >= startNode; i -- ) {\n\n\t\t\tconst o = i * FLOATS_PER_NODE;\n\t\t\tconst b = ( i - startNode ) * 6; // bounds indexed relative to BLAS start\n\n\t\t\tif ( bvhData[ o + 3 ] === LEAF_MARKER ) {\n\n\t\t\t\tconst triOffset = bvhData[ o ];\n\t\t\t\tconst triCount = bvhData[ o + 1 ];\n\n\t\t\t\tlet minX = Infinity, minY = Infinity, minZ = Infinity;\n\t\t\t\tlet maxX = - Infinity, maxY = - Infinity, maxZ = - Infinity;\n\n\t\t\t\tfor ( let t = 0; t < triCount; t ++ ) {\n\n\t\t\t\t\tconst tOff = ( triOffset + t ) * FPT;\n\t\t\t\t\tconst ax = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET ];\n\t\t\t\t\tconst ay = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 1 ];\n\t\t\t\t\tconst az = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 2 ];\n\t\t\t\t\tconst bx = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET ];\n\t\t\t\t\tconst by = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 1 ];\n\t\t\t\t\tconst bz = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 2 ];\n\t\t\t\t\tconst cx = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET ];\n\t\t\t\t\tconst cy = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 1 ];\n\t\t\t\t\tconst cz = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 2 ];\n\n\t\t\t\t\tminX = Math.min( minX, ax, bx, cx );\n\t\t\t\t\tminY = Math.min( minY, ay, by, cy );\n\t\t\t\t\tminZ = Math.min( minZ, az, bz, cz );\n\t\t\t\t\tmaxX = Math.max( maxX, ax, bx, cx );\n\t\t\t\t\tmaxY = Math.max( maxY, ay, by, cy );\n\t\t\t\t\tmaxZ = Math.max( maxZ, az, bz, cz );\n\n\t\t\t\t}\n\n\t\t\t\tbounds[ b ] = minX;\n\t\t\t\tbounds[ b + 1 ] = minY;\n\t\t\t\tbounds[ b + 2 ] = minZ;\n\t\t\t\tbounds[ b + 3 ] = maxX;\n\t\t\t\tbounds[ b + 4 ] = maxY;\n\t\t\t\tbounds[ b + 5 ] = maxZ;\n\n\t\t\t} else {\n\n\t\t\t\t// Inner node — child indices are absolute, but bounds index relative to startNode\n\t\t\t\tconst leftIdx = bvhData[ o + 3 ];\n\t\t\t\tconst rightIdx = bvhData[ o + 7 ];\n\t\t\t\tconst lb = ( leftIdx - startNode ) * 6;\n\t\t\t\tconst rb = ( rightIdx - startNode ) * 6;\n\n\t\t\t\tconst lMinX = bounds[ lb ];\n\t\t\t\tconst lMinY = bounds[ lb + 1 ];\n\t\t\t\tconst lMinZ = bounds[ lb + 2 ];\n\t\t\t\tconst lMaxX = bounds[ lb + 3 ];\n\t\t\t\tconst lMaxY = bounds[ lb + 4 ];\n\t\t\t\tconst lMaxZ = bounds[ lb + 5 ];\n\n\t\t\t\tconst rMinX = bounds[ rb ];\n\t\t\t\tconst rMinY = bounds[ rb + 1 ];\n\t\t\t\tconst rMinZ = bounds[ rb + 2 ];\n\t\t\t\tconst rMaxX = bounds[ rb + 3 ];\n\t\t\t\tconst rMaxY = bounds[ rb + 4 ];\n\t\t\t\tconst rMaxZ = bounds[ rb + 5 ];\n\n\t\t\t\tbvhData[ o ] = lMinX;\n\t\t\t\tbvhData[ o + 1 ] = lMinY;\n\t\t\t\tbvhData[ o + 2 ] = lMinZ;\n\t\t\t\tbvhData[ o + 4 ] = lMaxX;\n\t\t\t\tbvhData[ o + 5 ] = lMaxY;\n\t\t\t\tbvhData[ o + 6 ] = lMaxZ;\n\n\t\t\t\tbvhData[ o + 8 ] = rMinX;\n\t\t\t\tbvhData[ o + 9 ] = rMinY;\n\t\t\t\tbvhData[ o + 10 ] = rMinZ;\n\t\t\t\tbvhData[ o + 12 ] = rMaxX;\n\t\t\t\tbvhData[ o + 13 ] = rMaxY;\n\t\t\t\tbvhData[ o + 14 ] = rMaxZ;\n\n\t\t\t\tbounds[ b ] = Math.min( lMinX, rMinX );\n\t\t\t\tbounds[ b + 1 ] = Math.min( lMinY, rMinY );\n\t\t\t\tbounds[ b + 2 ] = Math.min( lMinZ, rMinZ );\n\t\t\t\tbounds[ b + 3 ] = Math.max( lMaxX, rMaxX );\n\t\t\t\tbounds[ b + 4 ] = Math.max( lMaxY, rMaxY );\n\t\t\t\tbounds[ b + 5 ] = Math.max( lMaxZ, rMaxZ );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Bottom-up refit of all BVH node AABBs.\n\t * Reverse pre-order iteration gives valid bottom-up order (children have higher\n\t * indices than parents in pre-order, so reversing processes children first).\n\t *\n\t * @param {Float32Array} bvhData - Flat BVH array (mutated in place)\n\t * @param {Float32Array} triangleData - Updated triangle data\n\t * @param {number} nodeCount - Total number of BVH nodes\n\t */\n\trefit( bvhData, triangleData, nodeCount ) {\n\n\t\t// Reuse bounds buffer across frames (reallocate only on scene change)\n\t\tif ( nodeCount !== this._boundsNodeCount ) {\n\n\t\t\tthis._bounds = new Float32Array( nodeCount * 6 );\n\t\t\tthis._boundsNodeCount = nodeCount;\n\n\t\t}\n\n\t\tconst bounds = this._bounds;\n\n\t\t// Reverse iteration: bottom-up in pre-order layout\n\t\tfor ( let i = nodeCount - 1; i >= 0; i -- ) {\n\n\t\t\tconst o = i * FLOATS_PER_NODE;\n\t\t\tconst b = i * 6;\n\n\t\t\tconst marker = bvhData[ o + 3 ];\n\n\t\t\tif ( marker === LEAF_MARKER ) {\n\n\t\t\t\t// Triangle leaf: compute AABB from triangles\n\t\t\t\tconst triOffset = bvhData[ o ];\n\t\t\t\tconst triCount = bvhData[ o + 1 ];\n\n\t\t\t\tlet minX = Infinity, minY = Infinity, minZ = Infinity;\n\t\t\t\tlet maxX = - Infinity, maxY = - Infinity, maxZ = - Infinity;\n\n\t\t\t\tfor ( let t = 0; t < triCount; t ++ ) {\n\n\t\t\t\t\tconst tOff = ( triOffset + t ) * FPT;\n\n\t\t\t\t\t// Position A\n\t\t\t\t\tconst ax = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET ];\n\t\t\t\t\tconst ay = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 1 ];\n\t\t\t\t\tconst az = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 2 ];\n\t\t\t\t\t// Position B\n\t\t\t\t\tconst bx = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET ];\n\t\t\t\t\tconst by = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 1 ];\n\t\t\t\t\tconst bz = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 2 ];\n\t\t\t\t\t// Position C\n\t\t\t\t\tconst cx = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET ];\n\t\t\t\t\tconst cy = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 1 ];\n\t\t\t\t\tconst cz = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 2 ];\n\n\t\t\t\t\tminX = Math.min( minX, ax, bx, cx );\n\t\t\t\t\tminY = Math.min( minY, ay, by, cy );\n\t\t\t\t\tminZ = Math.min( minZ, az, bz, cz );\n\t\t\t\t\tmaxX = Math.max( maxX, ax, bx, cx );\n\t\t\t\t\tmaxY = Math.max( maxY, ay, by, cy );\n\t\t\t\t\tmaxZ = Math.max( maxZ, az, bz, cz );\n\n\t\t\t\t}\n\n\t\t\t\tbounds[ b ] = minX;\n\t\t\t\tbounds[ b + 1 ] = minY;\n\t\t\t\tbounds[ b + 2 ] = minZ;\n\t\t\t\tbounds[ b + 3 ] = maxX;\n\t\t\t\tbounds[ b + 4 ] = maxY;\n\t\t\t\tbounds[ b + 5 ] = maxZ;\n\n\t\t\t} else if ( marker === BLAS_POINTER_MARKER ) {\n\n\t\t\t\t// BLAS-pointer leaf (TLAS): read BLAS root node's bounds (already computed)\n\t\t\t\tconst blasRoot = bvhData[ o ];\n\t\t\t\tconst br = blasRoot * 6;\n\t\t\t\tbounds[ b ] = bounds[ br ];\n\t\t\t\tbounds[ b + 1 ] = bounds[ br + 1 ];\n\t\t\t\tbounds[ b + 2 ] = bounds[ br + 2 ];\n\t\t\t\tbounds[ b + 3 ] = bounds[ br + 3 ];\n\t\t\t\tbounds[ b + 4 ] = bounds[ br + 4 ];\n\t\t\t\tbounds[ b + 5 ] = bounds[ br + 5 ];\n\n\t\t\t} else {\n\n\t\t\t\t// Inner node: union children bounds (already computed since we iterate in reverse)\n\t\t\t\tconst leftIdx = bvhData[ o + 3 ];\n\t\t\t\tconst rightIdx = bvhData[ o + 7 ];\n\t\t\t\tconst lb = leftIdx * 6;\n\t\t\t\tconst rb = rightIdx * 6;\n\n\t\t\t\tconst lMinX = bounds[ lb ];\n\t\t\t\tconst lMinY = bounds[ lb + 1 ];\n\t\t\t\tconst lMinZ = bounds[ lb + 2 ];\n\t\t\t\tconst lMaxX = bounds[ lb + 3 ];\n\t\t\t\tconst lMaxY = bounds[ lb + 4 ];\n\t\t\t\tconst lMaxZ = bounds[ lb + 5 ];\n\n\t\t\t\tconst rMinX = bounds[ rb ];\n\t\t\t\tconst rMinY = bounds[ rb + 1 ];\n\t\t\t\tconst rMinZ = bounds[ rb + 2 ];\n\t\t\t\tconst rMaxX = bounds[ rb + 3 ];\n\t\t\t\tconst rMaxY = bounds[ rb + 4 ];\n\t\t\t\tconst rMaxZ = bounds[ rb + 5 ];\n\n\t\t\t\t// Write left child AABB into bvhData\n\t\t\t\tbvhData[ o ] = lMinX;\n\t\t\t\tbvhData[ o + 1 ] = lMinY;\n\t\t\t\tbvhData[ o + 2 ] = lMinZ;\n\t\t\t\t// o+3 = leftChildIdx (preserved)\n\t\t\t\tbvhData[ o + 4 ] = lMaxX;\n\t\t\t\tbvhData[ o + 5 ] = lMaxY;\n\t\t\t\tbvhData[ o + 6 ] = lMaxZ;\n\t\t\t\t// o+7 = rightChildIdx (preserved)\n\n\t\t\t\t// Write right child AABB into bvhData\n\t\t\t\tbvhData[ o + 8 ] = rMinX;\n\t\t\t\tbvhData[ o + 9 ] = rMinY;\n\t\t\t\tbvhData[ o + 10 ] = rMinZ;\n\t\t\t\t// o+11 = 0 padding\n\t\t\t\tbvhData[ o + 12 ] = rMaxX;\n\t\t\t\tbvhData[ o + 13 ] = rMaxY;\n\t\t\t\tbvhData[ o + 14 ] = rMaxZ;\n\t\t\t\t// o+15 = 0 padding\n\n\t\t\t\t// Store this node's bounds as union of children\n\t\t\t\tbounds[ b ] = Math.min( lMinX, rMinX );\n\t\t\t\tbounds[ b + 1 ] = Math.min( lMinY, rMinY );\n\t\t\t\tbounds[ b + 2 ] = Math.min( lMinZ, rMinZ );\n\t\t\t\tbounds[ b + 3 ] = Math.max( lMaxX, rMaxX );\n\t\t\t\tbounds[ b + 4 ] = Math.max( lMaxY, rMaxY );\n\t\t\t\tbounds[ b + 5 ] = Math.max( lMaxZ, rMaxZ );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n","/**\n * BVHRefitWorker — Off-main-thread BVH refit using SharedArrayBuffer.\n *\n * Protocol:\n * 'init' → receives SharedArrayBuffers + index map (once per scene)\n * 'refit' → reads shared positions, writes shared bvh/tri data (per frame)\n */\n\nimport { BVHRefitter } from '../BVHRefitter.js';\n\nconst FLOATS_PER_NODE = 16;\nconst refitter = new BVHRefitter();\n\n// Cached shared memory views (set once on 'init', reused every frame)\nlet bvhData = null;\nlet triData = null;\nlet posData = null;\nlet bvhToOriginal = null;\nlet nodeCount = 0;\n\nself.onmessage = function ( e ) {\n\n\tconst { type } = e.data;\n\n\tif ( type === 'init' ) {\n\n\t\tbvhData = new Float32Array( e.data.sharedBvhBuf );\n\t\ttriData = new Float32Array( e.data.sharedTriBuf );\n\t\tposData = new Float32Array( e.data.sharedPosBuf );\n\t\tbvhToOriginal = e.data.bvhToOriginal; // transferred Uint32Array\n\t\tnodeCount = bvhData.length / FLOATS_PER_NODE;\n\t\treturn;\n\n\t}\n\n\tif ( type === 'refit' ) {\n\n\t\ttry {\n\n\t\t\tconst startTime = performance.now();\n\n\t\t\trefitter.updateTrianglePositions( triData, posData, bvhToOriginal );\n\t\t\trefitter.refit( bvhData, triData, nodeCount );\n\n\t\t\tself.postMessage( {\n\t\t\t\ttype: 'refitComplete',\n\t\t\t\trefitTimeMs: performance.now() - startTime\n\t\t\t} );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( '[BVHRefitWorker] Refit error:', error );\n\t\t\tself.postMessage( { type: 'error', error: error.message } );\n\n\t\t}\n\n\t}\n\n};\n"],"mappings":"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"}
|