rayzee 9.4.2 → 9.5.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 +223 -21
- package/dist/ActiveColor-YvuQq44O.js +13 -0
- package/dist/ArchiveImporter-xuUJiklP.js +4301 -0
- package/dist/AssetConfig-DSEntE65.js +30 -0
- package/dist/BidirectionalIntegrator-C5PKjJoT.js +934 -0
- package/dist/ColorManagement-CheBCPaW.js +1488 -0
- package/dist/EngineDefaults-DH3bI5t-.js +844 -0
- package/dist/EngineEvents-Ci8dVWCP.js +73 -0
- package/dist/EngineIssues-CIZnXHsF.js +72 -0
- package/dist/FileFormats-sPGJeCRc.js +102 -0
- package/dist/Logger-BnHghprG.js +97 -0
- package/dist/{NeuralRetouch-vkdXXyK2.js → NeuralRetouch-CW11yLZ_.js} +8 -6
- package/dist/{NeuralSuperRes-FVqRJKQV.js → NeuralSuperRes-BekaecYg.js} +55 -120
- package/dist/PathTracer-Ccl9KOuF.js +29729 -0
- package/dist/PhysicalSky-Dv2PMFGW.js +1219 -0
- package/dist/RayzeeRenderer-BQxnzCTD.js +3542 -0
- package/dist/SolarLimb-ken-JbDK.js +721 -0
- package/dist/ToneMapGPU-BHi5JzIq.js +177 -0
- package/dist/ViewTransforms-D0rTLBsX.js +255 -0
- package/dist/WorkingMatrix-C6y233hT.js +27 -0
- package/dist/ZipReader-gDSWr0I5.js +98 -0
- package/dist/addons/archives.es.js +3 -0
- package/dist/addons/bidirectional.es.js +2 -0
- package/dist/addons/color.es.js +3 -0
- package/dist/addons/formats.es.js +2 -0
- package/dist/addons/physical-sky.es.js +2 -0
- package/dist/addons/storage.es.js +3 -0
- package/dist/archiveFormats-BZw7TiDp.js +75 -0
- package/dist/identity-DrQsGT2o.js +39 -0
- package/dist/openStorage-DwcBRUj7.js +820 -0
- package/dist/rayzee-core.es.js +12 -0
- package/dist/rayzee.es.js +2266 -44079
- package/dist/rayzee.umd.js +485 -473
- package/dist/sceneCachePolicy-BmwYOYDu.js +183 -0
- package/dist/shared-BGBcSxBQ.js +17 -0
- package/package.json +23 -2
- package/src/Color/ActiveColor.js +34 -0
- package/src/Color/BasicColor.js +147 -0
- package/src/Color/ColorManagement.js +7 -31
- package/src/EngineDefaults.js +22 -8
- package/src/EngineEvents.js +2 -0
- package/src/EngineIssues.js +1 -0
- package/src/PathTracerApp.js +617 -4016
- package/src/Pipeline/PipelineContext.js +0 -4
- package/src/Platform.js +36 -0
- package/src/Processor/ArchiveCache.js +1 -1
- package/src/Processor/ArchiveImporter.js +1259 -0
- package/src/Processor/AssetLoader.js +322 -1746
- package/src/Processor/AtmosphereModel.js +1 -2
- package/src/Processor/BVHBuilder.js +4 -95
- package/src/Processor/EnvironmentExactTable.js +187 -0
- package/src/Processor/EquirectHDRInfo.js +30 -223
- package/src/Processor/FileFormats.js +116 -0
- package/src/Processor/GLTFDecoders.js +85 -0
- package/src/Processor/GLTFDiffuseTransmission.js +63 -0
- package/src/Processor/GeometryExtractor.js +12 -1
- package/src/Processor/IESParser.js +47 -35
- package/src/Processor/KernelManager.js +35 -2
- package/src/Processor/MaterialPacking.js +8 -7
- package/src/Processor/PBRT/PBRTMaterials.js +129 -41
- package/src/Processor/PBRT/PBRTParser.js +30 -17
- package/src/Processor/PBRT/PBRTSceneBuilder.js +548 -37
- package/src/Processor/PBRT/PBRTTextureBake.js +154 -0
- package/src/Processor/PBRT/PFM.js +59 -0
- package/src/Processor/PackedRayBuffer.js +116 -9
- package/src/Processor/ParallelBVHBuilder.js +3 -6
- package/src/Processor/PhysicalSky.js +5 -4
- package/src/Processor/PlatformImageLoader.js +50 -6
- package/src/Processor/QueueManager.js +12 -3
- package/src/Processor/ReinsertionOptimizer.js +2 -2
- package/src/Processor/SceneProcessor.js +15 -55
- package/src/Processor/ShaderBuilder.js +6 -16
- package/src/Processor/SolarLimb.js +3 -0
- package/src/Processor/TextureCreator.js +1 -1
- package/src/Processor/Workers/BVHSubtreeWorker.js +1 -34
- package/src/Processor/Workers/BVHWorker.js +5 -18
- package/src/Processor/Workers/CDFWorker.js +6 -183
- package/src/Processor/archiveFormats.js +7 -0
- package/src/Processor/utils.js +2 -1
- package/src/RayzeeRenderer.js +3902 -0
- package/src/RenderSettings.js +49 -41
- package/src/Stages/ASVGF.js +5 -7
- package/src/Stages/AutoExposure.js +3 -12
- package/src/Stages/Compositor.js +8 -23
- package/src/Stages/NRD.js +9 -2
- package/src/Stages/NormalDepth.js +9 -6
- package/src/Stages/PathTracer.js +254 -38
- package/src/Stages/PathTracerStage.js +11 -16
- package/src/Storage/BLASCache.js +1 -1
- package/src/Storage/CDFCache.js +18 -17
- package/src/Storage/DownloadCache.js +1 -1
- package/src/Storage/SceneGraphCodec.js +46 -3
- package/src/Storage/SpillStore.js +1 -1
- package/src/Storage/StorageManager.js +2 -4
- package/src/Storage/areas.js +4 -0
- package/src/TSL/BVHTraversal.js +62 -24
- package/src/TSL/Bidirectional.js +204 -0
- package/src/TSL/BidirectionalLamps.js +132 -0
- package/src/TSL/Clearcoat.js +15 -6
- package/src/TSL/Common.js +19 -14
- package/src/TSL/ConnectKernel.js +147 -0
- package/src/TSL/Debugger.js +1 -1
- package/src/TSL/Displacement.js +4 -3
- package/src/TSL/EmissiveSampling.js +64 -17
- package/src/TSL/Environment.js +77 -106
- package/src/TSL/EnvironmentCDF.js +85 -39
- package/src/TSL/ExtendKernel.js +8 -3
- package/src/TSL/FinalWriteKernel.js +4 -4
- package/src/TSL/GenerateKernel.js +18 -4
- package/src/TSL/HitFacet.js +35 -5
- package/src/TSL/LightBVHSampling.js +40 -9
- package/src/TSL/LightGenerateKernel.js +293 -0
- package/src/TSL/LightGuide.js +198 -0
- package/src/TSL/LightSplatKernel.js +165 -0
- package/src/TSL/LightsCore.js +13 -38
- package/src/TSL/LightsDirect.js +28 -29
- package/src/TSL/LightsIndirect.js +3 -1
- package/src/TSL/LightsSampling.js +90 -38
- package/src/TSL/MaterialEvaluation.js +113 -31
- package/src/TSL/MaterialProperties.js +44 -17
- package/src/TSL/MaterialTransmission.js +52 -47
- package/src/TSL/MergeKernel.js +218 -0
- package/src/TSL/PathTracerCore.js +65 -29
- package/src/TSL/Random.js +4 -0
- package/src/TSL/SceneResources.js +51 -0
- package/src/TSL/ShadeKernel.js +881 -103
- package/src/TSL/Struct.js +10 -0
- package/src/TSL/Sun.js +1 -1
- package/src/TSL/TextureSampling.js +65 -60
- package/src/addons/archives.js +14 -0
- package/src/addons/bidirectional.js +13 -0
- package/src/addons/color.js +32 -0
- package/src/addons/formats.js +16 -0
- package/src/addons/physicalSky.js +14 -0
- package/src/addons/storage.js +19 -0
- package/src/core.js +35 -0
- package/src/index.js +3 -1
- package/src/integrators/BidirectionalIntegrator.js +832 -0
- package/src/managers/EnvironmentManager.js +85 -33
- package/src/managers/MaterialDataManager.js +42 -0
- package/src/managers/UniformManager.js +0 -1
- package/dist/ToneMapGPU-DKQM1pMd.js +0 -552
- package/dist/rolldown-runtime-CAFD8bLK.js +0 -11
- package/src/Processor/TreeletOptimizer.js +0 -574
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
select,
|
|
38
38
|
If,
|
|
39
39
|
Loop,
|
|
40
|
+
sampler,
|
|
40
41
|
} from 'three/tsl';
|
|
41
42
|
|
|
42
43
|
import {
|
|
@@ -71,8 +72,8 @@ import {
|
|
|
71
72
|
} from './LightsDirect.js';
|
|
72
73
|
|
|
73
74
|
import { traverseBVHShadow } from './BVHTraversal.js';
|
|
74
|
-
import { evaluateMaterialResponseFromDots } from './MaterialEvaluation.js';
|
|
75
|
-
import { calculateBSDFSamplingPDFFromMaterial } from './MaterialProperties.js';
|
|
75
|
+
import { evaluateMaterialResponseFromDots, evaluateDiffuseTransmission } from './MaterialEvaluation.js';
|
|
76
|
+
import { calculateBSDFSamplingPDFFromMaterial, diffuseTransmissionWeight, diffuseTransmissionPdf } from './MaterialProperties.js';
|
|
76
77
|
import { getRandomSample1D, getRandomSample2D, SAMPLER_DIM_AUX_BASE } from './Random.js';
|
|
77
78
|
import {
|
|
78
79
|
PI,
|
|
@@ -84,9 +85,7 @@ import {
|
|
|
84
85
|
offsetRayOrigin,
|
|
85
86
|
SHADOW_END,
|
|
86
87
|
} from './Common.js';
|
|
87
|
-
import {
|
|
88
|
-
sampleEquirectProbability,
|
|
89
|
-
} from './Environment.js';
|
|
88
|
+
import { sampleEnvironment, sampleEnvironmentExact } from './Environment.js';
|
|
90
89
|
import { shadowTerminatorOrigin } from './ShadowTerminator.js';
|
|
91
90
|
import { sunRadianceToward, sampleSunDisc } from './Sun.js';
|
|
92
91
|
|
|
@@ -757,8 +756,20 @@ export const calculateMaterialPDFFromDots = Fn( ( [ material, dots ] ) => {
|
|
|
757
756
|
|
|
758
757
|
export const calculateMaterialPDF = Fn( ( [ viewDir, lightDir, normal, material ] ) => {
|
|
759
758
|
|
|
760
|
-
const
|
|
761
|
-
|
|
759
|
+
const pdf = float( 0.0 ).toVar();
|
|
760
|
+
|
|
761
|
+
If( dot( normal, lightDir ).lessThan( 0.0 ).and( material.diffuseTransmission.greaterThan( 0.0 ) ), () => {
|
|
762
|
+
|
|
763
|
+
pdf.assign( max( diffuseTransmissionPdf( diffuseTransmissionWeight( material ), dot( normal, lightDir ).negate() ), 1e-8 ) );
|
|
764
|
+
|
|
765
|
+
} ).Else( () => {
|
|
766
|
+
|
|
767
|
+
const dots = DotProducts.wrap( computeDotProductsAniso( normal, viewDir, lightDir, material ) );
|
|
768
|
+
pdf.assign( calculateMaterialPDFFromDots( material, dots ) );
|
|
769
|
+
|
|
770
|
+
} );
|
|
771
|
+
|
|
772
|
+
return pdf;
|
|
762
773
|
|
|
763
774
|
} );
|
|
764
775
|
|
|
@@ -766,8 +777,9 @@ export const calculateMaterialPDF = Fn( ( [ viewDir, lightDir, normal, material
|
|
|
766
777
|
// Unified Direct Lighting System
|
|
767
778
|
// =============================================================================
|
|
768
779
|
|
|
769
|
-
// Optimized direct lighting function with importance-based sampling and better MIS
|
|
770
|
-
|
|
780
|
+
// Optimized direct lighting function with importance-based sampling and better MIS. `throughSurfaces` (compile
|
|
781
|
+
// time) adds light from behind a surface that passes it through diffusely; without it the code is exactly as before.
|
|
782
|
+
const makeDirectLighting = ( throughSurfaces ) => Fn( ( [
|
|
771
783
|
// Surface hit data
|
|
772
784
|
hitPoint, hitNormal, geomNormal, material,
|
|
773
785
|
// View direction
|
|
@@ -791,7 +803,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
791
803
|
// Environment resources
|
|
792
804
|
envTexture, environmentIntensity, envMatrix,
|
|
793
805
|
envCDFTexture,
|
|
794
|
-
envTotalSum,
|
|
806
|
+
envTotalSum, envResolution,
|
|
795
807
|
enableEnvironmentLight,
|
|
796
808
|
// Shadow catcher: when true, also accumulate the unoccluded (visibility=1) reference
|
|
797
809
|
// at every light/env site so the caller can form a shadow ratio. Dead path otherwise.
|
|
@@ -817,6 +829,46 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
817
829
|
traceShadowRay( origin, dir, maxDist, traverseBVHShadow, bvhBuffer, triangleBuffer, materialBuffer )
|
|
818
830
|
);
|
|
819
831
|
|
|
832
|
+
// Light from behind a surface that passes light through diffusely: its shadow ray leaves from the other side,
|
|
833
|
+
// and the BSDF is the transmission lobe.
|
|
834
|
+
const throughSurface = L => ( throughSurfaces
|
|
835
|
+
? material.diffuseTransmission.greaterThan( 0.0 ).and( dot( hitNormal, L ).lessThan( 0.0 ) ).and( dot( L, geomNormal ).lessThan( 0.0 ) )
|
|
836
|
+
: tslBool( false ) );
|
|
837
|
+
const backOrigin = throughSurfaces ? offsetRayOrigin( hitPoint, facetNormal.negate() ) : null;
|
|
838
|
+
const inFront = L => dot( hitNormal, L ).greaterThan( 0.0 ).and( isDirectionValid( { direction: L, surfaceNormal: geomNormal } ) );
|
|
839
|
+
const reachesSurface = ( L, back ) => ( throughSurfaces ? back.or( inFront( L ) ) : inFront( L ) );
|
|
840
|
+
const cosineTo = L => ( throughSurfaces ? abs( dot( hitNormal, L ) ) : max( float( 0.0 ), dot( hitNormal, L ) ) );
|
|
841
|
+
const originFor = ( back, front ) => ( throughSurfaces ? select( back, backOrigin, front ) : front );
|
|
842
|
+
const reflection = L => {
|
|
843
|
+
|
|
844
|
+
// Share H + dot products between BRDF eval and PDF — otherwise each
|
|
845
|
+
// would recompute normalize(V+L) + 5 dot products independently.
|
|
846
|
+
const dots = DotProducts.wrap( computeDotProductsAniso( hitNormal, viewDir, L, material ) );
|
|
847
|
+
return { value: evaluateMaterialResponseFromDots( material, dots ), pdf: calculateMaterialPDFFromDots( material, dots ).toVar() };
|
|
848
|
+
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
const bsdfToward = ( L, back ) => {
|
|
852
|
+
|
|
853
|
+
if ( ! throughSurfaces ) return reflection( L );
|
|
854
|
+
const value = vec3( 0.0 ).toVar();
|
|
855
|
+
const pdf = float( 0.0 ).toVar();
|
|
856
|
+
If( back, () => {
|
|
857
|
+
|
|
858
|
+
value.assign( evaluateDiffuseTransmission( material, max( dot( hitNormal, viewDir ), 0.001 ) ) );
|
|
859
|
+
pdf.assign( max( diffuseTransmissionPdf( diffuseTransmissionWeight( material ), dot( hitNormal, L ).negate() ), 1e-8 ) );
|
|
860
|
+
|
|
861
|
+
} ).Else( () => {
|
|
862
|
+
|
|
863
|
+
const r = reflection( L );
|
|
864
|
+
value.assign( r.value );
|
|
865
|
+
pdf.assign( r.pdf );
|
|
866
|
+
|
|
867
|
+
} );
|
|
868
|
+
return { value, pdf };
|
|
869
|
+
|
|
870
|
+
};
|
|
871
|
+
|
|
820
872
|
|
|
821
873
|
// NEE and BSDF-hit both run at every vertex, one sample each, so the MIS weights are the
|
|
822
874
|
// plain power heuristic over the raw densities. Picking one strategy stochastically and
|
|
@@ -849,24 +901,21 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
849
901
|
|
|
850
902
|
If( lightSample.valid.and( lightSample.pdf.greaterThan( 0.0 ) ), () => {
|
|
851
903
|
|
|
852
|
-
const
|
|
904
|
+
const back = throughSurface( lightSample.direction ).toVar();
|
|
905
|
+
const NoL = cosineTo( lightSample.direction ).toVar();
|
|
853
906
|
|
|
854
|
-
If(
|
|
907
|
+
If( reachesSurface( lightSample.direction, back ), () => {
|
|
855
908
|
|
|
856
909
|
// Light geometry is measured from the hit point; only the visibility ray starts on the lifted
|
|
857
910
|
// origin, aimed at the sampled point and stopped a relative hair short of it.
|
|
858
|
-
const lightOrigin = lightShadowOrigin( lightSample.direction ).toVar();
|
|
911
|
+
const lightOrigin = originFor( back, lightShadowOrigin( lightSample.direction ) ).toVar();
|
|
859
912
|
const toSample = hitPoint.add( lightSample.direction.mul( lightSample.distance ) ).sub( lightOrigin ).toVar();
|
|
860
913
|
const shadowDistance = length( toSample ).toVar();
|
|
861
914
|
const visibility = shadow( lightOrigin, toSample.div( shadowDistance ), shadowDistance.mul( SHADOW_END ) );
|
|
862
915
|
|
|
863
916
|
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
864
917
|
|
|
865
|
-
|
|
866
|
-
// would recompute normalize(V+L) + 5 dot products independently.
|
|
867
|
-
const sharedDots = DotProducts.wrap( computeDotProductsAniso( hitNormal, viewDir, lightSample.direction, material ) );
|
|
868
|
-
const brdfValue = evaluateMaterialResponseFromDots( material, sharedDots );
|
|
869
|
-
const bPdf = calculateMaterialPDFFromDots( material, sharedDots ).toVar();
|
|
918
|
+
const { value: brdfValue, pdf: bPdf } = bsdfToward( lightSample.direction, back );
|
|
870
919
|
|
|
871
920
|
// Power heuristic only for area lights — they are the only type the BRDF
|
|
872
921
|
// path can intersect, so elsewhere MIS would just delete energy.
|
|
@@ -902,9 +951,10 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
902
951
|
|
|
903
952
|
If( brdfSamplePdf.greaterThan( 0.0 ).and( numAreaLights.greaterThan( int( 0 ) ) ), () => {
|
|
904
953
|
|
|
905
|
-
const
|
|
954
|
+
const back = throughSurface( brdfSampleDirection ).toVar();
|
|
955
|
+
const NoL = cosineTo( brdfSampleDirection ).toVar();
|
|
906
956
|
|
|
907
|
-
If(
|
|
957
|
+
If( reachesSurface( brdfSampleDirection, back ), () => {
|
|
908
958
|
|
|
909
959
|
// Nearest hit — the ray stops at the first emitter, so a brighter one behind it is
|
|
910
960
|
// the wrong emitter and the wrong pdf. No importance gate: skipping a light drops
|
|
@@ -936,9 +986,10 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
936
986
|
If( nearestLight.greaterThanEqual( int( 0 ) ), () => {
|
|
937
987
|
|
|
938
988
|
const light = AreaLight.wrap( getAreaLight( areaLightsBuffer, nearestLight ) );
|
|
939
|
-
const
|
|
989
|
+
const hitOrigin = originFor( back, rayOrigin ).toVar();
|
|
990
|
+
const toLight = hitPoint.add( brdfSampleDirection.mul( nearestT ) ).sub( hitOrigin ).toVar();
|
|
940
991
|
const shadowDistance = length( toLight ).toVar();
|
|
941
|
-
const visibility = shadow(
|
|
992
|
+
const visibility = shadow( hitOrigin, toLight.div( shadowDistance ), shadowDistance.mul( SHADOW_END ) );
|
|
942
993
|
|
|
943
994
|
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
944
995
|
|
|
@@ -1011,33 +1062,31 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
1011
1062
|
radiance.assign( sunRadianceToward( direction, sunDirection, sunRadiance, sunParams ).mul( environmentIntensity ) );
|
|
1012
1063
|
lightPdf.assign( select( radiance.x.add( radiance.y ).add( radiance.z ).greaterThan( 0.0 ), float( 1.0 ).div( sunParams.y ), float( 0.0 ) ) );
|
|
1013
1064
|
|
|
1014
|
-
} ).
|
|
1065
|
+
} ).ElseIf( envTotalSum.greaterThan( 0.0 ), () => {
|
|
1015
1066
|
|
|
1016
|
-
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
lightPdf.assign( envSampleResult.w );
|
|
1067
|
+
// Drawn from the table its density is read back from; a total of 0 is no table, or a black map.
|
|
1068
|
+
const drawn = sampleEnvironmentExact( envCDFTexture, envMatrix, envResolution,
|
|
1069
|
+
getRandomSample2D( pixelCoord, int( 0 ), dimBase.add( int( 1 ) ), rngState, resolution, frame ) );
|
|
1070
|
+
direction.assign( drawn.direction );
|
|
1071
|
+
radiance.assign( sampleEnvironment( {
|
|
1072
|
+
tex: envTexture, samp: sampler( envTexture ), direction, environmentMatrix: envMatrix, environmentIntensity, enableEnvironmentLight: float( 1.0 ),
|
|
1073
|
+
} ).xyz );
|
|
1074
|
+
lightPdf.assign( drawn.pdf );
|
|
1025
1075
|
|
|
1026
1076
|
} );
|
|
1027
1077
|
|
|
1028
1078
|
If( lightPdf.greaterThan( 0.0 ), () => {
|
|
1029
1079
|
|
|
1030
|
-
const
|
|
1080
|
+
const back = throughSurface( direction ).toVar();
|
|
1081
|
+
const NoL = cosineTo( direction ).toVar();
|
|
1031
1082
|
|
|
1032
|
-
If(
|
|
1083
|
+
If( reachesSurface( direction, back ), () => {
|
|
1033
1084
|
|
|
1034
|
-
const visibility = shadow( lightShadowOrigin( direction ), direction, float( 1e20 ) );
|
|
1085
|
+
const visibility = shadow( originFor( back, lightShadowOrigin( direction ) ), direction, float( 1e20 ) );
|
|
1035
1086
|
|
|
1036
1087
|
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
1037
1088
|
|
|
1038
|
-
const
|
|
1039
|
-
const brdfValue = evaluateMaterialResponseFromDots( material, envDots );
|
|
1040
|
-
const bPdf = calculateMaterialPDFFromDots( material, envDots ).toVar();
|
|
1089
|
+
const { value: brdfValue, pdf: bPdf } = bsdfToward( direction, back );
|
|
1041
1090
|
|
|
1042
1091
|
// The map pairs with the balance heuristic — optimal for its MIS-compensated pdf
|
|
1043
1092
|
// (Karlík et al. 2019); the sun, a small bright source, with the power heuristic.
|
|
@@ -1073,3 +1122,6 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
1073
1122
|
return DirectLightingDual( { shadowed: totalContribution, unoccluded: unoccludedContribution } );
|
|
1074
1123
|
|
|
1075
1124
|
} );
|
|
1125
|
+
|
|
1126
|
+
export const calculateDirectLightingUnified = /*@__PURE__*/ makeDirectLighting( false );
|
|
1127
|
+
export const calculateDirectLightingThroughSurfaces = /*@__PURE__*/ makeDirectLighting( true );
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Fn, float, vec3,
|
|
2
|
+
Fn, float, vec3, dot,
|
|
3
3
|
If, max, min, clamp, mix
|
|
4
4
|
} from 'three/tsl';
|
|
5
5
|
|
|
6
6
|
import { DotProducts, DFGResult, BaseFresnel } from './Struct.js';
|
|
7
7
|
import { PI_INV, MIN_CLEARCOAT_ROUGHNESS, computeDotProductsAniso } from './Common.js';
|
|
8
8
|
import { fresnelSchlick, fresnelDielectric, dielectricFresnelWeight } from './Fresnel.js';
|
|
9
|
+
import { materialLayers } from './SceneResources.js';
|
|
9
10
|
import {
|
|
10
11
|
DistributionGGX, SheenDistribution, VisibilitySheen, VisibilityGGXSmithCorrelated,
|
|
11
12
|
sheenDirectionalAlbedo, evaluateSpecularDFG, baseFresnelParams,
|
|
@@ -21,27 +22,32 @@ import { evalIridescence } from './MaterialProperties.js';
|
|
|
21
22
|
// Main Material Response Evaluation
|
|
22
23
|
// -----------------------------------------------------------------------------
|
|
23
24
|
|
|
25
|
+
// A rough plain dielectric: its BSDF is a bare Lambert term (no specular lobe, no DFG budget).
|
|
26
|
+
const isLambertian = ( material ) => material.roughness.greaterThan( 0.98 )
|
|
27
|
+
.and( material.metalness.lessThan( 0.02 ) )
|
|
28
|
+
.and( material.transmission.equal( 0.0 ) )
|
|
29
|
+
.and( material.clearcoat.equal( 0.0 ) )
|
|
30
|
+
.and( material.sheen.equal( 0.0 ) )
|
|
31
|
+
.and( material.iridescence.equal( 0.0 ) );
|
|
32
|
+
|
|
24
33
|
// Body of evaluateMaterialResponse taking precomputed dot products. Callers
|
|
25
34
|
// that also need calculateMaterialPDF for the same (V, L, N) should share dots
|
|
26
35
|
// to save one computeDotProducts call.
|
|
27
36
|
// Roughness 0 marks an exact mirror (ShadeKernel): its delta lobe is left out of this BSDF, and
|
|
28
37
|
// deltaOnly returns that lobe's reflectance instead, under the same sheen and coat attenuation.
|
|
29
|
-
const makeEvaluateMaterialResponse = ( deltaOnly ) => Fn( ( [ material, dots ] ) => {
|
|
38
|
+
const makeEvaluateMaterialResponse = ( deltaOnly ) => Fn( ( [ material, dots ], builder ) => {
|
|
30
39
|
|
|
40
|
+
const layers = materialLayers( builder );
|
|
31
41
|
const result = vec3( 0.0 ).toVar();
|
|
32
42
|
|
|
33
43
|
// Early exit for purely diffuse materials (skip if iridescent)
|
|
34
44
|
// `sheen` is in the guard because this fast path returns a bare Lambert term: without it a
|
|
35
45
|
// rough sheen material lost its sheen lobe entirely — silently, since the fast path is only
|
|
36
46
|
// taken above roughness 0.98.
|
|
37
|
-
If( material
|
|
38
|
-
.and( material.metalness.lessThan( 0.02 ) )
|
|
39
|
-
.and( material.transmission.equal( 0.0 ) )
|
|
40
|
-
.and( material.clearcoat.equal( 0.0 ) )
|
|
41
|
-
.and( material.sheen.equal( 0.0 ) )
|
|
42
|
-
.and( material.iridescence.equal( 0.0 ) ), () => {
|
|
47
|
+
If( isLambertian( material ), () => {
|
|
43
48
|
|
|
44
|
-
result.assign( material.color.rgb.mul( float( 1.0 ).sub( material.metalness ) )
|
|
49
|
+
result.assign( material.color.rgb.mul( float( 1.0 ).sub( material.metalness ) )
|
|
50
|
+
.mul( float( 1.0 ).sub( material.diffuseTransmission ) ).mul( PI_INV ) );
|
|
45
51
|
|
|
46
52
|
} ).Else( () => {
|
|
47
53
|
|
|
@@ -51,7 +57,7 @@ const makeEvaluateMaterialResponse = ( deltaOnly ) => Fn( ( [ material, dots ] )
|
|
|
51
57
|
|
|
52
58
|
// Modify material color for dispersive materials to enhance color separation
|
|
53
59
|
const materialColor = material.color.rgb.toVar();
|
|
54
|
-
If( material.dispersion.greaterThan( 0.0 ).and( material.transmission.greaterThan( 0.5 ) ), () => {
|
|
60
|
+
if ( layers.dispersion ) If( material.dispersion.greaterThan( 0.0 ).and( material.transmission.greaterThan( 0.5 ) ), () => {
|
|
55
61
|
|
|
56
62
|
// For highly dispersive transmissive materials, boost color saturation
|
|
57
63
|
const dispersionEffect = clamp( material.dispersion.mul( 0.1 ), 0.0, 0.8 );
|
|
@@ -67,7 +73,7 @@ const makeEvaluateMaterialResponse = ( deltaOnly ) => Fn( ( [ material, dots ] )
|
|
|
67
73
|
} );
|
|
68
74
|
|
|
69
75
|
// Add iridescence effect if enabled
|
|
70
|
-
If( material.iridescence.greaterThan( 0.0 ), () => {
|
|
76
|
+
if ( layers.iridescence ) If( material.iridescence.greaterThan( 0.0 ), () => {
|
|
71
77
|
|
|
72
78
|
// Per glTF KHR_materials_iridescence spec: use max thickness when no texture
|
|
73
79
|
const thickness = material.iridescenceThicknessRange.y;
|
|
@@ -78,29 +84,29 @@ const makeEvaluateMaterialResponse = ( deltaOnly ) => Fn( ( [ material, dots ] )
|
|
|
78
84
|
|
|
79
85
|
// Dielectric on the exact Fresnel curve, metal and iridescence on Schlick.
|
|
80
86
|
const Fd = mix( bf.f0, vec3( bf.f90 ), dielectricFresnelWeight( dots.VoH, bf.eta ) );
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
fresnelSchlick( dots.VoH, iridF ),
|
|
84
|
-
material.iridescence,
|
|
85
|
-
).toVar();
|
|
87
|
+
const Fbase = mix( Fd, fresnelSchlick( dots.VoH, bf.F0m ), material.metalness );
|
|
88
|
+
const F = ( layers.iridescence ? mix( Fbase, fresnelSchlick( dots.VoH, iridF ), material.iridescence ) : Fbase ).toVar();
|
|
86
89
|
|
|
87
90
|
// Single-scatter specular BRDF (anisotropic when material.anisotropy > 0; the aniso
|
|
88
91
|
// visibility term already carries the 1/(4·NoV·NoL) denominator)
|
|
89
92
|
const specularSS = vec3( 0.0 ).toVar();
|
|
90
|
-
|
|
93
|
+
const isotropic = () => {
|
|
94
|
+
|
|
95
|
+
const D = DistributionGGX( dots.NoH, material.roughness );
|
|
96
|
+
const Vis = VisibilityGGXSmithCorrelated( dots.NoV, dots.NoL, material.roughness );
|
|
97
|
+
specularSS.assign( D.mul( Vis ).mul( F ) );
|
|
98
|
+
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
if ( layers.anisotropy ) If( material.anisotropy.greaterThan( 0.0 ), () => {
|
|
91
102
|
|
|
92
103
|
const a = computeAnisoAlphas( material.roughness, material.anisotropy );
|
|
93
104
|
const Da = DistributionGGXAniso( a.x, a.y, dots.NoH, dots.ToH, dots.BoH );
|
|
94
105
|
const Va = VisibilityGGXAniso( a.x, a.y, dots.ToV, dots.BoV, dots.ToL, dots.BoL, dots.NoV, dots.NoL );
|
|
95
106
|
specularSS.assign( F.mul( Da.mul( Va ) ) );
|
|
96
107
|
|
|
97
|
-
} ).ElseIf( material.roughness.greaterThan( 0.0 ),
|
|
98
|
-
|
|
99
|
-
const D = DistributionGGX( dots.NoH, material.roughness );
|
|
100
|
-
const Vis = VisibilityGGXSmithCorrelated( dots.NoV, dots.NoL, material.roughness );
|
|
101
|
-
specularSS.assign( D.mul( Vis ).mul( F ) );
|
|
102
|
-
|
|
103
|
-
} );
|
|
108
|
+
} ).ElseIf( material.roughness.greaterThan( 0.0 ), isotropic );
|
|
109
|
+
else If( material.roughness.greaterThan( 0.0 ), isotropic );
|
|
104
110
|
|
|
105
111
|
// Compensation factor and total directional albedo from the same table fetch.
|
|
106
112
|
const dfg = DFGResult.wrap( evaluateSpecularDFG(
|
|
@@ -112,15 +118,17 @@ const makeEvaluateMaterialResponse = ( deltaOnly ) => Fn( ( [ material, dots ] )
|
|
|
112
118
|
// Diffuse energy budget from hemisphere-integrated specular albedo (includes multiscatter)
|
|
113
119
|
// Transmission removes energy from diffuse just as metalness does — KHR_materials_transmission
|
|
114
120
|
// defines transmission as replacing the diffuse component.
|
|
121
|
+
// Diffuse transmission takes its share of the diffuse lobe to the other side (evaluateDiffuseTransmission).
|
|
115
122
|
const kD = vec3( 1.0 ).sub( dfg.E_total )
|
|
116
123
|
.mul( float( 1.0 ).sub( material.metalness ) )
|
|
117
|
-
.mul( float( 1.0 ).sub( material.transmission ) )
|
|
124
|
+
.mul( float( 1.0 ).sub( material.transmission ) )
|
|
125
|
+
.mul( float( 1.0 ).sub( material.diffuseTransmission ) );
|
|
118
126
|
const diffuse = deltaOnly ? vec3( 0.0 ) : kD.mul( materialColor ).mul( PI_INV );
|
|
119
127
|
|
|
120
128
|
const baseLayer = diffuse.add( specular ).toVar();
|
|
121
129
|
|
|
122
|
-
|
|
123
|
-
If( material.sheen.greaterThan( 0.0 ), () => {
|
|
130
|
+
if ( ! layers.sheen ) result.assign( baseLayer );
|
|
131
|
+
else If( material.sheen.greaterThan( 0.0 ), () => {
|
|
124
132
|
|
|
125
133
|
// D · V, not · NoL — callers apply the cosine when they integrate.
|
|
126
134
|
const sheenDist = SheenDistribution( dots.NoH, material.sheenRoughness );
|
|
@@ -143,7 +151,7 @@ const makeEvaluateMaterialResponse = ( deltaOnly ) => Fn( ( [ material, dots ] )
|
|
|
143
151
|
|
|
144
152
|
// The coat lives here, not in a separate layered-BRDF function: every strategy has to
|
|
145
153
|
// evaluate the same integrand or MIS is biased however good the weights are.
|
|
146
|
-
If( material.clearcoat.greaterThan( 0.0 ), () => {
|
|
154
|
+
if ( layers.clearcoat ) If( material.clearcoat.greaterThan( 0.0 ), () => {
|
|
147
155
|
|
|
148
156
|
const ccRoughness = max( material.clearcoatRoughness, MIN_CLEARCOAT_ROUGHNESS );
|
|
149
157
|
const ccF0 = vec3( 0.04 ); // IOR 1.5
|
|
@@ -176,12 +184,86 @@ const makeEvaluateMaterialResponse = ( deltaOnly ) => Fn( ( [ material, dots ] )
|
|
|
176
184
|
export const evaluateMaterialResponseFromDots = /*@__PURE__*/ makeEvaluateMaterialResponse( false );
|
|
177
185
|
export const evaluateSpecularDeltaFromDots = /*@__PURE__*/ makeEvaluateMaterialResponse( true );
|
|
178
186
|
|
|
187
|
+
/**
|
|
188
|
+
* KHR_materials_diffuse_transmission's BTDF, for light arriving from the far side of a thin surface: the diffuse
|
|
189
|
+
* budget the reflected diffuse lobe has (what the specular lobes leave, less metal and specular transmission) × the
|
|
190
|
+
* transmitted share × its colour / π, under the same sheen and coat attenuation. Zero when the material has none.
|
|
191
|
+
*/
|
|
192
|
+
export const evaluateDiffuseTransmission = Fn( ( [ material, NoV ], builder ) => {
|
|
193
|
+
|
|
194
|
+
const layers = materialLayers( builder );
|
|
195
|
+
const result = vec3( 0.0 ).toVar();
|
|
196
|
+
|
|
197
|
+
if ( layers.diffuseTransmission ) If( material.diffuseTransmission.greaterThan( 0.0 ), () => {
|
|
198
|
+
|
|
199
|
+
const kD = vec3( float( 1.0 ).sub( material.metalness ).mul( float( 1.0 ).sub( material.transmission ) ) ).toVar();
|
|
200
|
+
|
|
201
|
+
If( isLambertian( material ).not(), () => {
|
|
202
|
+
|
|
203
|
+
const bf = BaseFresnel.wrap( baseFresnelParams( material, material.color.rgb ) );
|
|
204
|
+
const F0 = bf.F0.toVar();
|
|
205
|
+
const iridF = vec3( 0.0 ).toVar();
|
|
206
|
+
if ( layers.iridescence ) If( material.iridescence.greaterThan( 0.0 ), () => {
|
|
207
|
+
|
|
208
|
+
// No half vector across the surface: the iridescent F0 at normal incidence to the view.
|
|
209
|
+
iridF.assign( evalIridescence( float( 1.0 ), material.iridescenceIOR, NoV, material.iridescenceThicknessRange.y, F0 ) );
|
|
210
|
+
F0.assign( mix( F0, iridF, material.iridescence ) );
|
|
211
|
+
|
|
212
|
+
} );
|
|
213
|
+
const dfg = DFGResult.wrap( evaluateSpecularDFG(
|
|
214
|
+
bf.f0, bf.f90, bf.eta, bf.F0m, material.metalness, iridF, material.iridescence, F0,
|
|
215
|
+
NoV, material.roughness,
|
|
216
|
+
) );
|
|
217
|
+
kD.mulAssign( vec3( 1.0 ).sub( dfg.E_total ) );
|
|
218
|
+
|
|
219
|
+
} );
|
|
220
|
+
|
|
221
|
+
result.assign( kD.mul( material.diffuseTransmission ).mul( material.diffuseTransmissionColor ).mul( PI_INV ) );
|
|
222
|
+
|
|
223
|
+
if ( layers.sheen ) If( material.sheen.greaterThan( 0.0 ), () => {
|
|
224
|
+
|
|
225
|
+
const sheenE = sheenDirectionalAlbedo( NoV, material.sheenRoughness );
|
|
226
|
+
result.mulAssign( vec3( 1.0 ).sub( clamp( material.sheenColor.mul( material.sheen ).mul( sheenE ), vec3( 0.0 ), vec3( 1.0 ) ) ) );
|
|
227
|
+
|
|
228
|
+
} );
|
|
229
|
+
|
|
230
|
+
if ( layers.clearcoat ) If( material.clearcoat.greaterThan( 0.0 ), () => {
|
|
231
|
+
|
|
232
|
+
const ccDfg = DFGResult.wrap( evaluateSpecularDFG(
|
|
233
|
+
vec3( 0.04 ), float( 1.0 ), float( 1.5 ), vec3( 0.0 ), float( 0.0 ), vec3( 0.0 ), float( 0.0 ), vec3( 0.04 ),
|
|
234
|
+
NoV, max( material.clearcoatRoughness, MIN_CLEARCOAT_ROUGHNESS ),
|
|
235
|
+
) );
|
|
236
|
+
result.mulAssign( vec3( 1.0 ).sub( ccDfg.E_total.mul( material.clearcoat ) ) );
|
|
237
|
+
|
|
238
|
+
} );
|
|
239
|
+
|
|
240
|
+
} );
|
|
241
|
+
|
|
242
|
+
return result;
|
|
243
|
+
|
|
244
|
+
} );
|
|
245
|
+
|
|
179
246
|
// Wrapper that computes dot products internally. Use this when you don't already
|
|
180
247
|
// have dots; otherwise prefer evaluateMaterialResponseFromDots to share the work.
|
|
181
|
-
|
|
248
|
+
// L below N reaches only the diffuse transmission lobe.
|
|
249
|
+
export const evaluateMaterialResponse = Fn( ( [ V, L, N, material ], builder ) => {
|
|
250
|
+
|
|
251
|
+
const result = vec3( 0.0 ).toVar();
|
|
252
|
+
const reflected = () => {
|
|
182
253
|
|
|
183
|
-
|
|
184
|
-
|
|
254
|
+
const dots = DotProducts.wrap( computeDotProductsAniso( N, V, L, material ) );
|
|
255
|
+
result.assign( evaluateMaterialResponseFromDots( material, dots ) );
|
|
256
|
+
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
if ( ! materialLayers( builder ).diffuseTransmission ) reflected();
|
|
260
|
+
else If( dot( N, L ).lessThan( 0.0 ).and( material.diffuseTransmission.greaterThan( 0.0 ) ), () => {
|
|
261
|
+
|
|
262
|
+
result.assign( evaluateDiffuseTransmission( material, max( dot( N, V ), 0.001 ) ) );
|
|
263
|
+
|
|
264
|
+
} ).Else( reflected );
|
|
265
|
+
|
|
266
|
+
return result;
|
|
185
267
|
|
|
186
268
|
} );
|
|
187
269
|
|
|
@@ -6,6 +6,7 @@ import { BRDFWeights, MaterialCache, MaterialClassification, DFGResult, BaseFres
|
|
|
6
6
|
import { PI, PI_INV, TWO_PI, EPSILON, MIN_ROUGHNESS, MIN_CLEARCOAT_ROUGHNESS, MAX_ROUGHNESS, XYZ_TO_REC709, square, classifyMaterial } from './Common.js';
|
|
7
7
|
|
|
8
8
|
import { fresnelSchlickFloat, fresnel0ToIor, iorToFresnel0Vec3, iorToFresnel0, dielectricF0 } from './Fresnel.js';
|
|
9
|
+
import { materialLayers } from './SceneResources.js';
|
|
9
10
|
|
|
10
11
|
// -----------------------------------------------------------------------------
|
|
11
12
|
// Microfacet Distribution Functions
|
|
@@ -407,8 +408,9 @@ export const evalIridescence = Fn( ( [ outsideIOR, eta2, cosTheta1, thinFilmThic
|
|
|
407
408
|
// BRDF Weight Calculation
|
|
408
409
|
// -----------------------------------------------------------------------------
|
|
409
410
|
|
|
410
|
-
export const calculateBRDFWeights = Fn( ( [ material, mc, cache ] ) => {
|
|
411
|
+
export const calculateBRDFWeights = Fn( ( [ material, mc, cache ], builder ) => {
|
|
411
412
|
|
|
413
|
+
const layers = materialLayers( builder );
|
|
412
414
|
// Use precomputed values from cache
|
|
413
415
|
const invRoughness = cache.invRoughness;
|
|
414
416
|
const metalFactor = cache.metalFactor;
|
|
@@ -438,10 +440,10 @@ export const calculateBRDFWeights = Fn( ( [ material, mc, cache ] ) => {
|
|
|
438
440
|
const diffuse = float( 1.0 ).sub( baseSpecularWeight )
|
|
439
441
|
.mul( float( 1.0 ).sub( material.metalness ) )
|
|
440
442
|
.mul( float( 1.0 ).sub( material.transmission ) ).toVar();
|
|
441
|
-
const sheen = material.sheen.mul( cache.maxSheenColor ).toVar();
|
|
443
|
+
const sheen = ( layers.sheen ? material.sheen.mul( cache.maxSheenColor ) : float( 0.0 ) ).toVar();
|
|
442
444
|
|
|
443
445
|
const clearcoat = float( 0.0 ).toVar();
|
|
444
|
-
If( mc.hasClearcoat, () => {
|
|
446
|
+
if ( layers.clearcoat ) If( mc.hasClearcoat, () => {
|
|
445
447
|
|
|
446
448
|
clearcoat.assign( material.clearcoat.mul( invRoughness ).mul( 0.4 ) );
|
|
447
449
|
|
|
@@ -471,17 +473,22 @@ export const calculateBRDFWeights = Fn( ( [ material, mc, cache ] ) => {
|
|
|
471
473
|
// Iridescence: shifts energy from diffuse to specular since it modifies specular F0
|
|
472
474
|
// This preserves the total weight (no inflation) while increasing specular importance
|
|
473
475
|
const iridescenceBase = invRoughness.mul( mc.isSmooth.select( float( 0.6 ), float( 0.5 ) ) );
|
|
474
|
-
|
|
476
|
+
// Off, the weight is 0 but the shift is kept: min( 0, diffuse ) still moves a negative diffuse weight to specular.
|
|
477
|
+
const iridescenceWeight = layers.iridescence ? material.iridescence.mul( iridescenceBase )
|
|
475
478
|
.mul( float( 0.5 ).add( float( 0.5 ).mul(
|
|
476
479
|
material.iridescenceThicknessRange.y.sub( material.iridescenceThicknessRange.x ).div( 1000.0 )
|
|
477
480
|
) ) )
|
|
478
|
-
.mul( float( 0.5 ).add( float( 0.5 ).mul( material.iridescenceIOR.div( 2.0 ) ) ) );
|
|
481
|
+
.mul( float( 0.5 ).add( float( 0.5 ).mul( material.iridescenceIOR.div( 2.0 ) ) ) ) : float( 0.0 );
|
|
479
482
|
const iridescenceShift = min( iridescenceWeight, diffuse );
|
|
480
483
|
specular.addAssign( iridescenceShift );
|
|
481
484
|
diffuse.subAssign( iridescenceShift );
|
|
482
485
|
|
|
486
|
+
// The diffuse lobe's transmitted share is drawn on the other side, in proportion.
|
|
487
|
+
const diffuseTransmission = ( layers.diffuseTransmission ? diffuse.mul( material.diffuseTransmission ) : float( 0.0 ) ).toVar();
|
|
488
|
+
diffuse.subAssign( diffuseTransmission );
|
|
489
|
+
|
|
483
490
|
// Single normalization pass
|
|
484
|
-
const total = specular.add( diffuse ).add( sheen ).add( clearcoat ).add( transmission );
|
|
491
|
+
const total = specular.add( diffuse ).add( sheen ).add( clearcoat ).add( transmission ).add( diffuseTransmission );
|
|
485
492
|
const invTotal = float( 1.0 ).div( max( total, 0.001 ) );
|
|
486
493
|
|
|
487
494
|
return BRDFWeights( {
|
|
@@ -491,6 +498,7 @@ export const calculateBRDFWeights = Fn( ( [ material, mc, cache ] ) => {
|
|
|
491
498
|
clearcoat: clearcoat.mul( invTotal ),
|
|
492
499
|
transmission: transmission.mul( invTotal ),
|
|
493
500
|
iridescence: float( 0.0 ),
|
|
501
|
+
diffuseTransmission: diffuseTransmission.mul( invTotal ),
|
|
494
502
|
} );
|
|
495
503
|
|
|
496
504
|
} );
|
|
@@ -508,8 +516,9 @@ export const calculateBRDFWeights = Fn( ( [ material, mc, cache ] ) => {
|
|
|
508
516
|
//
|
|
509
517
|
// Transmission is absent because it only produces NoL < 0. `weights` is normalised over all
|
|
510
518
|
// lobes including transmission, so omitting its term is exact, not an approximation.
|
|
511
|
-
export const calculateBSDFSamplingPDF = Fn( ( [ material, weights, dots ] ) => {
|
|
519
|
+
export const calculateBSDFSamplingPDF = Fn( ( [ material, weights, dots ], builder ) => {
|
|
512
520
|
|
|
521
|
+
const layers = materialLayers( builder );
|
|
513
522
|
const pdf = float( 0.0 ).toVar();
|
|
514
523
|
|
|
515
524
|
If( dots.NoL.greaterThan( 0.0 ), () => {
|
|
@@ -517,21 +526,24 @@ export const calculateBSDFSamplingPDF = Fn( ( [ material, weights, dots ] ) => {
|
|
|
517
526
|
pdf.addAssign( weights.diffuse.mul( dots.NoL ).mul( PI_INV ) );
|
|
518
527
|
|
|
519
528
|
const specPdf = float( 0.0 ).toVar();
|
|
520
|
-
|
|
529
|
+
// Raw roughness, not clamped: sampleGGXVNDF is called with material.roughness, and a
|
|
530
|
+
// clamp here would report a density the sampler never drew from.
|
|
531
|
+
const isotropic = () => {
|
|
521
532
|
|
|
522
|
-
|
|
523
|
-
specPdf.assign( calculateVNDFPDFAniso( a.x, a.y, dots.NoH, dots.ToH, dots.BoH, dots.NoV, dots.ToV, dots.BoV ) );
|
|
533
|
+
specPdf.assign( calculateVNDFPDF( dots.NoH, dots.NoV, material.roughness ) );
|
|
524
534
|
|
|
525
|
-
}
|
|
535
|
+
};
|
|
526
536
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
specPdf.assign( calculateVNDFPDF( dots.NoH, dots.NoV, material.roughness ) );
|
|
537
|
+
if ( ! layers.anisotropy ) isotropic();
|
|
538
|
+
else If( material.anisotropy.greaterThan( 0.0 ), () => {
|
|
530
539
|
|
|
531
|
-
|
|
540
|
+
const a = computeAnisoAlphas( material.roughness, material.anisotropy );
|
|
541
|
+
specPdf.assign( calculateVNDFPDFAniso( a.x, a.y, dots.NoH, dots.ToH, dots.BoH, dots.NoV, dots.ToV, dots.BoV ) );
|
|
542
|
+
|
|
543
|
+
} ).Else( isotropic );
|
|
532
544
|
pdf.addAssign( weights.specular.mul( specPdf ) );
|
|
533
545
|
|
|
534
|
-
If( weights.sheen.greaterThan( 0.0 ), () => {
|
|
546
|
+
if ( layers.sheen ) If( weights.sheen.greaterThan( 0.0 ), () => {
|
|
535
547
|
|
|
536
548
|
const sheenPdf = SheenDistribution( dots.NoH, material.sheenRoughness )
|
|
537
549
|
.mul( dots.NoH ).div( max( float( 4.0 ).mul( dots.VoH ), EPSILON ) );
|
|
@@ -539,7 +551,7 @@ export const calculateBSDFSamplingPDF = Fn( ( [ material, weights, dots ] ) => {
|
|
|
539
551
|
|
|
540
552
|
} );
|
|
541
553
|
|
|
542
|
-
If( weights.clearcoat.greaterThan( 0.0 ), () => {
|
|
554
|
+
if ( layers.clearcoat ) If( weights.clearcoat.greaterThan( 0.0 ), () => {
|
|
543
555
|
|
|
544
556
|
const ccRoughness = clamp( material.clearcoatRoughness, MIN_CLEARCOAT_ROUGHNESS, MAX_ROUGHNESS );
|
|
545
557
|
pdf.addAssign( weights.clearcoat.mul( calculateVNDFPDF( dots.NoH, dots.NoV, ccRoughness ) ) );
|
|
@@ -552,6 +564,21 @@ export const calculateBSDFSamplingPDF = Fn( ( [ material, weights, dots ] ) => {
|
|
|
552
564
|
|
|
553
565
|
} );
|
|
554
566
|
|
|
567
|
+
// The sampling density below the surface (cosBelow = −N·L > 0): only the diffuse transmission lobe draws there,
|
|
568
|
+
// chosen with probability `weight` (BRDFWeights.diffuseTransmission).
|
|
569
|
+
export const diffuseTransmissionPdf = Fn( ( [ weight, cosBelow ] ) => {
|
|
570
|
+
|
|
571
|
+
return weight.mul( cosBelow ).mul( PI_INV );
|
|
572
|
+
|
|
573
|
+
} );
|
|
574
|
+
|
|
575
|
+
// The diffuse transmission lobe's selection probability, for samplers outside the classify-once path.
|
|
576
|
+
export const diffuseTransmissionWeight = Fn( ( [ material ] ) => {
|
|
577
|
+
|
|
578
|
+
return BRDFWeights.wrap( calculateBRDFWeightsFromMaterial( material ) ).diffuseTransmission;
|
|
579
|
+
|
|
580
|
+
} );
|
|
581
|
+
|
|
555
582
|
// Selection probabilities from `material` alone, for samplers outside the wavefront's
|
|
556
583
|
// classify-once path. Must be the same weights whose density calculateBSDFSamplingPDF reports.
|
|
557
584
|
export const calculateBRDFWeightsFromMaterial = Fn( ( [ material ] ) => {
|