rayzee 5.10.2 → 6.0.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 +82 -22
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js +2 -0
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js.map +1 -0
- package/dist/rayzee.es.js +1299 -1843
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +50 -74
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -4
- package/src/AssetConfig.js +56 -0
- package/src/EngineDefaults.js +5 -3
- package/src/EngineEvents.js +1 -0
- package/src/Passes/AIUpscaler.js +44 -22
- package/src/Passes/OIDNDenoiser.js +4 -104
- package/src/PathTracerApp.js +59 -63
- package/src/Processor/AssetLoader.js +5 -2
- package/src/Processor/TextureCreator.js +42 -15
- package/src/Processor/Workers/AIUpscalerWorker.js +21 -6
- package/src/Stages/ASVGF.js +6 -27
- package/src/Stages/AdaptiveSampling.js +10 -26
- package/src/Stages/PathTracer.js +4 -5
- package/src/TSL/BVHTraversal.js +2 -18
- package/src/TSL/Clearcoat.js +1 -2
- package/src/TSL/Common.js +0 -13
- package/src/TSL/EmissiveSampling.js +0 -16
- package/src/TSL/Environment.js +0 -7
- package/src/TSL/LightsDirect.js +3 -379
- package/src/TSL/LightsSampling.js +0 -171
- package/src/TSL/MaterialEvaluation.js +3 -103
- package/src/TSL/MaterialProperties.js +1 -56
- package/src/TSL/MaterialSampling.js +2 -284
- package/src/TSL/MaterialTransmission.js +0 -93
- package/src/TSL/Random.js +0 -23
- package/src/TSL/Struct.js +0 -21
- package/src/TSL/TextureSampling.js +0 -69
- package/src/index.js +5 -2
- package/src/managers/DenoisingManager.js +13 -5
- package/src/managers/OverlayManager.js +14 -2
- package/src/managers/VideoRenderManager.js +4 -4
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js +0 -2
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js.map +0 -1
- package/src/Processor/createRenderTargetHelper.js +0 -521
- package/src/TSL/RayIntersection.js +0 -162
- package/src/managers/helpers/StatsHelper.js +0 -45
package/src/TSL/LightsDirect.js
CHANGED
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
Fn,
|
|
7
|
-
vec2,
|
|
8
|
-
vec3,
|
|
9
|
-
vec4,
|
|
10
7
|
float,
|
|
11
8
|
int,
|
|
12
9
|
bool as tslBool,
|
|
@@ -14,13 +11,11 @@ import {
|
|
|
14
11
|
Loop,
|
|
15
12
|
Break,
|
|
16
13
|
dot,
|
|
17
|
-
normalize,
|
|
18
14
|
abs,
|
|
19
15
|
max,
|
|
20
16
|
min,
|
|
21
17
|
length,
|
|
22
18
|
sqrt,
|
|
23
|
-
pow,
|
|
24
19
|
cos,
|
|
25
20
|
clamp,
|
|
26
21
|
smoothstep,
|
|
@@ -29,14 +24,9 @@ import {
|
|
|
29
24
|
} from 'three/tsl';
|
|
30
25
|
|
|
31
26
|
import { Ray, ShadowMaterial, HitInfo } from './Struct.js';
|
|
32
|
-
import {
|
|
33
|
-
import { fresnelSchlickFloat } from './Fresnel.js';
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
sampleCone, intersectAreaLight,
|
|
37
|
-
} from './LightsCore.js';
|
|
38
|
-
import { calculateBeerLawAbsorption, calculateShadowTransmittance } from './MaterialTransmission.js';
|
|
39
|
-
import { RandomValue } from './Random.js';
|
|
27
|
+
import { REC709_LUMINANCE_COEFFICIENTS, getShadowMaterial, getDatafromStorageBuffer } from './Common.js';
|
|
28
|
+
import { fresnelSchlickFloat, iorToFresnel0 } from './Fresnel.js';
|
|
29
|
+
import { calculateBeerLawAbsorption } from './MaterialTransmission.js';
|
|
40
30
|
import { getTransformedUV } from './TextureSampling.js';
|
|
41
31
|
|
|
42
32
|
// Module-level state for alpha-cutout shadow testing.
|
|
@@ -65,31 +55,6 @@ export function setAlphaShadowsUniform( node ) {
|
|
|
65
55
|
|
|
66
56
|
}
|
|
67
57
|
|
|
68
|
-
// ================================================================================
|
|
69
|
-
// SHADOW RAY MATERIAL TRANSPARENCY
|
|
70
|
-
// ================================================================================
|
|
71
|
-
|
|
72
|
-
export const getMaterialTransparency = Fn( ( [ shadowHit, shadowRayDir, rngState ] ) => {
|
|
73
|
-
|
|
74
|
-
const result = float( 1.0 ).toVar();
|
|
75
|
-
|
|
76
|
-
// Check if the material has transmission (like glass)
|
|
77
|
-
If( shadowHit.material.transmission.greaterThan( 0.0 ), () => {
|
|
78
|
-
|
|
79
|
-
const isEntering = dot( shadowRayDir, shadowHit.normal ).lessThan( 0.0 );
|
|
80
|
-
const transmittance = calculateShadowTransmittance( shadowRayDir, shadowHit.normal, shadowHit.material, isEntering );
|
|
81
|
-
result.assign( float( 1.0 ).sub( transmittance ) );
|
|
82
|
-
|
|
83
|
-
} ).ElseIf( shadowHit.material.transparent, () => {
|
|
84
|
-
|
|
85
|
-
result.assign( shadowHit.material.opacity );
|
|
86
|
-
|
|
87
|
-
} );
|
|
88
|
-
|
|
89
|
-
return result;
|
|
90
|
-
|
|
91
|
-
} );
|
|
92
|
-
|
|
93
58
|
// ================================================================================
|
|
94
59
|
// SHADOW RAY TRACING
|
|
95
60
|
// ================================================================================
|
|
@@ -499,344 +464,3 @@ export const calculateSpotLightImportance = Fn( ( [ light, hitPoint, normal, mat
|
|
|
499
464
|
|
|
500
465
|
} );
|
|
501
466
|
|
|
502
|
-
// ================================================================================
|
|
503
|
-
// DIRECTIONAL LIGHT CONTRIBUTION
|
|
504
|
-
// ================================================================================
|
|
505
|
-
|
|
506
|
-
export const shouldSkipDirectionalLight = Fn( ( [ light, normal, material, bounceIndex ] ) => {
|
|
507
|
-
|
|
508
|
-
const NoL = max( float( 0.0 ), dot( normal, light.direction ) );
|
|
509
|
-
const shouldSkip = tslBool( false ).toVar();
|
|
510
|
-
|
|
511
|
-
If( light.intensity.lessThanEqual( 0.001 ).or( NoL.lessThanEqual( 0.001 ) ), () => {
|
|
512
|
-
|
|
513
|
-
shouldSkip.assign( true );
|
|
514
|
-
|
|
515
|
-
} );
|
|
516
|
-
|
|
517
|
-
If( shouldSkip.not().and( bounceIndex.greaterThan( int( 0 ) ) ), () => {
|
|
518
|
-
|
|
519
|
-
If( light.intensity.lessThan( 0.01 ), () => {
|
|
520
|
-
|
|
521
|
-
shouldSkip.assign( true );
|
|
522
|
-
|
|
523
|
-
} );
|
|
524
|
-
|
|
525
|
-
If( shouldSkip.not().and( material.metalness.greaterThan( 0.9 ) ).and( NoL.lessThan( 0.1 ) ), () => {
|
|
526
|
-
|
|
527
|
-
shouldSkip.assign( true );
|
|
528
|
-
|
|
529
|
-
} );
|
|
530
|
-
|
|
531
|
-
If( shouldSkip.not().and( material.metalness.lessThan( 0.1 ) ).and( material.roughness.greaterThan( 0.9 ) ).and( light.intensity.lessThan( 0.1 ) ), () => {
|
|
532
|
-
|
|
533
|
-
shouldSkip.assign( true );
|
|
534
|
-
|
|
535
|
-
} );
|
|
536
|
-
|
|
537
|
-
} );
|
|
538
|
-
|
|
539
|
-
return shouldSkip;
|
|
540
|
-
|
|
541
|
-
} );
|
|
542
|
-
|
|
543
|
-
export const calculateDirectionalLightContribution = Fn( ( [
|
|
544
|
-
light, hitPoint, normal, viewDir, material, matCache, brdfSample, bounceIndex,
|
|
545
|
-
rngState,
|
|
546
|
-
// Shadow tracing callback + textures
|
|
547
|
-
traceShadowRayFn,
|
|
548
|
-
evaluateMaterialResponseCachedFn,
|
|
549
|
-
] ) => {
|
|
550
|
-
|
|
551
|
-
const result = vec3( 0.0 ).toVar();
|
|
552
|
-
|
|
553
|
-
If( shouldSkipDirectionalLight( light, normal, material, bounceIndex ).not(), () => {
|
|
554
|
-
|
|
555
|
-
const rayOffset = calculateRayOffset( hitPoint, normal, material );
|
|
556
|
-
const rayOrigin = hitPoint.add( rayOffset );
|
|
557
|
-
|
|
558
|
-
// Determine shadow sampling strategy based on light angle
|
|
559
|
-
const shadowDirection = vec3( 0.0 ).toVar();
|
|
560
|
-
const lightPdf = float( 1e6 ).toVar();
|
|
561
|
-
|
|
562
|
-
If( light.angle.greaterThan( 0.001 ), () => {
|
|
563
|
-
|
|
564
|
-
// Soft shadows: sample direction within cone
|
|
565
|
-
const xi_r1 = RandomValue( rngState ).toVar();
|
|
566
|
-
const xi_r2 = RandomValue( rngState ).toVar();
|
|
567
|
-
const xi = vec2( xi_r1, xi_r2 );
|
|
568
|
-
const halfAngle = light.angle.mul( 0.5 );
|
|
569
|
-
shadowDirection.assign( sampleCone( { direction: light.direction, halfAngle, xi } ) );
|
|
570
|
-
|
|
571
|
-
// Calculate PDF for cone sampling
|
|
572
|
-
const cosHalfAngle = cos( halfAngle );
|
|
573
|
-
lightPdf.assign( float( 1.0 ).div( TWO_PI.mul( float( 1.0 ).sub( cosHalfAngle ) ) ) );
|
|
574
|
-
|
|
575
|
-
} ).Else( () => {
|
|
576
|
-
|
|
577
|
-
shadowDirection.assign( light.direction );
|
|
578
|
-
|
|
579
|
-
} );
|
|
580
|
-
|
|
581
|
-
const NoL = max( float( 0.0 ), dot( normal, shadowDirection ) );
|
|
582
|
-
|
|
583
|
-
If( NoL.greaterThan( 0.0 ), () => {
|
|
584
|
-
|
|
585
|
-
const maxShadowDistance = float( 1e6 );
|
|
586
|
-
const visibility = traceShadowRayFn( rayOrigin, shadowDirection, maxShadowDistance, rngState );
|
|
587
|
-
|
|
588
|
-
If( visibility.greaterThan( 0.0 ), () => {
|
|
589
|
-
|
|
590
|
-
const brdfValue = evaluateMaterialResponseCachedFn( viewDir, shadowDirection, normal, material, matCache );
|
|
591
|
-
const lightRadiance = light.color.mul( light.intensity );
|
|
592
|
-
const contribution = lightRadiance.mul( brdfValue ).mul( NoL ).mul( visibility );
|
|
593
|
-
|
|
594
|
-
// MIS for directional lights (only on primary rays)
|
|
595
|
-
If( bounceIndex.equal( int( 0 ) ).and( brdfSample.pdf.greaterThan( 0.0 ) ), () => {
|
|
596
|
-
|
|
597
|
-
const alignment = max( float( 0.0 ), dot( normalize( brdfSample.direction ), shadowDirection ) );
|
|
598
|
-
|
|
599
|
-
If( alignment.greaterThan( 0.996 ), () => {
|
|
600
|
-
|
|
601
|
-
const misWeight = powerHeuristic( { pdf1: lightPdf, pdf2: brdfSample.pdf } );
|
|
602
|
-
result.assign( contribution.mul( misWeight ) );
|
|
603
|
-
|
|
604
|
-
} ).Else( () => {
|
|
605
|
-
|
|
606
|
-
result.assign( contribution );
|
|
607
|
-
|
|
608
|
-
} );
|
|
609
|
-
|
|
610
|
-
} ).Else( () => {
|
|
611
|
-
|
|
612
|
-
result.assign( contribution );
|
|
613
|
-
|
|
614
|
-
} );
|
|
615
|
-
|
|
616
|
-
} );
|
|
617
|
-
|
|
618
|
-
} );
|
|
619
|
-
|
|
620
|
-
} );
|
|
621
|
-
|
|
622
|
-
return result;
|
|
623
|
-
|
|
624
|
-
} );
|
|
625
|
-
|
|
626
|
-
// ================================================================================
|
|
627
|
-
// AREA LIGHT CONTRIBUTION
|
|
628
|
-
// ================================================================================
|
|
629
|
-
|
|
630
|
-
export const calculateAreaLightContribution = Fn( ( [
|
|
631
|
-
light, hitPoint, normal, viewDir, material, matCache, brdfSample,
|
|
632
|
-
sampleIndex, bounceIndex, rngState,
|
|
633
|
-
traceShadowRayFn,
|
|
634
|
-
evaluateMaterialResponseCachedFn,
|
|
635
|
-
getRandomSampleFn,
|
|
636
|
-
] ) => {
|
|
637
|
-
|
|
638
|
-
const contribution = vec3( 0.0 ).toVar();
|
|
639
|
-
|
|
640
|
-
const lightImportance = estimateLightImportance( light, hitPoint, normal, material );
|
|
641
|
-
|
|
642
|
-
If( lightImportance.greaterThanEqual( 0.001 ), () => {
|
|
643
|
-
|
|
644
|
-
const rayOffset = calculateRayOffset( hitPoint, normal, material );
|
|
645
|
-
const rayOrigin = hitPoint.add( rayOffset );
|
|
646
|
-
|
|
647
|
-
const isDiffuse = material.roughness.greaterThan( 0.7 ).and( material.metalness.lessThan( 0.3 ) );
|
|
648
|
-
const isSpecular = material.roughness.lessThan( 0.3 ).or( material.metalness.greaterThan( 0.7 ) );
|
|
649
|
-
const isFirstBounce = bounceIndex.equal( int( 0 ) );
|
|
650
|
-
|
|
651
|
-
// LIGHT SAMPLING STRATEGY
|
|
652
|
-
If( isFirstBounce.or( isDiffuse ).or( lightImportance.greaterThan( 0.1 ).and( isSpecular.not() ) ), () => {
|
|
653
|
-
|
|
654
|
-
const ruv = getRandomSampleFn( sampleIndex, bounceIndex, rngState );
|
|
655
|
-
|
|
656
|
-
// Generate position on light surface
|
|
657
|
-
const lightPos = light.position
|
|
658
|
-
.add( light.u.mul( ruv.x.sub( 0.5 ) ) )
|
|
659
|
-
.add( light.v.mul( ruv.y.sub( 0.5 ) ) );
|
|
660
|
-
|
|
661
|
-
const toLight = lightPos.sub( hitPoint );
|
|
662
|
-
const lightDistSq = dot( toLight, toLight );
|
|
663
|
-
const lightDist = sqrt( lightDistSq );
|
|
664
|
-
const lightDir = toLight.div( lightDist );
|
|
665
|
-
|
|
666
|
-
const NoL = max( float( 0.0 ), dot( normal, lightDir ) );
|
|
667
|
-
const lightFacing = max( float( 0.0 ), dot( lightDir, light.normal ).negate() );
|
|
668
|
-
|
|
669
|
-
If( NoL.greaterThan( 0.0 ).and( lightFacing.greaterThan( 0.0 ) ), () => {
|
|
670
|
-
|
|
671
|
-
const visibility = traceShadowRayFn( rayOrigin, lightDir, lightDist, rngState );
|
|
672
|
-
|
|
673
|
-
If( visibility.greaterThan( 0.0 ), () => {
|
|
674
|
-
|
|
675
|
-
const brdfValue = evaluateMaterialResponseCachedFn( viewDir, lightDir, normal, material, matCache );
|
|
676
|
-
|
|
677
|
-
// Calculate PDFs for MIS
|
|
678
|
-
const lightPdf = lightDistSq.div( max( light.area.mul( lightFacing ), EPSILON ) );
|
|
679
|
-
const brdfPdf = brdfSample.pdf;
|
|
680
|
-
|
|
681
|
-
// Light contribution with inverse-square falloff
|
|
682
|
-
const falloff = light.area.div( float( 4.0 ).mul( PI ).mul( lightDistSq ) );
|
|
683
|
-
const lightContribution = light.color.mul( light.intensity ).mul( falloff ).mul( lightFacing );
|
|
684
|
-
|
|
685
|
-
// MIS weight
|
|
686
|
-
const misWeight = select(
|
|
687
|
-
brdfPdf.greaterThan( 0.0 ).and( isFirstBounce ),
|
|
688
|
-
powerHeuristic( { pdf1: lightPdf, pdf2: brdfPdf } ),
|
|
689
|
-
float( 1.0 ),
|
|
690
|
-
);
|
|
691
|
-
|
|
692
|
-
contribution.addAssign( lightContribution.mul( brdfValue ).mul( NoL ).mul( visibility ).mul( misWeight ) );
|
|
693
|
-
|
|
694
|
-
} );
|
|
695
|
-
|
|
696
|
-
} );
|
|
697
|
-
|
|
698
|
-
} );
|
|
699
|
-
|
|
700
|
-
// BRDF SAMPLING STRATEGY
|
|
701
|
-
If( isFirstBounce.or( isSpecular ).and( brdfSample.pdf.greaterThan( 0.0 ) ), () => {
|
|
702
|
-
|
|
703
|
-
const toLight = light.position.sub( rayOrigin );
|
|
704
|
-
const rayToLightDot = dot( toLight, brdfSample.direction );
|
|
705
|
-
|
|
706
|
-
If( rayToLightDot.greaterThan( 0.0 ), () => {
|
|
707
|
-
|
|
708
|
-
const hitDistance = intersectAreaLight( light, rayOrigin, brdfSample.direction );
|
|
709
|
-
|
|
710
|
-
If( hitDistance.greaterThan( 0.0 ), () => {
|
|
711
|
-
|
|
712
|
-
const visibility = traceShadowRayFn( rayOrigin, brdfSample.direction, hitDistance, rngState );
|
|
713
|
-
|
|
714
|
-
If( visibility.greaterThan( 0.0 ), () => {
|
|
715
|
-
|
|
716
|
-
const lightFacing = max( float( 0.0 ), dot( brdfSample.direction, light.normal ).negate() );
|
|
717
|
-
|
|
718
|
-
If( lightFacing.greaterThan( 0.0 ), () => {
|
|
719
|
-
|
|
720
|
-
const lightPdf = hitDistance.mul( hitDistance ).div( max( light.area.mul( lightFacing ), EPSILON ) );
|
|
721
|
-
const misWeight = powerHeuristic( { pdf1: brdfSample.pdf, pdf2: lightPdf } );
|
|
722
|
-
|
|
723
|
-
const lightEmission = light.color.mul( light.intensity );
|
|
724
|
-
const NoL = max( float( 0.0 ), dot( normal, brdfSample.direction ) );
|
|
725
|
-
|
|
726
|
-
contribution.addAssign( lightEmission.mul( brdfSample.value ).mul( NoL ).mul( visibility ).mul( misWeight ) );
|
|
727
|
-
|
|
728
|
-
} );
|
|
729
|
-
|
|
730
|
-
} );
|
|
731
|
-
|
|
732
|
-
} );
|
|
733
|
-
|
|
734
|
-
} );
|
|
735
|
-
|
|
736
|
-
} );
|
|
737
|
-
|
|
738
|
-
} );
|
|
739
|
-
|
|
740
|
-
return contribution;
|
|
741
|
-
|
|
742
|
-
} );
|
|
743
|
-
|
|
744
|
-
// ================================================================================
|
|
745
|
-
// POINT LIGHT CONTRIBUTION
|
|
746
|
-
// ================================================================================
|
|
747
|
-
|
|
748
|
-
export const calculatePointLightContribution = Fn( ( [
|
|
749
|
-
light, hitPoint, normal, viewDir, material, matCache, brdfSample, bounceIndex,
|
|
750
|
-
rngState,
|
|
751
|
-
traceShadowRayFn,
|
|
752
|
-
evaluateMaterialResponseFn,
|
|
753
|
-
] ) => {
|
|
754
|
-
|
|
755
|
-
const result = vec3( 0.0 ).toVar();
|
|
756
|
-
|
|
757
|
-
const toLight = light.position.sub( hitPoint );
|
|
758
|
-
const distance = length( toLight );
|
|
759
|
-
|
|
760
|
-
If( distance.lessThanEqual( 1000.0 ), () => {
|
|
761
|
-
|
|
762
|
-
const lightDir = toLight.div( distance );
|
|
763
|
-
const NdotL = dot( normal, lightDir );
|
|
764
|
-
|
|
765
|
-
If( NdotL.greaterThan( 0.0 ), () => {
|
|
766
|
-
|
|
767
|
-
const attenuation = float( 1.0 ).div( distance.mul( distance ) );
|
|
768
|
-
const lightRadiance = light.color.mul( light.intensity ).mul( attenuation );
|
|
769
|
-
|
|
770
|
-
const rayOffset = calculateRayOffset( hitPoint, normal, material );
|
|
771
|
-
const rayOrigin = hitPoint.add( rayOffset );
|
|
772
|
-
|
|
773
|
-
const visibility = traceShadowRayFn( rayOrigin, lightDir, distance.sub( 0.001 ), rngState );
|
|
774
|
-
|
|
775
|
-
If( visibility.greaterThan( 0.0 ), () => {
|
|
776
|
-
|
|
777
|
-
const brdfValue = evaluateMaterialResponseFn( viewDir, lightDir, normal, material );
|
|
778
|
-
result.assign( brdfValue.mul( lightRadiance ).mul( NdotL ).mul( visibility ) );
|
|
779
|
-
|
|
780
|
-
} );
|
|
781
|
-
|
|
782
|
-
} );
|
|
783
|
-
|
|
784
|
-
} );
|
|
785
|
-
|
|
786
|
-
return result;
|
|
787
|
-
|
|
788
|
-
} );
|
|
789
|
-
|
|
790
|
-
// ================================================================================
|
|
791
|
-
// SPOT LIGHT CONTRIBUTION
|
|
792
|
-
// ================================================================================
|
|
793
|
-
|
|
794
|
-
export const calculateSpotLightContribution = Fn( ( [
|
|
795
|
-
light, hitPoint, normal, viewDir, material, matCache, brdfSample, bounceIndex,
|
|
796
|
-
rngState,
|
|
797
|
-
traceShadowRayFn,
|
|
798
|
-
evaluateMaterialResponseFn,
|
|
799
|
-
] ) => {
|
|
800
|
-
|
|
801
|
-
const result = vec3( 0.0 ).toVar();
|
|
802
|
-
|
|
803
|
-
const toLight = light.position.sub( hitPoint );
|
|
804
|
-
const distance = length( toLight );
|
|
805
|
-
|
|
806
|
-
If( distance.lessThanEqual( 1000.0 ), () => {
|
|
807
|
-
|
|
808
|
-
const lightDir = toLight.div( distance );
|
|
809
|
-
const NdotL = dot( normal, lightDir );
|
|
810
|
-
|
|
811
|
-
If( NdotL.greaterThan( 0.0 ), () => {
|
|
812
|
-
|
|
813
|
-
const spotCosAngle = dot( lightDir.negate(), light.direction );
|
|
814
|
-
const coneCosAngle = cos( light.angle );
|
|
815
|
-
|
|
816
|
-
If( spotCosAngle.greaterThanEqual( coneCosAngle ), () => {
|
|
817
|
-
|
|
818
|
-
const coneAttenuation = smoothstep( coneCosAngle, coneCosAngle.add( 0.1 ), spotCosAngle );
|
|
819
|
-
const distanceAttenuation = float( 1.0 ).div( distance.mul( distance ) );
|
|
820
|
-
const lightRadiance = light.color.mul( light.intensity ).mul( distanceAttenuation ).mul( coneAttenuation );
|
|
821
|
-
|
|
822
|
-
const rayOffset = calculateRayOffset( hitPoint, normal, material );
|
|
823
|
-
const rayOrigin = hitPoint.add( rayOffset );
|
|
824
|
-
|
|
825
|
-
const visibility = traceShadowRayFn( rayOrigin, lightDir, distance.sub( 0.001 ), rngState );
|
|
826
|
-
|
|
827
|
-
If( visibility.greaterThan( 0.0 ), () => {
|
|
828
|
-
|
|
829
|
-
const brdfValue = evaluateMaterialResponseFn( viewDir, lightDir, normal, material );
|
|
830
|
-
result.assign( brdfValue.mul( lightRadiance ).mul( NdotL ).mul( visibility ) );
|
|
831
|
-
|
|
832
|
-
} );
|
|
833
|
-
|
|
834
|
-
} );
|
|
835
|
-
|
|
836
|
-
} );
|
|
837
|
-
|
|
838
|
-
} );
|
|
839
|
-
|
|
840
|
-
return result;
|
|
841
|
-
|
|
842
|
-
} );
|
|
@@ -8,14 +8,11 @@
|
|
|
8
8
|
* - Deterministic environment NEE (always runs, two-strategy Veach MIS with implicit miss)
|
|
9
9
|
*
|
|
10
10
|
* Contains:
|
|
11
|
-
* - initLightSample — fully initialize LightSample
|
|
12
11
|
* - sampleRectAreaLight — rectangle area light sampling
|
|
13
|
-
* - sampleCircAreaLight — circle area light sampling
|
|
14
12
|
* - sampleSpotLightWithRadius — spot light sampling with radius
|
|
15
13
|
* - samplePointLightWithAttenuation — point light sampling with attenuation
|
|
16
14
|
* - sampleLightWithImportance — importance-weighted light selection (3-pass)
|
|
17
15
|
* - calculateMaterialPDF — material PDF for MIS
|
|
18
|
-
* - sampleAreaLightContribution — area light with MIS
|
|
19
16
|
* - calculateDirectLightingUnified — unified direct lighting (main entry)
|
|
20
17
|
*/
|
|
21
18
|
|
|
@@ -93,23 +90,6 @@ import {
|
|
|
93
90
|
|
|
94
91
|
const TWO_PI = 2.0 * PI;
|
|
95
92
|
|
|
96
|
-
// =============================================================================
|
|
97
|
-
// Helper: Fully Initialize LightSample
|
|
98
|
-
// =============================================================================
|
|
99
|
-
|
|
100
|
-
export const initLightSample = Fn( () => {
|
|
101
|
-
|
|
102
|
-
return LightSample( {
|
|
103
|
-
valid: tslBool( false ),
|
|
104
|
-
direction: vec3( 0.0, 1.0, 0.0 ),
|
|
105
|
-
emission: vec3( 0.0 ),
|
|
106
|
-
distance: float( 0.0 ),
|
|
107
|
-
pdf: float( 0.0 ),
|
|
108
|
-
lightType: int( LIGHT_TYPE_POINT ),
|
|
109
|
-
} );
|
|
110
|
-
|
|
111
|
-
} );
|
|
112
|
-
|
|
113
93
|
// =============================================================================
|
|
114
94
|
// Light Sampling Functions
|
|
115
95
|
// =============================================================================
|
|
@@ -170,66 +150,6 @@ export const sampleRectAreaLight = Fn( ( [ light, rayOrigin, ruv, lightSelection
|
|
|
170
150
|
|
|
171
151
|
} );
|
|
172
152
|
|
|
173
|
-
// Enhanced area light sampling - circle
|
|
174
|
-
export const sampleCircAreaLight = Fn( ( [ light, rayOrigin, ruv, lightSelectionPdf ] ) => {
|
|
175
|
-
|
|
176
|
-
const ls_valid = tslBool( false ).toVar();
|
|
177
|
-
const ls_direction = vec3( 0.0, 1.0, 0.0 ).toVar();
|
|
178
|
-
const ls_emission = vec3( 0.0 ).toVar();
|
|
179
|
-
const ls_distance = float( 0.0 ).toVar();
|
|
180
|
-
const ls_pdf = float( 0.0 ).toVar();
|
|
181
|
-
const ls_lightType = int( LIGHT_TYPE_POINT ).toVar();
|
|
182
|
-
|
|
183
|
-
// Validate light area to prevent NaN
|
|
184
|
-
If( light.area.greaterThan( 0.0 ), () => {
|
|
185
|
-
|
|
186
|
-
// Sample random position on circle
|
|
187
|
-
const r = float( 0.5 ).mul( sqrt( ruv.x ) ).toVar();
|
|
188
|
-
const theta = ruv.y.mul( TWO_PI ).toVar();
|
|
189
|
-
const x = r.mul( cos( theta ) ).toVar();
|
|
190
|
-
const y = r.mul( sin( theta ) ).toVar();
|
|
191
|
-
|
|
192
|
-
const randomPos = light.position
|
|
193
|
-
.add( light.u.mul( x ) )
|
|
194
|
-
.add( light.v.mul( y ) )
|
|
195
|
-
.toVar();
|
|
196
|
-
|
|
197
|
-
const toLight = randomPos.sub( rayOrigin ).toVar();
|
|
198
|
-
const lightDistSq = dot( toLight, toLight ).toVar();
|
|
199
|
-
|
|
200
|
-
// Guard against zero distance
|
|
201
|
-
If( lightDistSq.greaterThanEqual( 1e-10 ), () => {
|
|
202
|
-
|
|
203
|
-
const dist = sqrt( lightDistSq ).toVar();
|
|
204
|
-
const direction = toLight.div( dist ).toVar();
|
|
205
|
-
const lightNormal = normalize( cross( light.u, light.v ) ).toVar();
|
|
206
|
-
const cosAngle = dot( direction.negate(), lightNormal ).toVar();
|
|
207
|
-
|
|
208
|
-
ls_lightType.assign( int( LIGHT_TYPE_AREA ) );
|
|
209
|
-
ls_emission.assign( light.color.mul( light.intensity ) );
|
|
210
|
-
ls_distance.assign( dist );
|
|
211
|
-
ls_direction.assign( direction );
|
|
212
|
-
// Guard division
|
|
213
|
-
ls_pdf.assign(
|
|
214
|
-
lightDistSq.div( max( light.area.mul( max( cosAngle, 0.001 ) ), 1e-10 ) ).mul( lightSelectionPdf )
|
|
215
|
-
);
|
|
216
|
-
ls_valid.assign( cosAngle.greaterThan( 0.0 ) );
|
|
217
|
-
|
|
218
|
-
} );
|
|
219
|
-
|
|
220
|
-
} );
|
|
221
|
-
|
|
222
|
-
return LightSample( {
|
|
223
|
-
valid: ls_valid,
|
|
224
|
-
direction: ls_direction,
|
|
225
|
-
emission: ls_emission,
|
|
226
|
-
distance: ls_distance,
|
|
227
|
-
pdf: ls_pdf,
|
|
228
|
-
lightType: ls_lightType,
|
|
229
|
-
} );
|
|
230
|
-
|
|
231
|
-
} );
|
|
232
|
-
|
|
233
153
|
// Enhanced spot light sampling with radius support
|
|
234
154
|
export const sampleSpotLightWithRadius = Fn( ( [ light, rayOrigin, ruv, lightSelectionPdf ] ) => {
|
|
235
155
|
|
|
@@ -819,97 +739,6 @@ export const calculateMaterialPDF = Fn( ( [ viewDir, lightDir, normal, material
|
|
|
819
739
|
|
|
820
740
|
} );
|
|
821
741
|
|
|
822
|
-
// =============================================================================
|
|
823
|
-
// Enhanced Area Light Sampling with MIS
|
|
824
|
-
// =============================================================================
|
|
825
|
-
|
|
826
|
-
export const sampleAreaLightContribution = Fn( ( [
|
|
827
|
-
light,
|
|
828
|
-
worldWo,
|
|
829
|
-
hitPoint, hitNormal, material,
|
|
830
|
-
rayOrigin,
|
|
831
|
-
bounceIndex,
|
|
832
|
-
rngState,
|
|
833
|
-
// Shadow ray resources
|
|
834
|
-
bvhBuffer,
|
|
835
|
-
triangleBuffer,
|
|
836
|
-
materialBuffer,
|
|
837
|
-
] ) => {
|
|
838
|
-
|
|
839
|
-
const result = vec3( 0.0 ).toVar();
|
|
840
|
-
|
|
841
|
-
// Sample random position on light surface
|
|
842
|
-
const ruv_r1 = RandomValue( rngState ).toVar();
|
|
843
|
-
const ruv_r2 = RandomValue( rngState ).toVar();
|
|
844
|
-
const ruv = vec2( ruv_r1, ruv_r2 ).toVar();
|
|
845
|
-
const lightPos = light.position.add( light.u.mul( ruv.x.mul( 2.0 ).sub( 1.0 ) ) ).add( light.v.mul( ruv.y.mul( 2.0 ).sub( 1.0 ) ) ).toVar();
|
|
846
|
-
|
|
847
|
-
const toLight = lightPos.sub( rayOrigin ).toVar();
|
|
848
|
-
const lightDistSq = dot( toLight, toLight ).toVar();
|
|
849
|
-
|
|
850
|
-
// Guard against zero distance
|
|
851
|
-
If( lightDistSq.greaterThanEqual( 1e-10 ), () => {
|
|
852
|
-
|
|
853
|
-
const lightDist = sqrt( lightDistSq ).toVar();
|
|
854
|
-
const lightDir = toLight.div( lightDist ).toVar();
|
|
855
|
-
|
|
856
|
-
// Check if light is facing the surface
|
|
857
|
-
const lightNormal = normalize( cross( light.u, light.v ) ).toVar();
|
|
858
|
-
const lightFacing = dot( lightDir.negate(), lightNormal ).toVar();
|
|
859
|
-
|
|
860
|
-
If( lightFacing.greaterThan( 0.0 ), () => {
|
|
861
|
-
|
|
862
|
-
// Check if surface is facing the light
|
|
863
|
-
const surfaceFacing = dot( hitNormal, lightDir ).toVar();
|
|
864
|
-
|
|
865
|
-
If( surfaceFacing.greaterThan( 0.0 ), () => {
|
|
866
|
-
|
|
867
|
-
// Validate direction
|
|
868
|
-
If( isDirectionValid( { direction: lightDir, surfaceNormal: hitNormal } ), () => {
|
|
869
|
-
|
|
870
|
-
// Test for occlusion
|
|
871
|
-
const visibility = traceShadowRay(
|
|
872
|
-
rayOrigin, lightDir, lightDist.sub( 0.001 ), rngState,
|
|
873
|
-
traverseBVHShadow,
|
|
874
|
-
bvhBuffer,
|
|
875
|
-
triangleBuffer,
|
|
876
|
-
materialBuffer,
|
|
877
|
-
);
|
|
878
|
-
|
|
879
|
-
If( visibility.greaterThan( 0.0 ), () => {
|
|
880
|
-
|
|
881
|
-
// Calculate BRDF
|
|
882
|
-
const brdfColor = evaluateMaterialResponse( worldWo, lightDir, hitNormal, material );
|
|
883
|
-
|
|
884
|
-
// Calculate light PDF - guard division
|
|
885
|
-
const lightPdf = lightDistSq.div( max( light.area.mul( lightFacing ), EPSILON ) ).toVar();
|
|
886
|
-
|
|
887
|
-
// Calculate BRDF PDF for MIS
|
|
888
|
-
const brdfPdf = calculateMaterialPDF( worldWo, lightDir, hitNormal, material ).toVar();
|
|
889
|
-
|
|
890
|
-
// Apply MIS weighting
|
|
891
|
-
const misWeight = select( brdfPdf.greaterThan( 0.0 ), powerHeuristic( { pdf1: lightPdf, pdf2: brdfPdf } ), float( 1.0 ) ).toVar();
|
|
892
|
-
|
|
893
|
-
// Calculate final contribution - guard division
|
|
894
|
-
const lightEmission = light.color.mul( light.intensity ).toVar();
|
|
895
|
-
result.assign(
|
|
896
|
-
lightEmission.mul( brdfColor ).mul( surfaceFacing ).mul( visibility ).mul( misWeight ).div( max( lightPdf, MIN_PDF ) )
|
|
897
|
-
);
|
|
898
|
-
|
|
899
|
-
} );
|
|
900
|
-
|
|
901
|
-
} );
|
|
902
|
-
|
|
903
|
-
} );
|
|
904
|
-
|
|
905
|
-
} );
|
|
906
|
-
|
|
907
|
-
} );
|
|
908
|
-
|
|
909
|
-
return result;
|
|
910
|
-
|
|
911
|
-
} );
|
|
912
|
-
|
|
913
742
|
// =============================================================================
|
|
914
743
|
// Unified Direct Lighting System
|
|
915
744
|
// =============================================================================
|