rayzee 7.16.0 → 7.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/assets/BVHSubtreeWorker-D7KMbrQ-.js +2 -0
- package/dist/assets/BVHSubtreeWorker-D7KMbrQ-.js.map +1 -0
- package/dist/assets/BVHWorker-DOaGn6uN.js +2 -0
- package/dist/assets/BVHWorker-DOaGn6uN.js.map +1 -0
- package/dist/rayzee.es.js +2817 -2537
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +55 -55
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +27 -11
- package/src/Passes/OIDNDenoiser.js +17 -14
- package/src/PathTracerApp.js +86 -13
- package/src/Processor/BVHBuilder.js +11 -7
- package/src/Processor/BuildTimer.js +20 -6
- package/src/Processor/EmissiveTriangleBuilder.js +11 -7
- package/src/Processor/GeometryExtractor.js +11 -5
- package/src/Processor/KernelManager.js +48 -1
- package/src/Processor/PackedRayBuffer.js +69 -35
- package/src/Processor/ParallelBVHBuilder.js +9 -6
- package/src/Processor/QueueManager.js +6 -8
- package/src/Processor/SceneProcessor.js +47 -21
- package/src/Processor/ShaderBuilder.js +4 -1
- package/src/Processor/StorageTexturePool.js +4 -1
- package/src/Processor/Workers/BVHWorker.js +12 -6
- package/src/RenderSettings.js +1 -1
- package/src/Stages/ASVGF.js +38 -0
- package/src/Stages/BilateralFilter.js +23 -0
- package/src/Stages/EdgeFilter.js +18 -0
- package/src/Stages/MotionVector.js +23 -0
- package/src/Stages/NormalDepth.js +22 -0
- package/src/Stages/PathTracer.js +324 -169
- package/src/Stages/PathTracerStage.js +17 -14
- package/src/Stages/Variance.js +23 -0
- package/src/TSL/FinalWriteKernel.js +30 -18
- package/src/TSL/GenerateKernel.js +28 -17
- package/src/TSL/ShadeKernel.js +186 -87
- package/src/TSL/TextureSampling.js +12 -2
- package/src/index.js +3 -0
- package/src/managers/EnvironmentManager.js +12 -8
- package/src/managers/MaterialDataManager.js +12 -9
- package/src/managers/UniformManager.js +1 -1
- package/src/utils/Logger.js +291 -0
- package/dist/assets/BVHSubtreeWorker-sNzvxn66.js +0 -2
- package/dist/assets/BVHSubtreeWorker-sNzvxn66.js.map +0 -1
- package/dist/assets/BVHWorker-CiVdFrwe.js +0 -2
- package/dist/assets/BVHWorker-CiVdFrwe.js.map +0 -1
package/src/TSL/ShadeKernel.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import {
|
|
8
8
|
Fn, float, vec2, vec3, vec4, int, uint,
|
|
9
9
|
bool as tslBool,
|
|
10
|
-
If, Loop, normalize, max, exp, log, clamp, dot, length, select,
|
|
10
|
+
If, Loop, normalize, max, exp, log, clamp, dot, length, select, smoothstep, mix,
|
|
11
11
|
instanceIndex,
|
|
12
12
|
sampler,
|
|
13
13
|
atomicAdd, atomicLoad, atomicStore, uintBitsToFloat,
|
|
@@ -27,7 +27,8 @@ 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 { getImportanceSamplingInfo } from './MaterialProperties.js';
|
|
30
|
+
import { getImportanceSamplingInfo, evaluateDFG } from './MaterialProperties.js';
|
|
31
|
+
import { dielectricF0 } from './Fresnel.js';
|
|
31
32
|
import { sampleClearcoat, ClearcoatResult } from './Clearcoat.js';
|
|
32
33
|
import { refineDisplacedIntersection, DisplacementResult } from './Displacement.js';
|
|
33
34
|
import { calculateEmissiveTriangleContribution, calculateEmissiveLightPdf, EmissiveSample } from './EmissiveSampling.js';
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
BRDFWeights,
|
|
45
46
|
MaterialCache,
|
|
46
47
|
DirectLightingDual,
|
|
48
|
+
DFGResult,
|
|
47
49
|
} from './Struct.js';
|
|
48
50
|
import { RandomValue, getRandomSample } from './Random.js';
|
|
49
51
|
import { RAY_FLAG, COUNTER } from '../Processor/QueueManager.js';
|
|
@@ -51,11 +53,13 @@ import {
|
|
|
51
53
|
readRayOrigin, readRayDirection, readRayBounceFlags, readRayThroughput, readRayPdf,
|
|
52
54
|
readMediumStack, writeMediumStack, readMediumSigmaA, writeMediumSigmaA,
|
|
53
55
|
readPathBounces, readSssSteps, readSSSMedium, writeSSSMedium,
|
|
56
|
+
readTransparentCount,
|
|
54
57
|
readHitDistance, readHitBarycentrics, readHitNormal,
|
|
55
58
|
readHitMaterialIndex, readHitTriangleIndex,
|
|
56
59
|
writeRayOriginMeta, writeRayDirFlags, writeRayThroughputPdf, writeRayRadiance,
|
|
57
|
-
writeGBuffer, readGBuffer, gbDecodeNormalDepth,
|
|
60
|
+
writeGBuffer, readGBuffer, gbDecodeNormalDepth,
|
|
58
61
|
readRayRadiance,
|
|
62
|
+
readFeatureThroughput, writeFeatureThroughput,
|
|
59
63
|
} from '../Processor/PackedRayBuffer.js';
|
|
60
64
|
|
|
61
65
|
const WG_SIZE = 256;
|
|
@@ -85,11 +89,13 @@ export function buildShadeKernel( params ) {
|
|
|
85
89
|
pointLightsBuffer, numPointLights,
|
|
86
90
|
spotLightsBuffer, numSpotLights,
|
|
87
91
|
maxBounceCount, maxSubsurfaceSteps,
|
|
92
|
+
maxTransparentBounces, // guard on alpha-skip depth; counter in ORIGIN_META.w
|
|
88
93
|
currentBounce, // loop iteration = path length (advances on free bounces); drives RR/firefly/giScale
|
|
89
94
|
transparentBackground, backgroundIntensity, backgroundColor, backgroundBlurriness, backgroundBlurSamples, showBackground,
|
|
90
95
|
globalIlluminationIntensity,
|
|
91
96
|
cameraProjectionMatrix, cameraViewMatrix,
|
|
92
97
|
fireflyThreshold, frame, resolution,
|
|
98
|
+
chunkRowBase, // chunked path pool: global pixel = chunkRowBase·W + localSlot (rayID). See spec.
|
|
93
99
|
emissiveTriangleCount, emissiveVec4Offset, emissiveTotalPower,
|
|
94
100
|
emissiveBoost, totalTriangleCount, enableEmissiveTriangleSampling,
|
|
95
101
|
lightBVHNodeCount, reverseMapVec4Offset,
|
|
@@ -183,6 +189,11 @@ export function buildShadeKernel( params ) {
|
|
|
183
189
|
const pixelIndex = rayID;
|
|
184
190
|
const rngState = rngBufferRW.element( rayID ).toVar();
|
|
185
191
|
|
|
192
|
+
// DDFA see-through aux tint carried across smooth glass/mirror; committed into the OIDN/ASVGF
|
|
193
|
+
// albedo guide at the first diffuse-enough surface (or env). Mutated locally, persisted on every
|
|
194
|
+
// deferring continue (a missed persist = stale tint). 1 = untinted (direct hit → today's albedo).
|
|
195
|
+
const featCarry = readFeatureThroughput( rayBufferRW, rayID ).toVar();
|
|
196
|
+
|
|
186
197
|
const hitDist = readHitDistance( hitBufferRO, rayID ).toVar();
|
|
187
198
|
const hitNormal = readHitNormal( hitBufferRO, rayID ).toVar();
|
|
188
199
|
// hitInfo.uv is the interpolated texture UV (not barycentrics)
|
|
@@ -195,6 +206,7 @@ export function buildShadeKernel( params ) {
|
|
|
195
206
|
// path length = loop iteration (advances every bounce incl. transmissive/SSS); drives RR/firefly/giScale/MIS. Megakernel: loop counter i.
|
|
196
207
|
const bounceIndex = int( currentBounce ).toVar();
|
|
197
208
|
const sssSteps = readSssSteps( rayBufferRW, rayID ).toVar();
|
|
209
|
+
const transparentCount = readTransparentCount( rayBufferRW, rayID ).toVar();
|
|
198
210
|
|
|
199
211
|
// ── Analytic ground-plane shadow catcher (primary ray only, no geometry) ──
|
|
200
212
|
// A horizontal plane at y = groundCatcherHeight. For a bounce-0 ray that crosses it
|
|
@@ -309,7 +321,8 @@ export function buildShadeKernel( params ) {
|
|
|
309
321
|
} );
|
|
310
322
|
|
|
311
323
|
writeRayRadiance( rayBufferRW, rayID, vec4( outRgb, outAlpha ) );
|
|
312
|
-
|
|
324
|
+
// DDFA: the plane's aux (written above) is a committed surface — lock it (cosmetic; ray dies here).
|
|
325
|
+
writeRayDirFlags( rayBufferRW, rayID, direction, flags.bitOr( uint( RAY_FLAG.AUX_LOCKED ) ).bitAnd( uint( ~ RAY_FLAG.ACTIVE ) ) );
|
|
313
326
|
rngBufferRW.element( rayID ).assign( rngState );
|
|
314
327
|
Return();
|
|
315
328
|
|
|
@@ -333,9 +346,8 @@ export function buildShadeKernel( params ) {
|
|
|
333
346
|
const wantBackdrop = isBackdropView.and( showBackground ); // draw env image as backdrop
|
|
334
347
|
const wantEnvLight = isBackdropView.not().and( enableEnvironmentLight ); // env as light on redirected bounces
|
|
335
348
|
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
// before the miss Return below, but only when the pixel still carries the black bounce-0 default.
|
|
349
|
+
// DDFA: env colour a redirected ray escaping to the environment sees (e.g. smooth glass over
|
|
350
|
+
// sky). Committed (tinted by featCarry) into the aux G-buffer before the miss Return below.
|
|
339
351
|
const escapedAuxAlbedo = vec3( 0.0 ).toVar();
|
|
340
352
|
|
|
341
353
|
If( wantBackdrop.or( wantEnvLight ), () => {
|
|
@@ -376,8 +388,8 @@ export function buildShadeKernel( params ) {
|
|
|
376
388
|
|
|
377
389
|
} );
|
|
378
390
|
|
|
379
|
-
//
|
|
380
|
-
//
|
|
391
|
+
// DDFA: remember the (clamped) env colour this escaped ray sees, for the redirected-escape
|
|
392
|
+
// aux commit below (tinted by the accumulated see-through throughput).
|
|
381
393
|
escapedAuxAlbedo.assign( clamp( envColor, vec3( 0.0 ), vec3( 1.0 ) ) );
|
|
382
394
|
|
|
383
395
|
// MIS weight for implicit env hit — prevents double-counting with NEE
|
|
@@ -447,22 +459,18 @@ export function buildShadeKernel( params ) {
|
|
|
447
459
|
|
|
448
460
|
} );
|
|
449
461
|
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
//
|
|
462
|
+
// DDFA: a redirected ray (specular chain — glass/mirror) that escaped to the environment without
|
|
463
|
+
// ever committing a surface guide commits the env colour it sees, tinted by the accumulated
|
|
464
|
+
// see-through throughput (red glass over sky → sky×red). Normal = the escaped direction (varies
|
|
465
|
+
// per pixel → OIDN sees structure tracking the refracted view); depth kept at the primary hit.
|
|
466
|
+
// AUX_LOCKED-clear alone means "never committed" — the old black-probe is redundant. A direct
|
|
467
|
+
// backdrop (REDIRECTED clear) keeps Generate's black/far default (regression-safe sky guide).
|
|
456
468
|
If( auxOn
|
|
457
469
|
.and( flags.bitAnd( uint( RAY_FLAG.AUX_LOCKED ) ).equal( uint( 0 ) ) )
|
|
458
470
|
.and( flags.bitAnd( uint( RAY_FLAG.REDIRECTED ) ).notEqual( uint( 0 ) ) ), () => {
|
|
459
471
|
|
|
460
472
|
const gPrev = readGBuffer( gBufferRW, pixelIndex );
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
writeGBuffer( gBufferRW, pixelIndex, normalize( direction ), gbDecodeNormalDepth( gPrev ).w, escapedAuxAlbedo );
|
|
464
|
-
|
|
465
|
-
} );
|
|
473
|
+
writeGBuffer( gBufferRW, pixelIndex, normalize( direction ), gbDecodeNormalDepth( gPrev ).w, clamp( escapedAuxAlbedo.mul( featCarry ), vec3( 0.0 ), vec3( 1.0 ) ) );
|
|
466
474
|
|
|
467
475
|
} );
|
|
468
476
|
|
|
@@ -496,7 +504,14 @@ export function buildShadeKernel( params ) {
|
|
|
496
504
|
If( max( max( mSigmaS.x, mSigmaS.y ), mSigmaS.z ).lessThanEqual( 0.0 ), () => {
|
|
497
505
|
|
|
498
506
|
// glass: Beer-Lambert absorption
|
|
499
|
-
|
|
507
|
+
const beer = exp( mSigmaA.mul( hitDist ).negate() ).toVar();
|
|
508
|
+
throughput.mulAssign( beer );
|
|
509
|
+
// DDFA: colored-glass volume tints the deferred aux guide by the same absorption.
|
|
510
|
+
If( auxOn.and( flags.bitAnd( uint( RAY_FLAG.AUX_LOCKED ) ).equal( uint( 0 ) ) ), () => {
|
|
511
|
+
|
|
512
|
+
featCarry.mulAssign( beer );
|
|
513
|
+
|
|
514
|
+
} );
|
|
500
515
|
|
|
501
516
|
} ).Else( () => {
|
|
502
517
|
|
|
@@ -534,7 +549,7 @@ export function buildShadeKernel( params ) {
|
|
|
534
549
|
// free-bounce continuation: ray stays in the same medium, so medium stack + coeffs persist
|
|
535
550
|
// SSS scatter changes direction → no longer the direct backdrop view.
|
|
536
551
|
flags.assign( flags.bitOr( uint( RAY_FLAG.REDIRECTED ) ) );
|
|
537
|
-
writeRayOriginMeta( rayBufferRW, rayID, scatterPoint, cameraDepth, sssSteps );
|
|
552
|
+
writeRayOriginMeta( rayBufferRW, rayID, scatterPoint, cameraDepth, sssSteps, transparentCount );
|
|
538
553
|
writeRayDirFlags( rayBufferRW, rayID, newDir, flags );
|
|
539
554
|
// Free bounce: preserve prevBouncePdf (megakernel leaves it untouched across SSS scatter,
|
|
540
555
|
// PathTracerCore.js:1272 sets it only after an opaque scatter). Writing 1.0 here spuriously
|
|
@@ -585,6 +600,10 @@ export function buildShadeKernel( params ) {
|
|
|
585
600
|
material, samplingUV, N,
|
|
586
601
|
) ).toVar();
|
|
587
602
|
|
|
603
|
+
// DDFA: snapshot the UNCLAMPED roughness (before the min-0.05 clamp below) for the specular↔diffuse
|
|
604
|
+
// classification, so a perfect mirror reads 0 (fully specular → defers).
|
|
605
|
+
const rawRough = matSamples.roughness.toVar();
|
|
606
|
+
|
|
588
607
|
// BRDF functions read material.color/metalness/roughness, so apply samples here
|
|
589
608
|
material.color.assign( matSamples.albedo );
|
|
590
609
|
material.metalness.assign( matSamples.metalness.clamp( 0.0, 1.0 ) );
|
|
@@ -635,36 +654,55 @@ export function buildShadeKernel( params ) {
|
|
|
635
654
|
|
|
636
655
|
} );
|
|
637
656
|
|
|
638
|
-
//
|
|
639
|
-
|
|
657
|
+
// ─── DDFA classification (Cycles smoothstep-by-roughness, not a hard binary) ───
|
|
658
|
+
// nonspec fraction over a 2-pseudo-closure model: a Lambert closure (weight 1-metal, fully diffuse)
|
|
659
|
+
// + a glossy/glass closure (weight metal·(1-trans)+trans, classified by roughness). Commit the aux at
|
|
660
|
+
// the first surface with a meaningful diffuse response (nonspec≥0.25); smooth glass/mirror defers.
|
|
661
|
+
// featPrefix = the tint accumulated BEFORE this surface (read-only for this bounce's commits).
|
|
662
|
+
const metal = material.metalness.toVar();
|
|
663
|
+
const trans = material.transmission.toVar();
|
|
664
|
+
const roughFrac = smoothstep( float( 0.0 ), float( 0.15 ), rawRough ).toVar();
|
|
665
|
+
const auxCommit = clamp(
|
|
666
|
+
float( 1.0 ).sub( metal ).mul( float( 1.0 ).sub( trans ) )
|
|
667
|
+
.add( roughFrac.mul( metal.mul( float( 1.0 ).sub( trans ) ).add( trans ) ) ),
|
|
668
|
+
0.0, 1.0,
|
|
669
|
+
).greaterThanEqual( float( 0.25 ) ).toVar();
|
|
670
|
+
const featPrefix = featCarry.toVar();
|
|
671
|
+
|
|
672
|
+
// DDFA terminal fallback: a ray dying while still deferring (e.g. a mirror maze) commits its last
|
|
673
|
+
// surface instead of leaving a black guide OIDN would demod-amplify.
|
|
674
|
+
// Plain JS inliner, not an Fn — it closes over albedo/featPrefix/flags .toVar()s.
|
|
675
|
+
const commitDeferredAux = ( normal ) => {
|
|
676
|
+
|
|
677
|
+
If( auxOn.and( flags.bitAnd( uint( RAY_FLAG.AUX_LOCKED ) ).equal( uint( 0 ) ) ), () => {
|
|
678
|
+
|
|
679
|
+
const primaryDepth = gbDecodeNormalDepth( readGBuffer( gBufferRW, pixelIndex ) ).w;
|
|
680
|
+
writeGBuffer( gBufferRW, pixelIndex, normal, primaryDepth, clamp( albedo.mul( featPrefix ), vec3( 0.0 ), vec3( 1.0 ) ) );
|
|
640
681
|
|
|
641
|
-
const linearDepth = computeNDCDepth( {
|
|
642
|
-
worldPos: hitPoint,
|
|
643
|
-
cameraProjectionMatrix,
|
|
644
|
-
cameraViewMatrix,
|
|
645
682
|
} );
|
|
646
|
-
// G-buffer is per-pixel (rayID == pixelIndex). writeGBuffer half-packs (normal/depth/albedo).
|
|
647
|
-
// Write the primary DEPTH now with the miss-default aux; the real normal/albedo are captured below
|
|
648
|
-
// (aux-extend) and may extend through specular surfaces (gap #9). Glass rays Return at the transparency
|
|
649
|
-
// block before that capture, so a glass-then-escape pixel keeps this default aux — megakernel parity
|
|
650
|
-
// (objectNormal/objectColor stay at their init for transmissive-then-miss).
|
|
651
|
-
//
|
|
652
|
-
// Fix ③: BLEND (semi-transparent) glass instead LOCKS its OWN normal + albedo here. Its per-frame
|
|
653
|
-
// stochastic alpha-skip-vs-opaque-shade outcome (MaterialTransmission.js:522-540) otherwise writes a
|
|
654
|
-
// DIFFERENT surface into the aux each frame (wall-behind on skip vs glass on opaque); with the normal
|
|
655
|
-
// not temporally averaged that becomes the speckle OIDN smears on — the visible artefact on
|
|
656
|
-
// alpha-transparent glass. A deterministic glass-surface guide every frame removes it. MASK / opaque
|
|
657
|
-
// materials keep the default + rely on the downstream aux-extend.
|
|
658
|
-
If( material.alphaMode.equal( int( 2 ) ), () => {
|
|
659
683
|
|
|
660
|
-
|
|
661
|
-
flags.assign( flags.bitOr( uint( RAY_FLAG.AUX_LOCKED ) ) );
|
|
684
|
+
};
|
|
662
685
|
|
|
663
|
-
|
|
686
|
+
// Deactivate the ray, keeping the radiance already gathered at this vertex.
|
|
687
|
+
const terminatePath = () => {
|
|
688
|
+
|
|
689
|
+
writeRayRadiance( rayBufferRW, rayID, currentRadiance );
|
|
690
|
+
writeRayDirFlags( rayBufferRW, rayID, direction, flags.bitAnd( uint( ~ RAY_FLAG.ACTIVE ) ) );
|
|
691
|
+
rngBufferRW.element( rayID ).assign( rngState );
|
|
692
|
+
|
|
693
|
+
};
|
|
664
694
|
|
|
665
|
-
|
|
695
|
+
// first-hit MRT data (bounce 0 only): write the primary DEPTH now with the default normal/albedo.
|
|
696
|
+
// The real normal/albedo are committed by the DDFA decision blocks below (which re-pack this depth);
|
|
697
|
+
// they may defer through smooth glass/mirror and commit at the first diffuse-enough surface or the env.
|
|
698
|
+
If( bounceIndex.equal( 0 ).and( auxOn ), () => {
|
|
666
699
|
|
|
700
|
+
const linearDepth = computeNDCDepth( {
|
|
701
|
+
worldPos: hitPoint,
|
|
702
|
+
cameraProjectionMatrix,
|
|
703
|
+
cameraViewMatrix,
|
|
667
704
|
} );
|
|
705
|
+
writeGBuffer( gBufferRW, pixelIndex, vec3( 0.0, 0.0, 1.0 ), linearDepth, vec3( 0.0 ) );
|
|
668
706
|
|
|
669
707
|
} );
|
|
670
708
|
|
|
@@ -698,6 +736,53 @@ export function buildShadeKernel( params ) {
|
|
|
698
736
|
// persist any wavelength locked on a fresh dispersive transmission; identity write otherwise
|
|
699
737
|
pathWavelength.assign( interaction.pathWavelength );
|
|
700
738
|
|
|
739
|
+
// ─── DDFA aux decision at a transmissive / alpha / SSS interaction. Runs for BOTH the continuing and
|
|
740
|
+
// the BLEND fall-through paths, so it sits before If(continueRay). If/ElseIf chaining so BLEND wins
|
|
741
|
+
// over the transmission branch (fixes the BLEND-glass per-frame flicker + the BLEND+transmission case). ───
|
|
742
|
+
If( auxOn.and( flags.bitAnd( uint( RAY_FLAG.AUX_LOCKED ) ).equal( uint( 0 ) ) ), () => {
|
|
743
|
+
|
|
744
|
+
const primaryDepth = gbDecodeNormalDepth( readGBuffer( gBufferRW, pixelIndex ) ).w;
|
|
745
|
+
// N here is the mapped normal — faceforward it (a ray exiting glass sees a back-facing N).
|
|
746
|
+
const Nff = select( dot( N, direction.negate() ).lessThan( 0.0 ), N.negate(), N ).toVar();
|
|
747
|
+
const auxAlbedo = clamp( albedo.mul( featPrefix ), vec3( 0.0 ), vec3( 1.0 ) ).toVar();
|
|
748
|
+
|
|
749
|
+
If( material.alphaMode.equal( int( 2 ) ), () => {
|
|
750
|
+
|
|
751
|
+
// BLEND: deterministic own-surface commit on BOTH stochastic skip + shade frames (no flicker).
|
|
752
|
+
writeGBuffer( gBufferRW, pixelIndex, Nff, primaryDepth, auxAlbedo );
|
|
753
|
+
flags.assign( flags.bitOr( uint( RAY_FLAG.AUX_LOCKED ) ) );
|
|
754
|
+
|
|
755
|
+
} ).ElseIf( interaction.isAlphaSkip, () => {
|
|
756
|
+
|
|
757
|
+
// MASK cutout passthrough: keep deferring, tint unchanged (backdrop through the hole commits later).
|
|
758
|
+
|
|
759
|
+
} ).ElseIf( interaction.isSubsurface, () => {
|
|
760
|
+
|
|
761
|
+
// SSS boundary: force-commit the skin surface as diffuse.
|
|
762
|
+
writeGBuffer( gBufferRW, pixelIndex, Nff, primaryDepth, auxAlbedo );
|
|
763
|
+
flags.assign( flags.bitOr( uint( RAY_FLAG.AUX_LOCKED ) ) );
|
|
764
|
+
|
|
765
|
+
} ).ElseIf( interaction.isTransmissive, () => {
|
|
766
|
+
|
|
767
|
+
If( auxCommit.or( material.dispersion.greaterThan( 0.0 ) ), () => {
|
|
768
|
+
|
|
769
|
+
// frosted / rough / dispersive glass: commit its own surface (deterministic per frame).
|
|
770
|
+
writeGBuffer( gBufferRW, pixelIndex, Nff, primaryDepth, auxAlbedo );
|
|
771
|
+
flags.assign( flags.bitOr( uint( RAY_FLAG.AUX_LOCKED ) ) );
|
|
772
|
+
|
|
773
|
+
} ).Else( () => {
|
|
774
|
+
|
|
775
|
+
// smooth glass: DEFER — tint by the glass colour (matches the transmission throughput,
|
|
776
|
+
// which multiplies material.color at both reflect and refract; achromatic eta²/Fr excluded).
|
|
777
|
+
featCarry.mulAssign( clamp( albedo, vec3( 0.0 ), vec3( 1.0 ) ) );
|
|
778
|
+
|
|
779
|
+
} );
|
|
780
|
+
|
|
781
|
+
} );
|
|
782
|
+
// else (opaque non-skip): the opaque decision block below handles it (needs the viewer-facing N).
|
|
783
|
+
|
|
784
|
+
} );
|
|
785
|
+
|
|
701
786
|
If( interaction.continueRay, () => {
|
|
702
787
|
|
|
703
788
|
// update medium stack for transmission (not reflection/TIR)
|
|
@@ -801,26 +886,6 @@ export function buildShadeKernel( params ) {
|
|
|
801
886
|
|
|
802
887
|
throughput.mulAssign( interaction.throughput );
|
|
803
888
|
|
|
804
|
-
// Fix ②: OIDN clean-aux for STOCHASTIC glass (rough / frosted / dispersive), whose refracted
|
|
805
|
-
// ray hits a different behind-surface every frame. Extending aux through it (the aux-extend on
|
|
806
|
-
// the opaque-hit path) captures that per-frame surface — albedo then temporally averaged, normal
|
|
807
|
-
// point-sampled — an inconsistent guide OIDN smears on. Instead lock the glass surface's OWN
|
|
808
|
-
// normal + tint here (deterministic, spatially coherent). Smooth non-dispersive glass is left
|
|
809
|
-
// alone: its refraction is a per-pixel delta, so the behind-surface aux captured downstream is
|
|
810
|
-
// already stable. Gated to a genuine dielectric event (not alpha-cutout passthrough or SSS).
|
|
811
|
-
If( auxOn
|
|
812
|
-
.and( flags.bitAnd( uint( RAY_FLAG.AUX_LOCKED ) ).equal( uint( 0 ) ) )
|
|
813
|
-
.and( interaction.isTransmissive )
|
|
814
|
-
.and( interaction.isAlphaSkip.not() )
|
|
815
|
-
.and( interaction.isSubsurface.not() )
|
|
816
|
-
.and( material.roughness.greaterThan( 0.05 ).or( material.dispersion.greaterThan( 0.0 ) ) ), () => {
|
|
817
|
-
|
|
818
|
-
const primaryDepth = gbDecodeNormalDepth( readGBuffer( gBufferRW, pixelIndex ) ).w;
|
|
819
|
-
writeGBuffer( gBufferRW, pixelIndex, N, primaryDepth, albedo.xyz );
|
|
820
|
-
flags.assign( flags.bitOr( uint( RAY_FLAG.AUX_LOCKED ) ) );
|
|
821
|
-
|
|
822
|
-
} );
|
|
823
|
-
|
|
824
889
|
// reflection stays on same side, transmission pushes through
|
|
825
890
|
const reflectOffsetDir = select( interaction.entering, N, N.negate() );
|
|
826
891
|
const offsetDir = select( interaction.didReflect, reflectOffsetDir, direction );
|
|
@@ -835,8 +900,21 @@ export function buildShadeKernel( params ) {
|
|
|
835
900
|
|
|
836
901
|
flags.assign( flags.bitOr( uint( RAY_FLAG.REDIRECTED ) ) );
|
|
837
902
|
|
|
903
|
+
} ).Else( () => {
|
|
904
|
+
|
|
905
|
+
// Alpha passthrough: charge the guard. N is faceforwarded for the aux commit (a ray
|
|
906
|
+
// exiting a back-facing skip would otherwise write an inverted normal).
|
|
907
|
+
transparentCount.addAssign( 1 );
|
|
908
|
+
If( transparentCount.greaterThan( maxTransparentBounces ), () => {
|
|
909
|
+
|
|
910
|
+
commitDeferredAux( select( dot( N, direction.negate() ).lessThan( 0.0 ), N.negate(), N ) );
|
|
911
|
+
terminatePath();
|
|
912
|
+
Return();
|
|
913
|
+
|
|
914
|
+
} );
|
|
915
|
+
|
|
838
916
|
} );
|
|
839
|
-
writeRayOriginMeta( rayBufferRW, rayID, newOrigin, cameraDepth, sssSteps );
|
|
917
|
+
writeRayOriginMeta( rayBufferRW, rayID, newOrigin, cameraDepth, sssSteps, transparentCount );
|
|
840
918
|
writeRayDirFlags( rayBufferRW, rayID, interaction.direction, flags );
|
|
841
919
|
// Free bounce: preserve prevBouncePdf (megakernel keeps the last opaque-scatter pdf across
|
|
842
920
|
// transmission/alpha-skip/SSS-boundary). Writing 1.0 corrupts the next bounce's env/emissive MIS,
|
|
@@ -844,6 +922,13 @@ export function buildShadeKernel( params ) {
|
|
|
844
922
|
writeRayThroughputPdf( rayBufferRW, rayID, throughput, readRayPdf( rayBufferRW, rayID ) );
|
|
845
923
|
writeRayRadiance( rayBufferRW, rayID, currentRadiance );
|
|
846
924
|
writeMediumStack( rayBufferRW, rayID, uint( mediumStackDepth ), uint( transTraversals ), mediumStack_ior_1, mediumStack_ior_2, mediumStack_ior_3, uint( pathWavelength.add( 0.5 ) ) );
|
|
925
|
+
// DDFA: persist the (possibly tinted) see-through throughput for the next bounce. MUST run after
|
|
926
|
+
// the writeMediumSigmaA above — both RMW slot 5 (sigmaA.xyz / featTP.w). Gated AUX_LOCKED-clear.
|
|
927
|
+
If( auxOn.and( flags.bitAnd( uint( RAY_FLAG.AUX_LOCKED ) ).equal( uint( 0 ) ) ), () => {
|
|
928
|
+
|
|
929
|
+
writeFeatureThroughput( rayBufferRW, rayID, featCarry );
|
|
930
|
+
|
|
931
|
+
} );
|
|
847
932
|
rngBufferRW.element( rayID ).assign( rngState );
|
|
848
933
|
Return();
|
|
849
934
|
|
|
@@ -930,22 +1015,29 @@ export function buildShadeKernel( params ) {
|
|
|
930
1015
|
|
|
931
1016
|
} );
|
|
932
1017
|
|
|
933
|
-
//
|
|
934
|
-
//
|
|
935
|
-
//
|
|
936
|
-
|
|
937
|
-
// stays at the primary hit (read back + re-packed; the snorm depth re-pack is idempotent — no drift).
|
|
938
|
-
If( flags.bitAnd( uint( RAY_FLAG.AUX_LOCKED ) ).equal( uint( 0 ) ).and( auxOn ), () => {
|
|
1018
|
+
// ─── DDFA opaque aux decision: commit at the first diffuse-enough surface, defer through smooth
|
|
1019
|
+
// mirror/metal so the guide describes what the mirror reflects, not the mirror itself. N is already
|
|
1020
|
+
// viewer-facing (two-sided flip above); depth read back + re-packed (idempotent snorm — no drift). ───
|
|
1021
|
+
If( auxOn.and( flags.bitAnd( uint( RAY_FLAG.AUX_LOCKED ) ).equal( uint( 0 ) ) ), () => {
|
|
939
1022
|
|
|
940
1023
|
const primaryDepth = gbDecodeNormalDepth( readGBuffer( gBufferRW, pixelIndex ) ).w;
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
const auxIsMirror = material.metalness.greaterThan( 0.7 ).and( material.roughness.lessThan( 0.3 ) );
|
|
944
|
-
const auxIsTransmissive = material.transmission.greaterThan( 0.5 );
|
|
945
|
-
If( auxIsMirror.or( auxIsTransmissive ).not(), () => {
|
|
1024
|
+
If( auxCommit, () => {
|
|
946
1025
|
|
|
1026
|
+
// diffuse / glossy / rough-metal: commit the surface guide, tinted by the see-through prefix.
|
|
1027
|
+
writeGBuffer( gBufferRW, pixelIndex, N, primaryDepth, clamp( albedo.mul( featPrefix ), vec3( 0.0 ), vec3( 1.0 ) ) );
|
|
947
1028
|
flags.assign( flags.bitOr( uint( RAY_FLAG.AUX_LOCKED ) ) );
|
|
948
1029
|
|
|
1030
|
+
} ).Else( () => {
|
|
1031
|
+
|
|
1032
|
+
// smooth mirror / metal: DEFER — tint by the specular directional albedo (metal colour),
|
|
1033
|
+
// matching the opaque throughput (≈ F at the near-delta mirror lobe). Achromatic energy excluded.
|
|
1034
|
+
const NoV = max( dot( N, V ), float( 1e-3 ) );
|
|
1035
|
+
const F0 = clamp(
|
|
1036
|
+
mix( dielectricF0( material.ior ).mul( material.specularColor ), albedo, material.metalness ).mul( material.specularIntensity ),
|
|
1037
|
+
vec3( 0.0 ), vec3( 1.0 ),
|
|
1038
|
+
);
|
|
1039
|
+
featCarry.mulAssign( DFGResult.wrap( evaluateDFG( F0, NoV, max( rawRough, float( 0.02 ) ) ) ).E_total );
|
|
1040
|
+
|
|
949
1041
|
} );
|
|
950
1042
|
|
|
951
1043
|
} );
|
|
@@ -955,11 +1047,14 @@ export function buildShadeKernel( params ) {
|
|
|
955
1047
|
material.clearcoat, material.emissive, material.subsurface,
|
|
956
1048
|
) ).toVar();
|
|
957
1049
|
|
|
958
|
-
// STBN keyed on (pixel, bounceIndex, frame).
|
|
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.
|
|
959
1053
|
const _resX = int( resolution.x ).toVar();
|
|
1054
|
+
const _globalPixel = int( pixelIndex ).add( chunkRowBase.mul( _resX ) );
|
|
960
1055
|
const _pixelCoord = vec2(
|
|
961
|
-
float(
|
|
962
|
-
float(
|
|
1056
|
+
float( _globalPixel.mod( _resX ) ).add( 0.5 ),
|
|
1057
|
+
float( _globalPixel.div( _resX ) ).add( 0.5 ),
|
|
963
1058
|
);
|
|
964
1059
|
const xi = getRandomSample( _pixelCoord, int( 0 ), bounceIndex, rngState, int( - 1 ), resolution, frame ).toVar();
|
|
965
1060
|
const emptyWeights = BRDFWeights( {
|
|
@@ -1174,9 +1269,8 @@ export function buildShadeKernel( params ) {
|
|
|
1174
1269
|
).toVar();
|
|
1175
1270
|
If( rrSurvival.lessThanEqual( 0.0 ), () => {
|
|
1176
1271
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
rngBufferRW.element( rayID ).assign( rngState );
|
|
1272
|
+
commitDeferredAux( N );
|
|
1273
|
+
terminatePath();
|
|
1180
1274
|
Return();
|
|
1181
1275
|
|
|
1182
1276
|
} );
|
|
@@ -1185,9 +1279,8 @@ export function buildShadeKernel( params ) {
|
|
|
1185
1279
|
// Terminate on CAMERA depth (opaque scatter count), not path length — glass/SSS free bounces no longer burn the maxBounces budget (gap #4).
|
|
1186
1280
|
If( cameraDepth.greaterThanEqual( maxBounceCount ), () => {
|
|
1187
1281
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
rngBufferRW.element( rayID ).assign( rngState );
|
|
1282
|
+
commitDeferredAux( N );
|
|
1283
|
+
terminatePath();
|
|
1191
1284
|
Return();
|
|
1192
1285
|
|
|
1193
1286
|
} );
|
|
@@ -1195,11 +1288,17 @@ export function buildShadeKernel( params ) {
|
|
|
1195
1288
|
const newOrigin = hitPoint.add( N.mul( 0.001 ) );
|
|
1196
1289
|
|
|
1197
1290
|
// Opaque scatter: the only bounce that advances camera depth.
|
|
1198
|
-
writeRayOriginMeta( rayBufferRW, rayID, newOrigin, cameraDepth.add( 1 ), sssSteps );
|
|
1291
|
+
writeRayOriginMeta( rayBufferRW, rayID, newOrigin, cameraDepth.add( 1 ), sssSteps, transparentCount );
|
|
1199
1292
|
writeRayDirFlags( rayBufferRW, rayID, bounceDir, flags );
|
|
1200
1293
|
writeRayThroughputPdf( rayBufferRW, rayID, throughput, bouncePdf );
|
|
1201
1294
|
writeRayRadiance( rayBufferRW, rayID, currentRadiance );
|
|
1202
1295
|
writeMediumStack( rayBufferRW, rayID, uint( mediumStackDepth ), uint( transTraversals ), mediumStack_ior_1, mediumStack_ior_2, mediumStack_ior_3, uint( pathWavelength.add( 0.5 ) ) );
|
|
1296
|
+
// DDFA: persist the (possibly mirror-tinted) see-through throughput for the next bounce.
|
|
1297
|
+
If( auxOn.and( flags.bitAnd( uint( RAY_FLAG.AUX_LOCKED ) ).equal( uint( 0 ) ) ), () => {
|
|
1298
|
+
|
|
1299
|
+
writeFeatureThroughput( rayBufferRW, rayID, featCarry );
|
|
1300
|
+
|
|
1301
|
+
} );
|
|
1203
1302
|
rngBufferRW.element( rayID ).assign( rngState );
|
|
1204
1303
|
|
|
1205
1304
|
} );
|
|
@@ -47,10 +47,20 @@ const withBucket = ( buckets, packedIndex, fn ) => {
|
|
|
47
47
|
|
|
48
48
|
const bucket = packedIndex.div( int( _STRIDE ) ).toVar();
|
|
49
49
|
const layer = packedIndex.sub( bucket.mul( int( _STRIDE ) ) ).toVar();
|
|
50
|
-
|
|
50
|
+
// Block bodies, not concise arrows: an If() callback that yields a value makes TSL emit a
|
|
51
|
+
// `return` inside an inline Fn(), which it comments out and warns about on every arm.
|
|
52
|
+
let chain = If( bucket.equal( int( 0 ) ), () => {
|
|
53
|
+
|
|
54
|
+
fn( buckets[ 0 ], layer );
|
|
55
|
+
|
|
56
|
+
} );
|
|
51
57
|
for ( let i = 1; i < buckets.length; i ++ ) {
|
|
52
58
|
|
|
53
|
-
chain = chain.ElseIf( bucket.equal( int( i ) ), () =>
|
|
59
|
+
chain = chain.ElseIf( bucket.equal( int( i ) ), () => {
|
|
60
|
+
|
|
61
|
+
fn( buckets[ i ], layer );
|
|
62
|
+
|
|
63
|
+
} );
|
|
54
64
|
|
|
55
65
|
}
|
|
56
66
|
|
package/src/index.js
CHANGED
|
@@ -17,6 +17,9 @@ export { EngineEvents } from './EngineEvents.js';
|
|
|
17
17
|
// Asset URL / cache namespace overrides (call before constructing PathTracerApp)
|
|
18
18
|
export { configureAssets, getAssetConfig } from './AssetConfig.js';
|
|
19
19
|
|
|
20
|
+
// Logging — leveled/namespaced console output shared with the workers
|
|
21
|
+
export { Logger, createLogger, fmt, LOG_LEVELS } from './utils/Logger.js';
|
|
22
|
+
|
|
20
23
|
// Configuration defaults and presets
|
|
21
24
|
export {
|
|
22
25
|
ENGINE_DEFAULTS,
|
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
import { EquirectHDRInfo } from '../Processor/EquirectHDRInfo.js';
|
|
14
14
|
import { ProceduralSky } from '../Processor/ProceduralSky.js';
|
|
15
15
|
import { SimpleSky } from '../Processor/SimpleSky.js';
|
|
16
|
+
import { createLogger, fmt } from '../utils/Logger.js';
|
|
17
|
+
|
|
18
|
+
const log = createLogger( 'env' );
|
|
16
19
|
import { ENGINE_DEFAULTS as DEFAULT_STATE } from '../EngineDefaults.js';
|
|
17
20
|
|
|
18
21
|
export class EnvironmentManager {
|
|
@@ -231,8 +234,6 @@ export class EnvironmentManager {
|
|
|
231
234
|
this.environmentTexture = envTex;
|
|
232
235
|
this.envTexSize.set( envTex.image.width, envTex.image.height );
|
|
233
236
|
|
|
234
|
-
console.log( `EnvironmentManager: Environment map ${envTex.image.width}x${envTex.image.height}` );
|
|
235
|
-
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
/**
|
|
@@ -317,11 +318,14 @@ export class EnvironmentManager {
|
|
|
317
318
|
|
|
318
319
|
}
|
|
319
320
|
|
|
320
|
-
|
|
321
|
+
log.info( fmt.list( [
|
|
322
|
+
fmt.px( this.envTexSize.x, this.envTexSize.y ),
|
|
323
|
+
`CDF ${fmt.ms( this.cdfBuildTime )}${useWorker ? '' : ' (main thread)'}`,
|
|
324
|
+
] ) );
|
|
321
325
|
|
|
322
326
|
} catch ( error ) {
|
|
323
327
|
|
|
324
|
-
|
|
328
|
+
log.error( 'CDF build failed:', error );
|
|
325
329
|
this.uniforms.set( 'useEnvMapIS', 0 );
|
|
326
330
|
this.uniforms.set( 'envTotalSum', 0.0 );
|
|
327
331
|
this.uniforms.set( 'envCompensationDelta', 0.0 );
|
|
@@ -437,7 +441,7 @@ export class EnvironmentManager {
|
|
|
437
441
|
|
|
438
442
|
} catch ( error ) {
|
|
439
443
|
|
|
440
|
-
|
|
444
|
+
log.error( 'gradient sky generation failed:', error );
|
|
441
445
|
|
|
442
446
|
}
|
|
443
447
|
|
|
@@ -467,7 +471,7 @@ export class EnvironmentManager {
|
|
|
467
471
|
|
|
468
472
|
} catch ( error ) {
|
|
469
473
|
|
|
470
|
-
|
|
474
|
+
log.error( 'solid color sky generation failed:', error );
|
|
471
475
|
|
|
472
476
|
}
|
|
473
477
|
|
|
@@ -503,11 +507,11 @@ export class EnvironmentManager {
|
|
|
503
507
|
this.uniforms.set( 'sunAngularSize', 0.0087 );
|
|
504
508
|
this.uniforms.set( 'hasSun', 1 );
|
|
505
509
|
|
|
506
|
-
|
|
510
|
+
log.debug( `sun synced · dir ${this.envParams.skySunDirection.toArray().map( v => v.toFixed( 2 ) ).join( ',' )}` );
|
|
507
511
|
|
|
508
512
|
} catch ( error ) {
|
|
509
513
|
|
|
510
|
-
|
|
514
|
+
log.error( 'procedural sky generation failed:', error );
|
|
511
515
|
|
|
512
516
|
}
|
|
513
517
|
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
import { StorageInstancedBufferAttribute } from 'three/webgpu';
|
|
11
11
|
import { storage } from 'three/tsl';
|
|
12
12
|
import { MATERIAL_DATA_LAYOUT as M, TRIANGLE_DATA_LAYOUT as T } from '../EngineDefaults.js';
|
|
13
|
+
import { createLogger, fmt } from '../utils/Logger.js';
|
|
14
|
+
|
|
15
|
+
const log = createLogger( 'material' );
|
|
13
16
|
|
|
14
17
|
const PIXELS_PER_MATERIAL = M.SLOTS_PER_MATERIAL;
|
|
15
18
|
// Per-triangle float offsets used by _patchTriangleSideForMaterial / _patchTriangleBlockerForMaterial.
|
|
@@ -84,7 +87,7 @@ export class MaterialDataManager {
|
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
this.materialCount = Math.floor( vec4Count / PIXELS_PER_MATERIAL );
|
|
87
|
-
|
|
90
|
+
log.debug( `${fmt.n( this.materialCount )} materials (storage buffer)` );
|
|
88
91
|
|
|
89
92
|
}
|
|
90
93
|
|
|
@@ -186,7 +189,7 @@ export class MaterialDataManager {
|
|
|
186
189
|
|
|
187
190
|
if ( ! this.materialStorageAttr ) {
|
|
188
191
|
|
|
189
|
-
|
|
192
|
+
log.warn( 'material storage buffer not available' );
|
|
190
193
|
return;
|
|
191
194
|
|
|
192
195
|
}
|
|
@@ -364,7 +367,7 @@ export class MaterialDataManager {
|
|
|
364
367
|
|
|
365
368
|
break;
|
|
366
369
|
default:
|
|
367
|
-
|
|
370
|
+
log.warn( `unknown material property: ${property}` );
|
|
368
371
|
return;
|
|
369
372
|
|
|
370
373
|
}
|
|
@@ -403,7 +406,7 @@ export class MaterialDataManager {
|
|
|
403
406
|
|
|
404
407
|
if ( ! this.materialStorageAttr ) {
|
|
405
408
|
|
|
406
|
-
|
|
409
|
+
log.warn( 'material storage buffer not available' );
|
|
407
410
|
return;
|
|
408
411
|
|
|
409
412
|
}
|
|
@@ -630,7 +633,7 @@ export class MaterialDataManager {
|
|
|
630
633
|
|
|
631
634
|
if ( ! this.materialStorageAttr ) {
|
|
632
635
|
|
|
633
|
-
|
|
636
|
+
log.warn( 'material storage buffer not available' );
|
|
634
637
|
return;
|
|
635
638
|
|
|
636
639
|
}
|
|
@@ -651,7 +654,7 @@ export class MaterialDataManager {
|
|
|
651
654
|
const offset = transformOffsets[ textureName ];
|
|
652
655
|
if ( offset === undefined ) {
|
|
653
656
|
|
|
654
|
-
|
|
657
|
+
log.warn( `unknown texture name for transform update: ${textureName}` );
|
|
655
658
|
return;
|
|
656
659
|
|
|
657
660
|
}
|
|
@@ -683,7 +686,7 @@ export class MaterialDataManager {
|
|
|
683
686
|
|
|
684
687
|
if ( ! this.materialStorageAttr?.array ) {
|
|
685
688
|
|
|
686
|
-
|
|
689
|
+
log.warn( 'material storage buffer not available for feature scanning' );
|
|
687
690
|
return false;
|
|
688
691
|
|
|
689
692
|
}
|
|
@@ -763,7 +766,7 @@ export class MaterialDataManager {
|
|
|
763
766
|
|
|
764
767
|
if ( ! features ) {
|
|
765
768
|
|
|
766
|
-
|
|
769
|
+
log.warn( 'no sceneFeatures detected, skipping define injection' );
|
|
767
770
|
return;
|
|
768
771
|
|
|
769
772
|
}
|
|
@@ -780,7 +783,7 @@ export class MaterialDataManager {
|
|
|
780
783
|
// For TSL, we can't inject defines into the shader at runtime
|
|
781
784
|
// Instead, we would need to conditionally generate the shader
|
|
782
785
|
// For now, log the features for debugging
|
|
783
|
-
|
|
786
|
+
log.debug( 'material features:', features );
|
|
784
787
|
|
|
785
788
|
this.compiledFeatures = featuresJSON;
|
|
786
789
|
|