rayzee 11.7.0 → 11.9.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 +31 -6
- package/dist/{ArchiveImporter-DYcRXeKE.js → ArchiveImporter-BJxszU_k.js} +2353 -1356
- package/dist/{BidirectionalIntegrator-BvjRpbTP.js → BidirectionalIntegrator-DWg66vAd.js} +302 -301
- package/dist/{BufferLayout-xPRH9jYD.js → BufferLayout-Bzgccg1o.js} +1 -0
- package/dist/{ColorManagement-oeEk7oSu.js → ColorManagement-B_HjAiy2.js} +1 -1
- package/dist/{EngineIssues-CIZnXHsF.js → EngineIssues-CfJiBaNj.js} +19 -8
- package/dist/{NeuralRetouch-ClaMiKYg.js → NeuralRetouch-BD9E-Yht.js} +2 -2
- package/dist/{NeuralSuperRes-WNEXun9s.js → NeuralSuperRes-C8Mf3tJX.js} +2 -2
- package/dist/{PathTracer-B9wZPPVL.js → PathTracer-DeE9UJ1n.js} +3530 -3402
- package/dist/{PhysicalSky-DcjItqKi.js → PhysicalSky-cKy75-oR.js} +45 -45
- package/dist/{RayzeeRenderer-p-NeE6E2.js → RayzeeRenderer-CzLyBA3G.js} +378 -374
- package/dist/SceneMetadata-C17UfOEp.js +183 -0
- package/dist/{SolarLimb-bZE6y63S.js → SolarLimb-DRYy8tln.js} +184 -172
- package/dist/{ToneMapGPU-sAs8wdgI.js → ToneMapGPU-4GPwoG_c.js} +13 -6
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/color.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/addons/storage.es.js +1 -1
- package/dist/{openStorage-Fe9BoS1_.js → openStorage-CdyVjmps.js} +1 -1
- package/dist/rayzee-core.es.js +7 -7
- package/dist/rayzee.es.js +821 -794
- package/dist/rayzee.umd.js +112 -103
- package/dist/{sceneCachePolicy-B9w3BnaP.js → sceneCachePolicy-ywbGKHD3.js} +9 -6
- package/package.json +1 -1
- package/src/EngineDefaults.js +1 -0
- package/src/EngineIssues.js +21 -6
- package/src/Passes/AIUpscaler.js +4 -0
- package/src/Passes/OIDNDenoiser.js +8 -6
- package/src/Passes/OIDNTemporalHistory.js +1 -1
- package/src/PathTracerApp.js +10 -1
- package/src/Processor/ArchiveImporter.js +106 -27
- package/src/Processor/AssetLoader.js +22 -0
- package/src/Processor/BufferLayout.js +3 -3
- package/src/Processor/GeometryExtractor.js +12 -4
- package/src/Processor/LightSerializer.js +6 -2
- package/src/Processor/MaterialPacking.js +2 -2
- package/src/Processor/PBRT/PBRTMaterials.js +49 -13
- package/src/Processor/PBRT/PBRTParser.js +144 -22
- package/src/Processor/PBRT/PBRTSceneBuilder.js +287 -67
- package/src/Processor/PBRT/PBRTTextureBake.js +191 -9
- package/src/Processor/PBRT/index.js +19 -4
- package/src/Processor/PlatformImageLoader.js +9 -0
- package/src/Processor/SceneProcessor.js +42 -26
- package/src/Processor/TextureCreator.js +44 -4
- package/src/Processor/ToneMapCPU.js +34 -5
- package/src/Processor/ToneMapGPU.js +15 -9
- package/src/Processor/USD/USDScene.js +981 -17
- package/src/RayzeeRenderer.js +85 -28
- package/src/Stages/AutoExposure.js +21 -8
- package/src/Stages/Compositor.js +39 -1
- package/src/Stages/DenoiserSettings.js +0 -4
- package/src/Stages/LocalExposure.js +44 -18
- package/src/Stages/NRD.js +53 -24
- package/src/Stages/NormalDepth.js +12 -6
- package/src/Stages/PathTracer.js +27 -1
- package/src/Stages/PathTracerStage.js +14 -2
- package/src/Storage/SceneGraphCodec.js +6 -3
- package/src/TSL/BVHTraversal.js +33 -20
- package/src/TSL/CameraRay.js +12 -1
- package/src/TSL/Common.js +25 -8
- package/src/TSL/DebugKernel.js +3 -2
- package/src/TSL/Displacement.js +66 -113
- package/src/TSL/FinalWriteKernel.js +4 -7
- package/src/TSL/GenerateKernel.js +6 -2
- package/src/TSL/LightSplatKernel.js +7 -4
- package/src/TSL/LightsCore.js +6 -2
- package/src/TSL/LightsDirect.js +33 -11
- package/src/TSL/LightsSampling.js +45 -12
- package/src/TSL/SceneResources.js +19 -0
- package/src/TSL/ShadeKernel.js +3 -4
- package/src/TSL/Struct.js +1 -0
- package/src/integrators/BidirectionalIntegrator.js +1 -0
- package/src/managers/CameraManager.js +9 -0
- package/src/managers/DenoisingManager.js +1 -0
- package/src/managers/InteractionManager.js +6 -0
- package/src/managers/MaterialDataManager.js +11 -0
- package/src/managers/UniformManager.js +3 -1
- package/dist/SceneMetadata-D2Sh-0e-.js +0 -110
package/src/Stages/NRD.js
CHANGED
|
@@ -8,7 +8,7 @@ import { createStorageTexture } from '../Processor/StorageTexturePool.js';
|
|
|
8
8
|
import { sanitizeRGB, sanitize1, FP16_MAX } from '../TSL/Common.js';
|
|
9
9
|
import { cameraRayPoint, cameraRayUniforms, isOrthographic } from '../TSL/CameraRay.js';
|
|
10
10
|
import {
|
|
11
|
-
NRD_DEFAULTS, NRD_HIT_DIST_A, NRD_HIT_DIST_B,
|
|
11
|
+
NRD_DEFAULTS, NRD_HIT_DIST_A, NRD_HIT_DIST_B, GBUFFER_MISS_THRESHOLD as MISS_THRESHOLD,
|
|
12
12
|
} from './DenoiserSettings.js';
|
|
13
13
|
|
|
14
14
|
const NRD_EPS = 1e-6;
|
|
@@ -25,6 +25,12 @@ const FIREFLY_RADIUS_SCALE = 0.1;
|
|
|
25
25
|
const FIREFLY_FAST_RELATIVE_INTENSITY = 4.0;
|
|
26
26
|
const ALMOST_ZERO_ANGLE_COS = Math.cos( 89.0 * Math.PI / 180.0 );
|
|
27
27
|
const CATROM_SHARPNESS = 0.5;
|
|
28
|
+
// The input fuses diffuse and specular, so it is demodulated by both lobes' albedo (NRD_MaterialFactors summed).
|
|
29
|
+
const SPECULAR_F0 = 0.04;
|
|
30
|
+
// A sample this dark reflects nothing diffusely: it carries emission, the environment or specular alone.
|
|
31
|
+
const BLACK_ALBEDO = 0.1 * SPECULAR_F0;
|
|
32
|
+
// Carried past the pre-pass in dataT.z, above the four occlusion bits.
|
|
33
|
+
const BLACK_BIT = 16;
|
|
28
34
|
const WG_SIZE = 16;
|
|
29
35
|
|
|
30
36
|
const PASS = {
|
|
@@ -111,6 +117,12 @@ const clampNegativeToZero = ( v ) => vec4( linearToYCoCg( yCoCgToLinear( v.xyz )
|
|
|
111
117
|
const stdDev = ( m1, m2 ) => sqrt( m2.sub( m1.mul( m1 ) ).abs() );
|
|
112
118
|
// rotator = (cos, sin, -sin, cos)
|
|
113
119
|
const rotate2 = ( rot, v ) => vec2( rot.x.mul( v.x ).add( rot.y.mul( v.y ) ), rot.z.mul( v.x ).add( rot.w.mul( v.y ) ) );
|
|
120
|
+
const demodAlbedo = ( a ) => a.mul( 1.0 - SPECULAR_F0 ).add( SPECULAR_F0 );
|
|
121
|
+
const isBlack = ( a ) => max( a.x, max( a.y, a.z ) ).lessThan( BLACK_ALBEDO );
|
|
122
|
+
const isBlackData = ( data ) => data.z.greaterThan( BLACK_BIT - 0.5 );
|
|
123
|
+
// NormalDepth negates an emitter's roughness, as NRD packs a material ID into its normal-roughness guide.
|
|
124
|
+
const isEmitter = ( guideW ) => guideW.lessThan( 0.0 );
|
|
125
|
+
const guideRoughness = ( guideW ) => guideW.abs().clamp( 0.05, 1.0 );
|
|
114
126
|
const denanify = ( w, s ) => select( w.equal( 0.0 ), vec4( 0.0 ), s );
|
|
115
127
|
|
|
116
128
|
function hash01( n ) {
|
|
@@ -256,7 +268,7 @@ export class NRD extends RenderStage {
|
|
|
256
268
|
this._geomPrev = tex();
|
|
257
269
|
this._stabA = tex(); // .x stabilized luma
|
|
258
270
|
this._stabB = tex();
|
|
259
|
-
this._dataT = tex(); // .x fast luma, .y accumSpeed, .z occlusion bits, .w catrom ok
|
|
271
|
+
this._dataT = tex(); // .x fast luma, .y accumSpeed, .z occlusion bits (+ BLACK_BIT), .w catrom ok
|
|
260
272
|
|
|
261
273
|
}
|
|
262
274
|
|
|
@@ -382,6 +394,13 @@ export class NRD extends RenderStage {
|
|
|
382
394
|
|
|
383
395
|
}
|
|
384
396
|
|
|
397
|
+
// Taps whose radiance is no estimate of the centre's lighting.
|
|
398
|
+
_excluded( zs, guideW, tapBlack, centerBlack ) {
|
|
399
|
+
|
|
400
|
+
return zs.greaterThanEqual( MISS_THRESHOLD ).or( isEmitter( guideW ) ).or( tapBlack.and( centerBlack.not() ) );
|
|
401
|
+
|
|
402
|
+
}
|
|
403
|
+
|
|
385
404
|
_bilinear( uv ) {
|
|
386
405
|
|
|
387
406
|
const t = uv.mul( vec2( this.resW, this.resH ) ).sub( 0.5 );
|
|
@@ -524,11 +543,9 @@ export class NRD extends RenderStage {
|
|
|
524
543
|
const inputFrames = this.inputFrames;
|
|
525
544
|
|
|
526
545
|
const readSignal = isPre
|
|
527
|
-
? ( c ) => {
|
|
546
|
+
? ( c, alb ) => {
|
|
528
547
|
|
|
529
|
-
const
|
|
530
|
-
const alb = textureLoad( albedoTex, c );
|
|
531
|
-
const lighting = sanitizeRGB( col.xyz.div( max( alb.xyz, vec3( ALBEDO_EPS ) ) ) );
|
|
548
|
+
const lighting = sanitizeRGB( textureLoad( colorTex, c ).xyz.div( demodAlbedo( alb.xyz ) ) );
|
|
532
549
|
return vec4( linearToYCoCg( lighting ), sanitize1( alb.w, FP16_MAX ).min( 1.0 ) );
|
|
533
550
|
|
|
534
551
|
}
|
|
@@ -547,10 +564,12 @@ export class NRD extends RenderStage {
|
|
|
547
564
|
const isSky = dist.greaterThanEqual( MISS_THRESHOLD );
|
|
548
565
|
const sn = textureLoad( snTex, coord ).toVar();
|
|
549
566
|
const N = normalize( sn.xyz.mul( 2.0 ).sub( 1.0 ) ).toVar();
|
|
550
|
-
const roughness = sn.w
|
|
567
|
+
const roughness = guideRoughness( sn.w ).toVar();
|
|
551
568
|
const viewZ = float( MISS_THRESHOLD ).toVar();
|
|
552
569
|
|
|
553
|
-
const
|
|
570
|
+
const albedo = isPre ? textureLoad( albedoTex, coord ) : null;
|
|
571
|
+
const black = ( isPre ? isBlack( albedo ) : isBlackData( textureLoad( dataTex, coord ) ) ).toVar();
|
|
572
|
+
const result = readSignal( coord, albedo ).toVar();
|
|
554
573
|
|
|
555
574
|
If( isSky.not(), () => {
|
|
556
575
|
|
|
@@ -558,7 +577,7 @@ export class NRD extends RenderStage {
|
|
|
558
577
|
const X = this._worldPosAt( pixelUv, dist, cwm, cpi ).toVar();
|
|
559
578
|
viewZ.assign( camView.mul( vec4( X, 1.0 ) ).z.abs() );
|
|
560
579
|
|
|
561
|
-
If( maxRadiusU.greaterThan( 0.0 ), () => {
|
|
580
|
+
If( maxRadiusU.greaterThan( 0.0 ).and( isEmitter( sn.w ).not() ), () => {
|
|
562
581
|
|
|
563
582
|
const V = this._viewVector( X, camPos, this.viewVector );
|
|
564
583
|
const NoV = dot( N, V ).abs().toVar();
|
|
@@ -625,11 +644,13 @@ export class NRD extends RenderStage {
|
|
|
625
644
|
|
|
626
645
|
const angle = acosApproxPositive( dot( N, Ns ) );
|
|
627
646
|
w.mulAssign( weightSmooth( angle, normalParam, float( 0.0 ) ) );
|
|
628
|
-
w.mulAssign( weightSmooth( sns.w, roughParams.x, roughParams.y ) );
|
|
647
|
+
w.mulAssign( weightSmooth( guideRoughness( sns.w ), roughParams.x, roughParams.y ) );
|
|
629
648
|
w.mulAssign( weightSmooth( dot( N, Xs ), geomParams.x, geomParams.y ) );
|
|
630
|
-
|
|
649
|
+
const albS = isPre ? textureLoad( albedoTex, pos ) : null;
|
|
650
|
+
const blackS = isPre ? isBlack( albS ) : isBlackData( textureLoad( dataTex, pos ) );
|
|
651
|
+
w.assign( select( this._excluded( zs, sns.w, blackS, black ), float( 0.0 ), w ) );
|
|
631
652
|
|
|
632
|
-
const s = denanify( w, readSignal( pos ) );
|
|
653
|
+
const s = denanify( w, readSignal( pos, albS ) );
|
|
633
654
|
w.mulAssign( minHitDistWeight.add( weightExp( s.w, hdParams.x, hdParams.y ) ) );
|
|
634
655
|
|
|
635
656
|
sum.addAssign( w );
|
|
@@ -675,6 +696,7 @@ export class NRD extends RenderStage {
|
|
|
675
696
|
const geomTex = this._nGeom;
|
|
676
697
|
const outTex = this._tmpB;
|
|
677
698
|
const dataOut = this._dataT;
|
|
699
|
+
const albedoTex = this._albedoTexNode;
|
|
678
700
|
const camView = this.camView;
|
|
679
701
|
const prevCamView = this.prevCamView;
|
|
680
702
|
const camPos = this.camPos;
|
|
@@ -709,7 +731,7 @@ export class NRD extends RenderStage {
|
|
|
709
731
|
|
|
710
732
|
const sn = textureLoad( snTex, coord );
|
|
711
733
|
const N = normalize( sn.xyz.mul( 2.0 ).sub( 1.0 ) ).toVar();
|
|
712
|
-
const roughness = sn.w
|
|
734
|
+
const roughness = guideRoughness( sn.w ).toVar();
|
|
713
735
|
|
|
714
736
|
const pixelUv = this._pixelUv( gx, gy );
|
|
715
737
|
const X = this._worldPosAt( pixelUv, dist, cwm, cpi ).toVar();
|
|
@@ -806,7 +828,8 @@ export class NRD extends RenderStage {
|
|
|
806
828
|
const fastClamped = min( fast, history.x.mul( maxRel ).mul( FIREFLY_FAST_RELATIVE_INTENSITY ) );
|
|
807
829
|
fast.assign( mix( fast, fastClamped, aff ) );
|
|
808
830
|
|
|
809
|
-
|
|
831
|
+
const blackBit = select( isBlack( textureLoad( albedoTex, coord ) ), float( BLACK_BIT ), float( 0.0 ) );
|
|
832
|
+
textureStore( dataOut, uvec, vec4( fast, accumSpeed, fbits.add( blackBit ), select( allowCatRom, float( 1.0 ), float( 0.0 ) ) ) ).toWriteOnly();
|
|
810
833
|
|
|
811
834
|
} );
|
|
812
835
|
|
|
@@ -862,7 +885,7 @@ export class NRD extends RenderStage {
|
|
|
862
885
|
|
|
863
886
|
const sn = textureLoad( snTex, coord );
|
|
864
887
|
const N = normalize( sn.xyz.mul( 2.0 ).sub( 1.0 ) ).toVar();
|
|
865
|
-
const roughness = sn.w
|
|
888
|
+
const roughness = guideRoughness( sn.w ).toVar();
|
|
866
889
|
const pixelUv = this._pixelUv( gx, gy );
|
|
867
890
|
const X = this._worldPosAt( pixelUv, dist, cwm, cpi ).toVar();
|
|
868
891
|
const viewZ = camView.mul( vec4( X, 1.0 ) ).z.abs();
|
|
@@ -881,13 +904,16 @@ export class NRD extends RenderStage {
|
|
|
881
904
|
.mul( mix( float( 0.25 ).add( sqrt( hitDistFactor ).mul( 0.75 ) ), float( 1.0 ), nonLinear ) );
|
|
882
905
|
const strideR = round( stride ).toVar();
|
|
883
906
|
|
|
884
|
-
|
|
907
|
+
const emitter = isEmitter( sn.w ).toVar();
|
|
908
|
+
|
|
909
|
+
If( strideR.notEqual( 0.0 ).and( emitter.not() ), () => {
|
|
885
910
|
|
|
886
911
|
const normalParam = normalWeightParam( nonLinear, u.lobeAngleFraction, roughness, u.lobeVolumePercent ).toVar();
|
|
887
912
|
const geomParams = geometryWeightParams( u.planeDistSensitivity, frustumSize, X, N ).toVar();
|
|
888
913
|
const hdParams = hitDistWeightParams( centerH, nonLinear ).toVar();
|
|
889
914
|
const relaxedRough = relaxedRoughnessWeightParams( roughness.mul( roughness ), sqrt( u.roughnessFraction ), ROUGHNESS_SENSITIVITY ).toVar();
|
|
890
915
|
|
|
916
|
+
const black = isBlackData( data ).toVar();
|
|
891
917
|
const sumd = float( 1.0 ).add( frameNum ).toVar();
|
|
892
918
|
diff.mulAssign( sumd );
|
|
893
919
|
|
|
@@ -909,10 +935,12 @@ export class NRD extends RenderStage {
|
|
|
909
935
|
const angle = acosApproxPositive( dot( Ns, N ) );
|
|
910
936
|
|
|
911
937
|
const w = weightExp( angle, normalParam, float( 0.0 ) ).toVar();
|
|
912
|
-
|
|
913
|
-
w.mulAssign(
|
|
938
|
+
const rs = guideRoughness( sns.w );
|
|
939
|
+
w.mulAssign( weightExp( rs.mul( rs ), relaxedRough.x, relaxedRough.y ) );
|
|
940
|
+
const dataS = textureLoad( dataTex, pos );
|
|
941
|
+
w.mulAssign( float( 1.0 ).add( dataS.y ) );
|
|
914
942
|
w.mulAssign( weightSmooth( dot( N, Xs ), geomParams.x, geomParams.y ) );
|
|
915
|
-
w.assign( select( zs.
|
|
943
|
+
w.assign( select( this._excluded( zs, sns.w, isBlackData( dataS ), black ), float( 0.0 ), w ) );
|
|
916
944
|
|
|
917
945
|
const s = denanify( w, textureLoad( inTex, pos ) );
|
|
918
946
|
w.mulAssign( weightExp( s.w, hdParams.x, hdParams.y ) );
|
|
@@ -1014,7 +1042,7 @@ export class NRD extends RenderStage {
|
|
|
1014
1042
|
|
|
1015
1043
|
}
|
|
1016
1044
|
|
|
1017
|
-
textureStore( outTex, uvec, changeLuma( diff, diffLuma ) ).toWriteOnly();
|
|
1045
|
+
textureStore( outTex, uvec, select( emitter, diff, changeLuma( diff, diffLuma ) ) ).toWriteOnly();
|
|
1018
1046
|
|
|
1019
1047
|
} );
|
|
1020
1048
|
|
|
@@ -1067,7 +1095,8 @@ export class NRD extends RenderStage {
|
|
|
1067
1095
|
|
|
1068
1096
|
} );
|
|
1069
1097
|
|
|
1070
|
-
const
|
|
1098
|
+
const guideW = textureLoad( snTex, coord ).w;
|
|
1099
|
+
const roughness = guideRoughness( guideW );
|
|
1071
1100
|
const pixelUv = this._pixelUv( gx, gy );
|
|
1072
1101
|
const mv = textureLoad( motionTex, coord );
|
|
1073
1102
|
const smbUv = pixelUv.sub( mv.xy ).toVar();
|
|
@@ -1107,7 +1136,7 @@ export class NRD extends RenderStage {
|
|
|
1107
1136
|
m2.divAssign( 9.0 );
|
|
1108
1137
|
const sigma = stdDev( m1, m2 ).toVar();
|
|
1109
1138
|
|
|
1110
|
-
If( accumSpeed.lessThan( u.historyFixFrameNum ), () => {
|
|
1139
|
+
If( accumSpeed.lessThan( u.historyFixFrameNum ).and( isEmitter( guideW ).not() ), () => {
|
|
1111
1140
|
|
|
1112
1141
|
diffLuma.assign( min( diffLuma, m1.mul( float( 1.2 ).add( float( 1.0 ).div( accumSpeed.add( 1.0 ) ) ) ) ) );
|
|
1113
1142
|
|
|
@@ -1145,7 +1174,7 @@ export class NRD extends RenderStage {
|
|
|
1145
1174
|
|
|
1146
1175
|
// Alpha stays the path tracer's coverage — Compositor reads it under transparentBackground.
|
|
1147
1176
|
const albedo = textureLoad( albedoTex, coord );
|
|
1148
|
-
const lighting = yCoCgToLinear( diff.xyz ).mul(
|
|
1177
|
+
const lighting = yCoCgToLinear( diff.xyz ).mul( demodAlbedo( albedo.xyz ) );
|
|
1149
1178
|
const out = vec4( lighting, color.w ).toVar();
|
|
1150
1179
|
|
|
1151
1180
|
If( debugMode.equal( int( 1 ) ), () => {
|
|
@@ -1168,7 +1197,7 @@ export class NRD extends RenderStage {
|
|
|
1168
1197
|
|
|
1169
1198
|
} ).ElseIf( debugMode.equal( int( 5 ) ), () => {
|
|
1170
1199
|
|
|
1171
|
-
out.assign( vec4( vec3(
|
|
1200
|
+
out.assign( vec4( vec3( float( bits.bitAnd( uint( 15 ) ) ).div( 15.0 ) ), 1.0 ) );
|
|
1172
1201
|
|
|
1173
1202
|
} );
|
|
1174
1203
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fn, vec2, vec4, float, int, uint, uvec2, uvec4, uniform, min, storage, If,
|
|
1
|
+
import { Fn, vec2, vec4, float, int, uint, uvec2, uvec4, uniform, min, max, select, storage, If,
|
|
2
2
|
textureStore, workgroupId, localId } from 'three/tsl';
|
|
3
3
|
import { RenderTarget, StorageTexture } from 'three/webgpu';
|
|
4
4
|
import { HalfFloatType, RGBAFormat, RedIntegerFormat, UnsignedIntType, NearestFilter, Matrix4, Box2, Vector2 } from 'three';
|
|
@@ -7,7 +7,7 @@ import { MAX_STORAGE_TEXTURE_SIZE } from '../Processor/StorageTexturePool.js';
|
|
|
7
7
|
import { GBUFFER_MISS_DEPTH } from './DenoiserSettings.js';
|
|
8
8
|
import { Ray, HitInfo, RayTracingMaterial, UVCache } from '../TSL/Struct.js';
|
|
9
9
|
import { traverseBVH } from '../TSL/BVHTraversal.js';
|
|
10
|
-
import { cameraRayOf, cameraRayUniforms } from '../TSL/CameraRay.js';
|
|
10
|
+
import { cameraRayOf, cameraRayUniforms, cameraClipDistance } from '../TSL/CameraRay.js';
|
|
11
11
|
import { getMaterial } from '../TSL/Common.js';
|
|
12
12
|
import { computeUVCache, processNormal, processBump, processMetalnessRoughness, triangleUVTangent, buildBucketTextureNodes, refreshBucketTextureNodes } from '../TSL/TextureSampling.js';
|
|
13
13
|
import { withSceneResources } from '../TSL/SceneResources.js';
|
|
@@ -31,10 +31,10 @@ import { withSceneResources } from '../TSL/SceneResources.js';
|
|
|
31
31
|
* map, recomputed from the SAME deterministic hit — no extra ray) so the
|
|
32
32
|
* spatial denoiser's edge-stop can see normal-map detail the flat geometric
|
|
33
33
|
* normal hides. Deterministic ⇒ jitter-free, so it's safe for the gates.
|
|
34
|
-
* Its .w is the sampled material roughness (NRD's normal+roughness guide).
|
|
34
|
+
* Its .w is the sampled material roughness (NRD's normal+roughness guide), negated on an emitter.
|
|
35
35
|
*
|
|
36
36
|
* Publishes: pathtracer:normalDepth, pathtracer:prevNormalDepth,
|
|
37
|
-
* pathtracer:shadingNormal (rgb = shading normal·0.5+0.5, a = roughness),
|
|
37
|
+
* pathtracer:shadingNormal (rgb = shading normal·0.5+0.5, a = roughness, negative on an emitter),
|
|
38
38
|
* pathtracer:instanceLeaf (r32uint: the hit's transformed TLAS leaf + 1, 0 = none)
|
|
39
39
|
*/
|
|
40
40
|
export class NormalDepth extends RenderStage {
|
|
@@ -233,14 +233,18 @@ export class NormalDepth extends RenderStage {
|
|
|
233
233
|
// No jitter — deterministic per-pixel ray so the temporal gate
|
|
234
234
|
// sees stable per-pixel normals across frames.
|
|
235
235
|
const uv = vec2( float( gx ).add( 0.5 ).div( resW ), float( gy ).add( 0.5 ).div( resH ) );
|
|
236
|
-
const
|
|
236
|
+
const primary = cameraRayOf( uv, camWorldMat, camProjInvMat, this.cameraRay );
|
|
237
|
+
const direction = primary.direction.toVar();
|
|
238
|
+
// From the camera's clip plane, as the path tracer's rays start; the depth stays measured from the camera.
|
|
239
|
+
const clipAlong = cameraClipDistance( direction, this.cameraRay.clipStart, camWorldMat, this.cameraRay.cameraProjection ).toVar();
|
|
240
|
+
const ray = Ray( { origin: primary.origin.add( direction.mul( clipAlong ) ), direction } );
|
|
237
241
|
const hit = HitInfo.wrap( traverseBVH( ray, bvhStorage, triStorage ) );
|
|
238
242
|
|
|
239
243
|
const encodedNormal = hit.normal.mul( 0.5 ).add( 0.5 );
|
|
240
244
|
// Clamp to the max finite HalfFloat: a hit farther than 65504 units would otherwise
|
|
241
245
|
// round to +Inf in the f16 texture (on backends that don't clamp), reintroducing the
|
|
242
246
|
// Inf-Inf=NaN in the denoiser depth weights that the finite miss-sentinel avoids.
|
|
243
|
-
const depth = min( hit.dst, float( 65504.0 ) );
|
|
247
|
+
const depth = min( hit.dst.add( clipAlong ), float( 65504.0 ) );
|
|
244
248
|
|
|
245
249
|
const result = hit.didHit.select(
|
|
246
250
|
vec4( encodedNormal, depth ),
|
|
@@ -289,6 +293,8 @@ export class NormalDepth extends RenderStage {
|
|
|
289
293
|
shadingNormal.assign( processBump( mapped, material, uvCache ) );
|
|
290
294
|
// Same floor the Shade kernel applies before shading.
|
|
291
295
|
roughness.assign( processMetalnessRoughness( material, uvCache ).y.clamp( 0.05, 1.0 ) );
|
|
296
|
+
const emits = max( material.emissive.x, max( material.emissive.y, material.emissive.z ) ).mul( material.emissiveIntensity ).greaterThan( 0.0 );
|
|
297
|
+
roughness.assign( select( emits, roughness.negate(), roughness ) );
|
|
292
298
|
|
|
293
299
|
} );
|
|
294
300
|
|
package/src/Stages/PathTracer.js
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
SORT_GLOBAL_WG_SIZE, SORT_GLOBAL_MAX_BINS,
|
|
28
28
|
} from '../TSL/SortGlobalKernels.js';
|
|
29
29
|
import { ENGINE_DEFAULTS } from '../EngineDefaults.js';
|
|
30
|
+
import { LIGHT_FLOATS } from '../managers/UniformManager.js';
|
|
30
31
|
import { MAX_STORAGE_TEXTURE_SIZE } from '../Processor/StorageTexturePool.js';
|
|
31
32
|
import { readPixels } from '../Processor/TextureReadback.js';
|
|
32
33
|
import { ISSUE_CODES } from '../EngineIssues.js';
|
|
@@ -380,6 +381,26 @@ export class PathTracer extends PathTracerStage {
|
|
|
380
381
|
|
|
381
382
|
}
|
|
382
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Whether some point or spot light has a radius: only then is disc sampling compiled in, and never for an integrator
|
|
386
|
+
* of its own (a bidirectional light path leaves a light's centre).
|
|
387
|
+
*/
|
|
388
|
+
_lightRadiusNeeded() {
|
|
389
|
+
|
|
390
|
+
if ( this._integrator ) return false;
|
|
391
|
+
const sized = ( type, count, slot ) => {
|
|
392
|
+
|
|
393
|
+
if ( ! count ) return false;
|
|
394
|
+
const data = this[ `${type}LightsBufferNode` ].array;
|
|
395
|
+
for ( let i = 0; i < count; i ++ ) if ( data[ i * LIGHT_FLOATS[ type ] + slot ] > 0 ) return true;
|
|
396
|
+
return false;
|
|
397
|
+
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
return sized( 'point', this.numPointLights.value, 9 ) || sized( 'spot', this.numSpotLights.value, 18 );
|
|
401
|
+
|
|
402
|
+
}
|
|
403
|
+
|
|
383
404
|
get integrator() {
|
|
384
405
|
|
|
385
406
|
return this._integrator?.name ?? 'path';
|
|
@@ -406,10 +427,12 @@ export class PathTracer extends PathTracerStage {
|
|
|
406
427
|
this._materialFeaturesChanged = false;
|
|
407
428
|
const needed = this._layersToCompile();
|
|
408
429
|
if ( MATERIAL_LAYERS.some( ( layer ) => needed[ layer ] && ! this._compiledLayers?.[ layer ] ) ) this._outputsChanged = true;
|
|
430
|
+
if ( ! this._compiledTransmissionMaps && this.materialData.hasTransmissionMaps() ) this._outputsChanged = true;
|
|
409
431
|
|
|
410
432
|
}
|
|
411
433
|
|
|
412
434
|
if ( PICKED_SHADOW_RAYS.includes( this._shadowRays ) && this._pickedShadowRays() !== this._compiledPick ) this._outputsChanged = true;
|
|
435
|
+
if ( this._lightRadiusNeeded() !== this._compiledLightRadius ) this._outputsChanged = true;
|
|
413
436
|
|
|
414
437
|
if ( this._lightBufferRealloc || this._outputsChanged ) {
|
|
415
438
|
|
|
@@ -1636,6 +1659,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
1636
1659
|
anamorphicRatio: this.anamorphicRatio,
|
|
1637
1660
|
dofMode: this.dofMode,
|
|
1638
1661
|
dofBlur: this.dofBlur,
|
|
1662
|
+
cameraClipStart: this.cameraClipStart,
|
|
1639
1663
|
renderWidth: this._wfRenderWidth,
|
|
1640
1664
|
renderHeight: this._wfRenderHeight,
|
|
1641
1665
|
chunkRowBase: this._wfChunkRowBase,
|
|
@@ -1851,6 +1875,8 @@ export class PathTracer extends PathTracerStage {
|
|
|
1851
1875
|
iesProfiles: texNodes.iesProfilesTex,
|
|
1852
1876
|
alphaShadows: this.uniforms.get( 'enableAlphaShadows' ),
|
|
1853
1877
|
materialLayers: this._compiledLayers = this._layersToCompile(),
|
|
1878
|
+
lightRadius: this._compiledLightRadius = this._lightRadiusNeeded(),
|
|
1879
|
+
transmissionMaps: this._compiledTransmissionMaps = this.materialData.hasTransmissionMaps(),
|
|
1854
1880
|
};
|
|
1855
1881
|
const own = ( call ) => withSceneResources( call, resources );
|
|
1856
1882
|
const freshEnvTex = _env.environmentTexture ? texture( _env.environmentTexture ) : texNodes.envTex;
|
|
@@ -2089,7 +2115,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
2089
2115
|
hasPreviousAux: this.hasPreviousAux,
|
|
2090
2116
|
auxAccumulationAlpha: this.auxAccumulationAlpha,
|
|
2091
2117
|
cameraIsMoving: this.cameraIsMoving,
|
|
2092
|
-
transparentBackground: this.transparentBackground,
|
|
2093
2118
|
prevAccumTexture: prevColor,
|
|
2094
2119
|
prevAlbedoTexture: prevAlbedo,
|
|
2095
2120
|
prevNormalDepthTexture: prevNormalDepth,
|
|
@@ -2146,6 +2171,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
2146
2171
|
anamorphicRatio: this.anamorphicRatio,
|
|
2147
2172
|
dofMode: this.dofMode,
|
|
2148
2173
|
dofBlur: this.dofBlur,
|
|
2174
|
+
cameraClipStart: this.cameraClipStart,
|
|
2149
2175
|
bvhBuffer: freshBvh,
|
|
2150
2176
|
triangleBuffer: freshTri,
|
|
2151
2177
|
materialBuffer: freshMat,
|
|
@@ -1018,7 +1018,11 @@ export class PathTracerStage extends RenderStage {
|
|
|
1018
1018
|
|
|
1019
1019
|
this._whenTLASResident( () => {
|
|
1020
1020
|
|
|
1021
|
-
|
|
1021
|
+
// Leaves are per placement: an InstancedMesh holds a run of them, and shifts every index after it.
|
|
1022
|
+
const run = this._instanceTable?.placementRunOf( meshIndex );
|
|
1023
|
+
let patched = false;
|
|
1024
|
+
for ( let p = run?.start; run && p < run.start + run.count; p ++ ) patched = this._patchTLASLeafVisibility( p, visible ) || patched;
|
|
1025
|
+
if ( ! patched ) return;
|
|
1022
1026
|
this._patchGroupNodes();
|
|
1023
1027
|
this._flushBVHEdits();
|
|
1024
1028
|
|
|
@@ -1437,13 +1441,18 @@ export class PathTracerStage extends RenderStage {
|
|
|
1437
1441
|
*/
|
|
1438
1442
|
_updateCameraUniforms() {
|
|
1439
1443
|
|
|
1444
|
+
// Only an imported camera's clip plane: `near` is the viewer's own for its default camera.
|
|
1445
|
+
const clipStart = Math.max( this.camera.userData?.clipStart ?? 0, 0 );
|
|
1446
|
+
|
|
1440
1447
|
if ( ! this._matricesApproxEqual( this.lastCameraMatrix, this.camera.matrixWorld ) ||
|
|
1441
|
-
! this._matricesApproxEqual( this.lastProjectionMatrix, this.camera.projectionMatrixInverse )
|
|
1448
|
+
! this._matricesApproxEqual( this.lastProjectionMatrix, this.camera.projectionMatrixInverse ) ||
|
|
1449
|
+
clipStart !== this.cameraClipStart.value ) {
|
|
1442
1450
|
|
|
1443
1451
|
this.cameraWorldMatrix.value.copy( this.camera.matrixWorld );
|
|
1444
1452
|
this.cameraViewMatrix.value.copy( this.camera.matrixWorldInverse );
|
|
1445
1453
|
this.cameraProjectionMatrix.value.copy( this.camera.projectionMatrix );
|
|
1446
1454
|
this.cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
|
|
1455
|
+
this.cameraClipStart.value = clipStart;
|
|
1447
1456
|
|
|
1448
1457
|
this.lastCameraMatrix.copy( this.camera.matrixWorld );
|
|
1449
1458
|
this.lastProjectionMatrix.copy( this.camera.projectionMatrixInverse );
|
|
@@ -1503,6 +1512,9 @@ export class PathTracerStage extends RenderStage {
|
|
|
1503
1512
|
// freezes when the frame retires or the camera moves, which is what a denoiser needs.
|
|
1504
1513
|
context.setState( 'pathtracer:samples', this.frameCount );
|
|
1505
1514
|
context.setState( 'renderMode', this.renderMode.value );
|
|
1515
|
+
// What a camera ray that sees nothing shows; the colour texture's alpha is 0 there in every case.
|
|
1516
|
+
context.setState( 'pathtracer:backdrop', this.uniforms.get( 'transparentBackground' ).value ? 'transparent'
|
|
1517
|
+
: this.uniforms.get( 'showBackground' ).value ? 'environment' : 'color' );
|
|
1506
1518
|
|
|
1507
1519
|
}
|
|
1508
1520
|
|
|
@@ -284,10 +284,11 @@ export function encodeSceneGraph( root, { environment = null, animations = [], s
|
|
|
284
284
|
|
|
285
285
|
/**
|
|
286
286
|
* Writes an encoded graph into a storage entry: `graph.json` and `data.bin`.
|
|
287
|
-
* @param {{release?: boolean}} [options] -
|
|
288
|
-
* written during a build does not keep arrays the build has since replaced
|
|
287
|
+
* @param {{release?: boolean, signal?: AbortSignal}} [options] - `release` drops each section's array once
|
|
288
|
+
* written, so a graph written during a build does not keep arrays the build has since replaced; `signal`
|
|
289
|
+
* stops the write between pieces
|
|
289
290
|
*/
|
|
290
|
-
export async function writeSceneGraph( writer, { manifest, sections }, { release = false } = {} ) {
|
|
291
|
+
export async function writeSceneGraph( writer, { manifest, sections }, { release = false, signal = null } = {} ) {
|
|
291
292
|
|
|
292
293
|
for ( const section of sections ) {
|
|
293
294
|
|
|
@@ -295,6 +296,7 @@ export async function writeSceneGraph( writer, { manifest, sections }, { release
|
|
|
295
296
|
const bytes = new Uint8Array( array.buffer, array.byteOffset, array.byteLength );
|
|
296
297
|
for ( let at = 0; at < bytes.length; at += PIECE_BYTES ) {
|
|
297
298
|
|
|
299
|
+
signal?.throwIfAborted();
|
|
298
300
|
await writer.write( 'data.bin', bytes.subarray( at, Math.min( bytes.length, at + PIECE_BYTES ) ), { at: offset + at } );
|
|
299
301
|
|
|
300
302
|
}
|
|
@@ -304,6 +306,7 @@ export async function writeSceneGraph( writer, { manifest, sections }, { release
|
|
|
304
306
|
}
|
|
305
307
|
|
|
306
308
|
if ( sections.length === 0 ) await writer.write( 'data.bin', new Uint8Array( 0 ) );
|
|
309
|
+
signal?.throwIfAborted();
|
|
307
310
|
await writer.writeJSON( 'graph.json', manifest );
|
|
308
311
|
|
|
309
312
|
}
|
package/src/TSL/BVHTraversal.js
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
} from '../Processor/BufferLayout.js';
|
|
33
33
|
import { HitInfo } from './Struct.js';
|
|
34
34
|
import {
|
|
35
|
-
getDatafromStorageBuffer, instanceRows, instanceNormalToWorld, unpackTriangleNormal, triangleRow, shadowFlagsSettle
|
|
35
|
+
getDatafromStorageBuffer, storageRows, instanceRows, instanceNormalToWorld, unpackTriangleNormal, triangleRow, shadowFlagsSettle
|
|
36
36
|
} from './Common.js';
|
|
37
37
|
|
|
38
38
|
const MAX_STACK_DEPTH = 32;
|
|
@@ -51,6 +51,15 @@ const HUGE_VAL = 1e8;
|
|
|
51
51
|
|
|
52
52
|
const createStack = () => array( 'int', MAX_STACK_DEPTH ).toVar();
|
|
53
53
|
|
|
54
|
+
// Row k of BVH node `nodeIndex`. One buffer reads each row where it is used; over parts, `rows` are read in one branch.
|
|
55
|
+
const nodeRows = ( bvhBuffer, nodeIndex, rows = [ 0, 1, 2, 3 ] ) => {
|
|
56
|
+
|
|
57
|
+
if ( ! bvhBuffer.isSplitStorage ) return ( k ) => getDatafromStorageBuffer( bvhBuffer, nodeIndex, int( k ), int( BVH_STRIDE ) );
|
|
58
|
+
const read = storageRows( bvhBuffer, int( nodeIndex ).mul( BVH_STRIDE ), rows );
|
|
59
|
+
return ( k ) => read[ rows.indexOf( k ) ];
|
|
60
|
+
|
|
61
|
+
};
|
|
62
|
+
|
|
54
63
|
// A negative stack entry is a leaf folded into its parent, `~( first << 4 | count )` as the parent
|
|
55
64
|
// stores it, so child slots are pushed as they are and a leaf needs no fetch.
|
|
56
65
|
const foldedFirst = ( entry ) => int( uint( entry.bitNot() ).shiftRight( uint( 4 ) ) );
|
|
@@ -91,10 +100,10 @@ const toObjectDir = ( rows, d ) => vec3(
|
|
|
91
100
|
// Compare-swaps sorting up to four ascending.
|
|
92
101
|
const SORT_NETWORKS = [[], [], [[ 0, 1 ]], [[ 0, 1 ], [ 1, 2 ], [ 0, 1 ]], [[ 0, 1 ], [ 2, 3 ], [ 0, 2 ], [ 1, 3 ], [ 1, 2 ]]];
|
|
93
102
|
|
|
94
|
-
const pushHitCopies = (
|
|
103
|
+
const pushHitCopies = ( node, nodeIndex, nodeData0, origin, invDir, stack, stackPtr, limit ) => {
|
|
95
104
|
|
|
96
|
-
const d1 =
|
|
97
|
-
const d2 =
|
|
105
|
+
const d1 = node( 1 );
|
|
106
|
+
const d2 = node( 2 );
|
|
98
107
|
const base = vec3( nodeData0.z, d1.x, d1.y ).toVar();
|
|
99
108
|
const exps = floatBitsToUint( nodeData0.y ).toVar();
|
|
100
109
|
const stepOf = ( a ) => uintBitsToFloat( exps.shiftRight( uint( a * 8 ) ).bitAnd( uint( 255 ) ).shiftLeft( uint( 23 ) ) );
|
|
@@ -363,8 +372,9 @@ const makeTraverseBVH = ( trackStats ) => Fn( ( [
|
|
|
363
372
|
|
|
364
373
|
const leaf = int( uint( entry ).bitAnd( uint( ~ COPY_ENTRY >>> 0 ) ).shiftRight( uint( 2 ) ) );
|
|
365
374
|
const k = uint( entry ).bitAnd( uint( 3 ) ).toVar();
|
|
366
|
-
const
|
|
367
|
-
const
|
|
375
|
+
const node = nodeRows( bvhBuffer, leaf, [ 0, 3 ] );
|
|
376
|
+
const head = node( 0 );
|
|
377
|
+
const words = floatBitsToUint( node( 3 ) ).toVar();
|
|
368
378
|
const word = select( k.equal( uint( 0 ) ), words.x, select( k.equal( uint( 1 ) ), words.y, select( k.equal( uint( 2 ) ), words.z, words.w ) ) ).toVar();
|
|
369
379
|
|
|
370
380
|
If( word.bitAnd( uint( CLUSTER_COPY_HIDDEN ) ).equal( uint( 0 ) ), () => {
|
|
@@ -483,12 +493,13 @@ const makeTraverseBVH = ( trackStats ) => Fn( ( [
|
|
|
483
493
|
const visitNode = () => {
|
|
484
494
|
|
|
485
495
|
const nodeIndex = entry;
|
|
496
|
+
const node = nodeRows( bvhBuffer, nodeIndex );
|
|
486
497
|
|
|
487
498
|
// 4 vec4 per node
|
|
488
499
|
// Leaf: vec4(0) = [triOffset, triCount, 0, TRIANGLE_LEAF]
|
|
489
500
|
// Inner: vec4(0) = [leftMin.xyz, leftChild], vec4(1) = [leftMax.xyz, rightChild],
|
|
490
501
|
// vec4(2) = [rightMin.xyz, 0], vec4(3) = [rightMax.xyz, 0]
|
|
491
|
-
const nodeData0 =
|
|
502
|
+
const nodeData0 = node( 0 );
|
|
492
503
|
if ( trackStats ) closestHit.boxTests.addAssign( 1 );
|
|
493
504
|
|
|
494
505
|
// Slot [3] carries a u32 bit pattern: an inner node's left child, or a leaf tag. Stored as
|
|
@@ -504,7 +515,7 @@ const makeTraverseBVH = ( trackStats ) => Fn( ( [
|
|
|
504
515
|
|
|
505
516
|
} ).Else( clustered ? () => {
|
|
506
517
|
|
|
507
|
-
pushHitCopies(
|
|
518
|
+
pushHitCopies( node, nodeIndex, nodeData0, worldOrigin, worldInvDir, stack, stackPtr, closestHit.dst );
|
|
508
519
|
|
|
509
520
|
} : () => {
|
|
510
521
|
|
|
@@ -516,7 +527,7 @@ const makeTraverseBVH = ( trackStats ) => Fn( ( [
|
|
|
516
527
|
// A baked placement already sits in world space; only a transformed one moves the ray.
|
|
517
528
|
If( floatBitsToUint( nodeData0.y ).bitAnd( uint( TLAS_LEAF_IDENTITY ) ).equal( uint( 0 ) ), () => {
|
|
518
529
|
|
|
519
|
-
const rows =
|
|
530
|
+
const rows = [ node( 1 ), node( 2 ), node( 3 ) ];
|
|
520
531
|
const localDir = toObjectDir( rows, worldDirection ).toVar();
|
|
521
532
|
|
|
522
533
|
rayOrigin.assign( toObjectPoint( rows, worldOrigin ) );
|
|
@@ -547,9 +558,9 @@ const makeTraverseBVH = ( trackStats ) => Fn( ( [
|
|
|
547
558
|
} ).Else( () => {
|
|
548
559
|
|
|
549
560
|
// Inner node — child AABBs stored in this node (4 reads total, no child fetches)
|
|
550
|
-
const nodeData1 =
|
|
551
|
-
const nodeData2 =
|
|
552
|
-
const nodeData3 =
|
|
561
|
+
const nodeData1 = node( 1 );
|
|
562
|
+
const nodeData2 = node( 2 );
|
|
563
|
+
const nodeData3 = node( 3 );
|
|
553
564
|
|
|
554
565
|
const leftChild = int( nodeTag ).toVar();
|
|
555
566
|
const rightChild = int( floatBitsToUint( nodeData1.w ) ).toVar();
|
|
@@ -705,8 +716,9 @@ const makeTraverseBVHShadow = ( cameraCulled ) => Fn( ( [
|
|
|
705
716
|
|
|
706
717
|
const leaf = int( uint( entry ).bitAnd( uint( ~ COPY_ENTRY >>> 0 ) ).shiftRight( uint( 2 ) ) );
|
|
707
718
|
const k = uint( entry ).bitAnd( uint( 3 ) ).toVar();
|
|
708
|
-
const
|
|
709
|
-
const
|
|
719
|
+
const node = nodeRows( bvhBuffer, leaf, [ 0, 3 ] );
|
|
720
|
+
const head = node( 0 );
|
|
721
|
+
const words = floatBitsToUint( node( 3 ) ).toVar();
|
|
710
722
|
const word = select( k.equal( uint( 0 ) ), words.x, select( k.equal( uint( 1 ) ), words.y, select( k.equal( uint( 2 ) ), words.z, words.w ) ) ).toVar();
|
|
711
723
|
|
|
712
724
|
If( word.bitAnd( uint( CLUSTER_COPY_HIDDEN ) ).equal( uint( 0 ) ), () => {
|
|
@@ -818,7 +830,8 @@ const makeTraverseBVHShadow = ( cameraCulled ) => Fn( ( [
|
|
|
818
830
|
const visitNode = () => {
|
|
819
831
|
|
|
820
832
|
const nodeIndex = entry;
|
|
821
|
-
const
|
|
833
|
+
const node = nodeRows( bvhBuffer, nodeIndex );
|
|
834
|
+
const nodeData0 = node( 0 );
|
|
822
835
|
|
|
823
836
|
// Slot [3] carries a u32 bit pattern: an inner node's left child, or a leaf tag. Stored as
|
|
824
837
|
// float VALUES these rounded past 2^24 and sent the ray to a neighbouring node, which
|
|
@@ -833,7 +846,7 @@ const makeTraverseBVHShadow = ( cameraCulled ) => Fn( ( [
|
|
|
833
846
|
|
|
834
847
|
} ).Else( clustered ? () => {
|
|
835
848
|
|
|
836
|
-
pushHitCopies(
|
|
849
|
+
pushHitCopies( node, nodeIndex, nodeData0, worldOrigin, worldInvDir, stack, stackPtr, closestHit.dst );
|
|
837
850
|
|
|
838
851
|
} : () => {
|
|
839
852
|
|
|
@@ -843,7 +856,7 @@ const makeTraverseBVHShadow = ( cameraCulled ) => Fn( ( [
|
|
|
843
856
|
// A baked placement already sits in world space; only a transformed one moves the ray.
|
|
844
857
|
If( floatBitsToUint( nodeData0.y ).bitAnd( uint( TLAS_LEAF_IDENTITY ) ).equal( uint( 0 ) ), () => {
|
|
845
858
|
|
|
846
|
-
const rows =
|
|
859
|
+
const rows = [ node( 1 ), node( 2 ), node( 3 ) ];
|
|
847
860
|
const localDir = toObjectDir( rows, worldDirection ).toVar();
|
|
848
861
|
|
|
849
862
|
rayOrigin.assign( toObjectPoint( rows, worldOrigin ) );
|
|
@@ -874,9 +887,9 @@ const makeTraverseBVHShadow = ( cameraCulled ) => Fn( ( [
|
|
|
874
887
|
} ).Else( () => {
|
|
875
888
|
|
|
876
889
|
// Inner node — child AABBs stored in this node
|
|
877
|
-
const nodeData1 =
|
|
878
|
-
const nodeData2 =
|
|
879
|
-
const nodeData3 =
|
|
890
|
+
const nodeData1 = node( 1 );
|
|
891
|
+
const nodeData2 = node( 2 );
|
|
892
|
+
const nodeData3 = node( 3 );
|
|
880
893
|
|
|
881
894
|
const leftChild = int( nodeTag ).toVar();
|
|
882
895
|
const rightChild = int( floatBitsToUint( nodeData1.w ) ).toVar();
|
package/src/TSL/CameraRay.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
Fn, vec3, vec4, float, int, uniform,
|
|
17
|
-
If, normalize, mat3, mix, sin, cos, cross, dot, select,
|
|
17
|
+
If, normalize, mat3, mix, sin, cos, cross, dot, select, max,
|
|
18
18
|
} from 'three/tsl';
|
|
19
19
|
import { Vector2 } from 'three';
|
|
20
20
|
|
|
@@ -39,10 +39,12 @@ export function cameraRayUniforms() {
|
|
|
39
39
|
panoLonRange: uniform( new Vector2(), 'vec2' ),
|
|
40
40
|
panoLatRange: uniform( new Vector2(), 'vec2' ),
|
|
41
41
|
panoLevelHorizon: uniform( 1, 'int' ),
|
|
42
|
+
clipStart: uniform( 0, 'float' ),
|
|
42
43
|
/** @param {import('../managers/UniformManager.js').UniformManager} source */
|
|
43
44
|
sync( source ) {
|
|
44
45
|
|
|
45
46
|
u.cameraProjection.value = source.get( 'cameraProjection' ).value;
|
|
47
|
+
u.clipStart.value = source.get( 'cameraClipStart' ).value;
|
|
46
48
|
u.panoLonRange.value.copy( source.get( 'panoLonRange' ).value );
|
|
47
49
|
u.panoLatRange.value.copy( source.get( 'panoLatRange' ).value );
|
|
48
50
|
u.panoLevelHorizon.value = source.get( 'panoLevelHorizon' ).value;
|
|
@@ -70,6 +72,15 @@ export const cameraRayPoint = ( uv01, distance, cameraWorldMatrix, cameraProject
|
|
|
70
72
|
|
|
71
73
|
};
|
|
72
74
|
|
|
75
|
+
/**
|
|
76
|
+
* How far along a camera ray its clip plane lies: Cycles' clip start, a plane `clipStart` in front of the camera (a
|
|
77
|
+
* sphere for a panorama). Camera rays start there, so a camera can see through a wall behind it.
|
|
78
|
+
*/
|
|
79
|
+
export const cameraClipDistance = ( direction, clipStart, cameraWorldMatrix, cameraProjection ) => select(
|
|
80
|
+
cameraProjection.equal( PANORAMA ), clipStart,
|
|
81
|
+
clipStart.div( max( dot( direction, normalize( vec3( cameraWorldMatrix[ 2 ] ) ) ).negate(), 1e-4 ) )
|
|
82
|
+
);
|
|
83
|
+
|
|
73
84
|
/** World-space primary ray origin for pixel uv: the camera, or its image plane when orthographic. */
|
|
74
85
|
export const cameraRayOrigin = Fn( ( [ uv01, cameraWorldMatrix, cameraProjectionMatrixInverse, cameraProjection ] ) => {
|
|
75
86
|
|