rayzee 9.5.0 → 11.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 +22 -20
- package/dist/{ArchiveImporter-xuUJiklP.js → ArchiveImporter-BRQvLggb.js} +12 -13
- package/dist/{BidirectionalIntegrator-C5PKjJoT.js → BidirectionalIntegrator-9lZQxFVH.js} +87 -87
- package/dist/BufferLayout-D_IBo2ci.js +150 -0
- package/dist/{ColorManagement-CheBCPaW.js → ColorManagement-oeEk7oSu.js} +171 -171
- package/dist/{NeuralRetouch-CW11yLZ_.js → NeuralRetouch-BCXylThH.js} +3 -3
- package/dist/{NeuralSuperRes-BekaecYg.js → NeuralSuperRes-sZAeS142.js} +3 -3
- package/dist/{PathTracer-Ccl9KOuF.js → PathTracer-CQPW06Mm.js} +2911 -2669
- package/dist/{PhysicalSky-Dv2PMFGW.js → PhysicalSky-BvqCw_dy.js} +2 -2
- package/dist/{RayzeeRenderer-BQxnzCTD.js → RayzeeRenderer-COpwotpU.js} +338 -329
- package/dist/{SolarLimb-ken-JbDK.js → SolarLimb-CPaeZVwz.js} +10 -10
- package/dist/{ToneMapGPU-BHi5JzIq.js → ToneMapGPU-CjU3osDd.js} +1 -1
- package/dist/{ViewTransforms-D0rTLBsX.js → ViewTransforms-CYN7bq2w.js} +2 -2
- 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/rayzee-core.es.js +11 -11
- package/dist/rayzee.es.js +734 -599
- package/dist/rayzee.umd.js +102 -102
- package/dist/sceneCachePolicy-BSSsKY98.js +443 -0
- package/package.json +1 -1
- package/src/Color/ViewTransforms.js +5 -1
- package/src/EngineDefaults.js +59 -1032
- package/src/Headless.js +6 -5
- package/src/Passes/OIDNTemporalHistory.js +1 -1
- package/src/PathTracerApp.js +5 -3
- package/src/Processor/AssetLoader.js +6 -5
- package/src/Processor/BVHBuilder.js +10 -31
- package/src/Processor/BVHLeafFold.js +4 -6
- package/src/Processor/BVHRefitter.js +3 -46
- package/src/Processor/BufferLayout.js +291 -0
- package/src/Processor/EmissiveTriangleBuilder.js +1 -1
- package/src/Processor/GeometryExtractor.js +3 -3
- package/src/Processor/HostMemory.js +7 -1
- package/src/Processor/InstanceTable.js +1 -1
- package/src/Processor/MaterialPacking.js +2 -1
- package/src/Processor/PBRT/PBRTSceneBuilder.js +1 -1
- package/src/Processor/QueueManager.js +28 -3
- package/src/Processor/SceneProcessor.js +4 -2
- package/src/Processor/SplitBLAS.js +1 -1
- package/src/Processor/StorageTexturePool.js +22 -1
- package/src/Processor/TLASBuilder.js +1 -3
- package/src/Processor/TextureBuckets.js +237 -0
- package/src/Processor/TextureCreator.js +10 -1
- package/src/Processor/Workers/TexturesWorker.js +5 -6
- package/src/RayzeeRenderer.js +29 -32
- package/src/RenderSettings.js +26 -28
- package/src/Stages/ASVGF.js +8 -7
- package/src/Stages/AutoExposure.js +14 -5
- package/src/Stages/BilateralFilter.js +2 -1
- package/src/Stages/DenoiserSettings.js +182 -0
- package/src/Stages/EdgeFilter.js +2 -1
- package/src/Stages/MotionVector.js +1 -1
- package/src/Stages/NRD.js +2 -3
- package/src/Stages/NormalDepth.js +3 -2
- package/src/Stages/PathTracer.js +72 -4
- package/src/Stages/PathTracerStage.js +2 -1
- package/src/Stages/Variance.js +1 -1
- package/src/TSL/BVHTraversal.js +4 -1
- package/src/TSL/Common.js +1 -1
- package/src/TSL/EmissiveSampling.js +1 -1
- package/src/TSL/LightVisibility.js +111 -0
- package/src/TSL/LightsSampling.js +151 -15
- package/src/TSL/Random.js +1 -1
- package/src/TSL/ShadeKernel.js +88 -3
- package/src/TSL/TextureSampling.js +1 -1
- package/src/core.js +5 -9
- package/src/index.js +10 -21
- package/src/integrators/BidirectionalIntegrator.js +2 -1
- package/src/managers/CameraManager.js +17 -7
- package/src/managers/DenoisingManager.js +14 -10
- package/src/managers/EnvironmentManager.js +32 -13
- package/src/managers/MaterialDataManager.js +3 -3
- package/src/managers/UniformManager.js +2 -2
- package/dist/EngineDefaults-DH3bI5t-.js +0 -844
- package/dist/sceneCachePolicy-BmwYOYDu.js +0 -183
- /package/dist/{Logger-BnHghprG.js → Logger-dDDGj9Av.js} +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where each kind of light gets through, learned from the shadow-ray pick's own rays (LightsSampling lightPick):
|
|
3
|
+
* tries and visible counts per cell of space and facing. Shade reads the learned half, which no kernel writes during
|
|
4
|
+
* a frame, so a render repeats; it adds to the fresh half, folded into the learned one after each frame.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Fn, float, int, uint, If, Return, instanceIndex, abs, ceil, exp2, floor, length, log2, max, select, sqrt, atomicAdd, atomicLoad, atomicStore } from 'three/tsl';
|
|
8
|
+
|
|
9
|
+
import { VISIBILITY_BASE, VISIBILITY_CELLS, VISIBILITY_KINDS, VISIBILITY_COUNTER_WORDS } from '../Processor/QueueManager.js';
|
|
10
|
+
import { pcgHash } from './Random.js';
|
|
11
|
+
|
|
12
|
+
export const VISIBILITY_KIND = { LAMPS: 0, ENVIRONMENT: 1, SUN: 2, EMITTERS: 3 };
|
|
13
|
+
|
|
14
|
+
const LEARNED = VISIBILITY_BASE;
|
|
15
|
+
const FRESH = VISIBILITY_BASE + VISIBILITY_COUNTER_WORDS;
|
|
16
|
+
const SLOTS = VISIBILITY_CELLS * VISIBILITY_KINDS;
|
|
17
|
+
// A cell is this share of its distance from the camera, rounded up to a power of two.
|
|
18
|
+
const CELL_SHARE = 1 / 32;
|
|
19
|
+
// Below it a kind is still picked now and then, so a light that comes into view is found again.
|
|
20
|
+
const MIN_SHARE = 1 / 64;
|
|
21
|
+
const HALVE_AT = 1 << 16;
|
|
22
|
+
|
|
23
|
+
const hashIn = ( h, v ) => pcgHash( { state: h.bitXor( v ) } );
|
|
24
|
+
|
|
25
|
+
/** The cell of a point and the side its surface faces. */
|
|
26
|
+
export const visibilityCell = ( p, n, cameraPosition ) => {
|
|
27
|
+
|
|
28
|
+
const level = ceil( log2( max( length( p.sub( cameraPosition ) ).mul( CELL_SHARE ), 1e-6 ) ) ).toVar();
|
|
29
|
+
const c = floor( p.div( exp2( level ) ) ).toVar();
|
|
30
|
+
const a = abs( n );
|
|
31
|
+
const facing = select( a.x.greaterThan( max( a.y, a.z ) ), select( n.x.greaterThan( 0.0 ), uint( 0 ), uint( 1 ) ),
|
|
32
|
+
select( a.y.greaterThan( a.z ), select( n.y.greaterThan( 0.0 ), uint( 2 ), uint( 3 ) ), select( n.z.greaterThan( 0.0 ), uint( 4 ), uint( 5 ) ) ) );
|
|
33
|
+
|
|
34
|
+
let h = pcgHash( { state: uint( int( level ).add( 1024 ) ) } );
|
|
35
|
+
h = hashIn( h, uint( int( c.x ) ) );
|
|
36
|
+
h = hashIn( h, uint( int( c.y ) ) );
|
|
37
|
+
h = hashIn( h, uint( int( c.z ) ) );
|
|
38
|
+
h = hashIn( h, facing );
|
|
39
|
+
return h.bitAnd( uint( VISIBILITY_CELLS - 1 ) ).toVar();
|
|
40
|
+
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const slot = ( cell, kind ) => cell.mul( uint( VISIBILITY_KINDS ) ).add( uint( kind ) ).mul( uint( 2 ) );
|
|
44
|
+
|
|
45
|
+
/** √ of the share of rays that got through: the pick's best weight against a light that is on or off. */
|
|
46
|
+
export const visibilityShare = ( counters, cell, kind ) => {
|
|
47
|
+
|
|
48
|
+
const at = uint( LEARNED ).add( slot( cell, kind ) ).toVar();
|
|
49
|
+
const tries = float( atomicLoad( counters.element( at ) ) );
|
|
50
|
+
const seen = float( atomicLoad( counters.element( at.add( uint( 1 ) ) ) ) );
|
|
51
|
+
return sqrt( max( seen.add( 0.5 ).div( tries.add( 1.0 ) ), MIN_SHARE ) );
|
|
52
|
+
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const recordVisibility = ( counters, cell, kind, visible ) => {
|
|
56
|
+
|
|
57
|
+
const at = uint( FRESH ).add( slot( cell, kind ) ).toVar();
|
|
58
|
+
atomicAdd( counters.element( at ), uint( 1 ) );
|
|
59
|
+
If( visible, () => {
|
|
60
|
+
|
|
61
|
+
atomicAdd( counters.element( at.add( uint( 1 ) ) ), uint( 1 ) );
|
|
62
|
+
|
|
63
|
+
} );
|
|
64
|
+
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/** Adds the frame's counts to the learned ones, halving both past HALVE_AT so the table keeps up with a change. */
|
|
68
|
+
export function buildVisibilityFoldKernel( { counters } ) {
|
|
69
|
+
|
|
70
|
+
return Fn( () => {
|
|
71
|
+
|
|
72
|
+
If( instanceIndex.greaterThanEqual( uint( SLOTS ) ), () => {
|
|
73
|
+
|
|
74
|
+
Return();
|
|
75
|
+
|
|
76
|
+
} );
|
|
77
|
+
|
|
78
|
+
const learned = uint( LEARNED ).add( instanceIndex.mul( uint( 2 ) ) ).toVar();
|
|
79
|
+
const fresh = uint( FRESH ).add( instanceIndex.mul( uint( 2 ) ) ).toVar();
|
|
80
|
+
const tries = atomicLoad( counters.element( learned ) ).add( atomicLoad( counters.element( fresh ) ) ).toVar();
|
|
81
|
+
const seen = atomicLoad( counters.element( learned.add( uint( 1 ) ) ) ).add( atomicLoad( counters.element( fresh.add( uint( 1 ) ) ) ) ).toVar();
|
|
82
|
+
If( tries.greaterThan( uint( HALVE_AT ) ), () => {
|
|
83
|
+
|
|
84
|
+
tries.assign( tries.shiftRight( uint( 1 ) ) );
|
|
85
|
+
seen.assign( seen.shiftRight( uint( 1 ) ) );
|
|
86
|
+
|
|
87
|
+
} );
|
|
88
|
+
atomicStore( counters.element( learned ), tries );
|
|
89
|
+
atomicStore( counters.element( learned.add( uint( 1 ) ) ), seen );
|
|
90
|
+
atomicStore( counters.element( fresh ), uint( 0 ) );
|
|
91
|
+
atomicStore( counters.element( fresh.add( uint( 1 ) ) ), uint( 0 ) );
|
|
92
|
+
|
|
93
|
+
} )().compute( SLOTS, [ 256 ] );
|
|
94
|
+
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Forgets everything learned: a reset may have moved what blocks what. */
|
|
98
|
+
export function buildVisibilityClearKernel( { counters } ) {
|
|
99
|
+
|
|
100
|
+
const words = 2 * VISIBILITY_COUNTER_WORDS;
|
|
101
|
+
return Fn( () => {
|
|
102
|
+
|
|
103
|
+
If( instanceIndex.lessThan( uint( words ) ), () => {
|
|
104
|
+
|
|
105
|
+
atomicStore( counters.element( uint( LEARNED ).add( instanceIndex ) ), uint( 0 ) );
|
|
106
|
+
|
|
107
|
+
} );
|
|
108
|
+
|
|
109
|
+
} )().compute( words, [ 256 ] );
|
|
110
|
+
|
|
111
|
+
}
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
length,
|
|
36
36
|
mix,
|
|
37
37
|
select,
|
|
38
|
+
min,
|
|
38
39
|
If,
|
|
39
40
|
Loop,
|
|
40
41
|
sampler,
|
|
@@ -84,10 +85,12 @@ import {
|
|
|
84
85
|
computeDotProductsAniso,
|
|
85
86
|
offsetRayOrigin,
|
|
86
87
|
SHADOW_END,
|
|
88
|
+
REC709_LUMINANCE_COEFFICIENTS,
|
|
87
89
|
} from './Common.js';
|
|
88
90
|
import { sampleEnvironment, sampleEnvironmentExact } from './Environment.js';
|
|
89
91
|
import { shadowTerminatorOrigin } from './ShadowTerminator.js';
|
|
90
92
|
import { sunRadianceToward, sampleSunDisc } from './Sun.js';
|
|
93
|
+
import { VISIBILITY_KIND, visibilityShare, recordVisibility } from './LightVisibility.js';
|
|
91
94
|
|
|
92
95
|
const TWO_PI = 2.0 * PI;
|
|
93
96
|
|
|
@@ -773,13 +776,97 @@ export const calculateMaterialPDF = Fn( ( [ viewDir, lightDir, normal, material
|
|
|
773
776
|
|
|
774
777
|
} );
|
|
775
778
|
|
|
779
|
+
// Fewer shadow rays a hit (setting shadowRays). Each light kind offers its sample unshadowed; `variant`:
|
|
780
|
+
// 'one' — keeps one with chance ∝ luminance × √(its kind's learned visibility), traced and divided by that chance;
|
|
781
|
+
// 'two' — always traces the strongest as well, and picks one of the rest.
|
|
782
|
+
const ONE_BELOW = 0.99999994;
|
|
783
|
+
|
|
784
|
+
const slotOf = () => ( {
|
|
785
|
+
kind: int( 0 ).toVar(), weight: float( 0.0 ).toVar(), value: vec3( 0.0 ).toVar(),
|
|
786
|
+
origin: vec3( 0.0 ).toVar(), direction: vec3( 0.0, 1.0, 0.0 ).toVar(), distance: float( 0.0 ).toVar(),
|
|
787
|
+
} );
|
|
788
|
+
|
|
789
|
+
const FIELDS = [ 'kind', 'weight', 'value', 'origin', 'direction', 'distance' ];
|
|
790
|
+
const assignSlot = ( slot, from ) => FIELDS.forEach( ( f ) => slot[ f ].assign( from[ f ] ) );
|
|
791
|
+
|
|
792
|
+
// `learned` (LightVisibility.js) gives `share( kind )` and `record( kind, visible )`.
|
|
793
|
+
export const lightPick = ( u0, learned = null, variant = 'one' ) => {
|
|
794
|
+
|
|
795
|
+
const u = float( u0 ).toVar();
|
|
796
|
+
const total = float( 0.0 ).toVar();
|
|
797
|
+
const kept = slotOf();
|
|
798
|
+
const best = variant === 'two' ? slotOf() : null;
|
|
799
|
+
|
|
800
|
+
const keep = ( c ) => {
|
|
801
|
+
|
|
802
|
+
If( c.weight.greaterThan( 0.0 ), () => {
|
|
803
|
+
|
|
804
|
+
total.addAssign( c.weight );
|
|
805
|
+
const p = c.weight.div( total ).toVar();
|
|
806
|
+
If( u.lessThan( p ), () => {
|
|
807
|
+
|
|
808
|
+
u.assign( min( u.div( p ), ONE_BELOW ) );
|
|
809
|
+
assignSlot( kept, c );
|
|
810
|
+
|
|
811
|
+
} ).Else( () => {
|
|
812
|
+
|
|
813
|
+
u.assign( min( u.sub( p ).div( float( 1.0 ).sub( p ) ), ONE_BELOW ) );
|
|
814
|
+
|
|
815
|
+
} );
|
|
816
|
+
|
|
817
|
+
} );
|
|
818
|
+
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
const offer = ( contribution, from, dir, maxDist, lightKind = 0 ) => {
|
|
822
|
+
|
|
823
|
+
const luminance = dot( contribution, REC709_LUMINANCE_COEFFICIENTS );
|
|
824
|
+
const c = {
|
|
825
|
+
kind: int( lightKind ), value: contribution, origin: from, direction: dir, distance: maxDist,
|
|
826
|
+
weight: ( learned ? luminance.mul( learned.share( lightKind ) ) : luminance ).toVar(),
|
|
827
|
+
};
|
|
828
|
+
if ( ! best ) return keep( c );
|
|
829
|
+
|
|
830
|
+
const better = c.weight.greaterThan( best.weight ).toVar();
|
|
831
|
+
const loser = Object.fromEntries( FIELDS.map( ( f ) => [ f, select( better, best[ f ], c[ f ] ).toVar() ] ) );
|
|
832
|
+
keep( loser );
|
|
833
|
+
If( better, () => assignSlot( best, c ) );
|
|
834
|
+
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
// The direct light, tracing what the variant keeps through `shadow( origin, direction, distance )`.
|
|
838
|
+
const trace = ( shadow ) => {
|
|
839
|
+
|
|
840
|
+
const result = vec3( 0.0 ).toVar();
|
|
841
|
+
Loop( { start: int( 0 ), end: int( best ? 2 : 1 ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
842
|
+
|
|
843
|
+
const isBest = best ? i.equal( int( 0 ) ) : tslBool( false );
|
|
844
|
+
const pickOf = ( f ) => ( best ? select( isBest, best[ f ], kept[ f ] ) : kept[ f ] );
|
|
845
|
+
const weight = pickOf( 'weight' ).toVar();
|
|
846
|
+
If( weight.greaterThan( 0.0 ), () => {
|
|
847
|
+
|
|
848
|
+
const visibility = shadow( pickOf( 'origin' ), pickOf( 'direction' ), pickOf( 'distance' ) ).toVar();
|
|
849
|
+
if ( learned ) learned.record( pickOf( 'kind' ), visibility.greaterThan( 0.0 ) );
|
|
850
|
+
result.addAssign( pickOf( 'value' ).mul( visibility ).mul( select( isBest, float( 1.0 ), total.div( weight ) ) ) );
|
|
851
|
+
|
|
852
|
+
} );
|
|
853
|
+
|
|
854
|
+
} );
|
|
855
|
+
return result;
|
|
856
|
+
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
return { offer, total, trace };
|
|
860
|
+
|
|
861
|
+
};
|
|
862
|
+
|
|
776
863
|
// =============================================================================
|
|
777
864
|
// Unified Direct Lighting System
|
|
778
865
|
// =============================================================================
|
|
779
866
|
|
|
780
867
|
// Optimized direct lighting function with importance-based sampling and better MIS. `throughSurfaces` (compile
|
|
781
868
|
// 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( ( [
|
|
869
|
+
const makeDirectLighting = ( throughSurfaces, picked = null ) => Fn( ( [
|
|
783
870
|
// Surface hit data
|
|
784
871
|
hitPoint, hitNormal, geomNormal, material,
|
|
785
872
|
// View direction
|
|
@@ -813,6 +900,9 @@ const makeDirectLighting = ( throughSurfaces ) => Fn( ( [
|
|
|
813
900
|
terminatorLift, facetNormal, terminatorCutoff,
|
|
814
901
|
// The physical sky's sun (Sun.js); its BSDF-sampled partner is the miss branch in ShadeKernel.
|
|
815
902
|
hasSun, sunDirection, sunRadiance, sunParams,
|
|
903
|
+
// picked: the caller's emitter sample, unshadowed, and the learned visibility (LightVisibility.js) at this point
|
|
904
|
+
emissiveValue, emissiveOrigin, emissiveDirection, emissiveDistance,
|
|
905
|
+
visibilityCounters, visibilityCell,
|
|
816
906
|
] ) => {
|
|
817
907
|
|
|
818
908
|
const totalContribution = vec3( 0.0 ).toVar();
|
|
@@ -880,6 +970,13 @@ const makeDirectLighting = ( throughSurfaces ) => Fn( ( [
|
|
|
880
970
|
// rather than re-walking every light buffer to rebuild the same sum.
|
|
881
971
|
const neeTotalWeight = float( 0.0 ).toVar();
|
|
882
972
|
|
|
973
|
+
const learned = picked ? {
|
|
974
|
+
share: ( kind ) => visibilityShare( visibilityCounters, visibilityCell, kind ),
|
|
975
|
+
record: ( kind, visible ) => recordVisibility( visibilityCounters, visibilityCell, kind, visible ),
|
|
976
|
+
} : null;
|
|
977
|
+
const pick = picked ? lightPick( getRandomSample1D( pixelCoord, int( 0 ), dimBase, rngState, resolution, frame ), learned, picked ) : null;
|
|
978
|
+
if ( pick ) pick.offer( emissiveValue, emissiveOrigin, emissiveDirection, emissiveDistance, VISIBILITY_KIND.EMITTERS );
|
|
979
|
+
|
|
883
980
|
// =====================================================================
|
|
884
981
|
// LIGHT SAMPLING PATH
|
|
885
982
|
// =====================================================================
|
|
@@ -911,9 +1008,7 @@ const makeDirectLighting = ( throughSurfaces ) => Fn( ( [
|
|
|
911
1008
|
const lightOrigin = originFor( back, lightShadowOrigin( lightSample.direction ) ).toVar();
|
|
912
1009
|
const toSample = hitPoint.add( lightSample.direction.mul( lightSample.distance ) ).sub( lightOrigin ).toVar();
|
|
913
1010
|
const shadowDistance = length( toSample ).toVar();
|
|
914
|
-
const
|
|
915
|
-
|
|
916
|
-
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
1011
|
+
const lampLight = () => {
|
|
917
1012
|
|
|
918
1013
|
const { value: brdfValue, pdf: bPdf } = bsdfToward( lightSample.direction, back );
|
|
919
1014
|
|
|
@@ -927,17 +1022,33 @@ const makeDirectLighting = ( throughSurfaces ) => Fn( ( [
|
|
|
927
1022
|
|
|
928
1023
|
} );
|
|
929
1024
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
1025
|
+
return lightSample.emission.mul( brdfValue ).mul( NoL ).mul( misW ).div( max( lightSample.pdf, 1e-10 ) );
|
|
1026
|
+
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1029
|
+
if ( pick ) {
|
|
1030
|
+
|
|
1031
|
+
pick.offer( lampLight(), lightOrigin, toSample.div( shadowDistance ), shadowDistance.mul( SHADOW_END ), VISIBILITY_KIND.LAMPS );
|
|
1032
|
+
|
|
1033
|
+
} else {
|
|
1034
|
+
|
|
1035
|
+
const visibility = shadow( lightOrigin, toSample.div( shadowDistance ), shadowDistance.mul( SHADOW_END ) );
|
|
1036
|
+
|
|
1037
|
+
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
1038
|
+
|
|
1039
|
+
// Base contribution WITHOUT visibility; shadowed = base × visibility (identical to the
|
|
1040
|
+
// pre-dual-sum math), unoccluded = base × 1 (shadow-catcher reference only).
|
|
1041
|
+
const baseContribution = lampLight();
|
|
1042
|
+
totalContribution.addAssign( baseContribution.mul( visibility ) );
|
|
1043
|
+
If( wantUnoccluded, () => {
|
|
1044
|
+
|
|
1045
|
+
unoccludedContribution.addAssign( baseContribution );
|
|
935
1046
|
|
|
936
|
-
|
|
1047
|
+
} );
|
|
937
1048
|
|
|
938
1049
|
} );
|
|
939
1050
|
|
|
940
|
-
}
|
|
1051
|
+
}
|
|
941
1052
|
|
|
942
1053
|
} );
|
|
943
1054
|
|
|
@@ -1082,9 +1193,7 @@ const makeDirectLighting = ( throughSurfaces ) => Fn( ( [
|
|
|
1082
1193
|
|
|
1083
1194
|
If( reachesSurface( direction, back ), () => {
|
|
1084
1195
|
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
1196
|
+
const skyLight = () => {
|
|
1088
1197
|
|
|
1089
1198
|
const { value: brdfValue, pdf: bPdf } = bsdfToward( direction, back );
|
|
1090
1199
|
|
|
@@ -1096,8 +1205,24 @@ const makeDirectLighting = ( throughSurfaces ) => Fn( ( [
|
|
|
1096
1205
|
float( 1.0 )
|
|
1097
1206
|
).toVar();
|
|
1098
1207
|
|
|
1208
|
+
return radiance.mul( brdfValue ).mul( NoL ).mul( misW ).div( max( lightPdf, 1e-10 ) );
|
|
1209
|
+
|
|
1210
|
+
};
|
|
1211
|
+
|
|
1212
|
+
if ( pick ) {
|
|
1213
|
+
|
|
1214
|
+
pick.offer( skyLight(), originFor( back, lightShadowOrigin( direction ) ), direction, float( 1e20 ),
|
|
1215
|
+
select( isSun, int( VISIBILITY_KIND.SUN ), int( VISIBILITY_KIND.ENVIRONMENT ) ) );
|
|
1216
|
+
return;
|
|
1217
|
+
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
const visibility = shadow( originFor( back, lightShadowOrigin( direction ) ), direction, float( 1e20 ) );
|
|
1221
|
+
|
|
1222
|
+
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
1223
|
+
|
|
1099
1224
|
// Base contribution WITHOUT visibility (deterministic estimator; no stochastic scaling).
|
|
1100
|
-
const baseContribution =
|
|
1225
|
+
const baseContribution = skyLight();
|
|
1101
1226
|
totalContribution.addAssign( baseContribution.mul( visibility ) );
|
|
1102
1227
|
If( wantUnoccluded, () => {
|
|
1103
1228
|
|
|
@@ -1115,6 +1240,8 @@ const makeDirectLighting = ( throughSurfaces ) => Fn( ( [
|
|
|
1115
1240
|
|
|
1116
1241
|
} );
|
|
1117
1242
|
|
|
1243
|
+
if ( pick ) totalContribution.addAssign( pick.trace( shadow ) );
|
|
1244
|
+
|
|
1118
1245
|
// EMISSIVE TRIANGLE DIRECT LIGHTING
|
|
1119
1246
|
// NOTE: Emissive triangle sampling is handled separately in pathtracer_core.fs
|
|
1120
1247
|
// to bypass firefly suppression. Do not add it here to avoid double-counting.
|
|
@@ -1125,3 +1252,12 @@ const makeDirectLighting = ( throughSurfaces ) => Fn( ( [
|
|
|
1125
1252
|
|
|
1126
1253
|
export const calculateDirectLightingUnified = /*@__PURE__*/ makeDirectLighting( false );
|
|
1127
1254
|
export const calculateDirectLightingThroughSurfaces = /*@__PURE__*/ makeDirectLighting( true );
|
|
1255
|
+
const pickedLighting = new Map();
|
|
1256
|
+
/** The direct light with fewer shadow rays a hit; `variant` as lightPick takes it. */
|
|
1257
|
+
export const calculateDirectLightingPicked = ( throughSurfaces, variant ) => {
|
|
1258
|
+
|
|
1259
|
+
const key = `${throughSurfaces}:${variant}`;
|
|
1260
|
+
if ( ! pickedLighting.has( key ) ) pickedLighting.set( key, makeDirectLighting( throughSurfaces, variant ) );
|
|
1261
|
+
return pickedLighting.get( key );
|
|
1262
|
+
|
|
1263
|
+
};
|
package/src/TSL/Random.js
CHANGED
|
@@ -107,7 +107,7 @@ export const RandomPointInCircle = ( rngState ) => {
|
|
|
107
107
|
// +7 ground-catcher cosine BSDF (ShadeKernel)
|
|
108
108
|
// +8 indirect-strategy direction (LightsIndirect)
|
|
109
109
|
// +9 physical-sky sun disc (LightsSampling)
|
|
110
|
-
// 1D +0
|
|
110
|
+
// 1D +0 light kind for the picked shadow ray (LightsSampling lightPick, setting shadowRays)
|
|
111
111
|
// +1 emissive-triangle pick (EmissiveSampling / LightBVHSampling)
|
|
112
112
|
// +2 BSDF lobe selection (ShadeKernel)
|
|
113
113
|
// +3 Russian roulette (PathTracerCore)
|
package/src/TSL/ShadeKernel.js
CHANGED
|
@@ -21,6 +21,7 @@ import { sampleAllMaterialTextures, processAnisotropyMap, applyExtensionMaps, ge
|
|
|
21
21
|
import { evaluateMaterialResponse } from './MaterialEvaluation.js';
|
|
22
22
|
import {
|
|
23
23
|
calculateDirectLightingUnified, calculateDirectLightingThroughSurfaces, calculateMaterialPDF,
|
|
24
|
+
calculateDirectLightingPicked,
|
|
24
25
|
sampleDirectionalLight, sampleRectAreaLight, samplePointLightWithAttenuation, sampleSpotLightWithRadius,
|
|
25
26
|
areaLightRadiance, areaLightSpreadAttenuation,
|
|
26
27
|
} from './LightsSampling.js';
|
|
@@ -28,6 +29,7 @@ import { traceShadowRay, traceShadowRayRefractiveOpaque, estimateLightImportance
|
|
|
28
29
|
import { shadowTerminatorOrigin } from './ShadowTerminator.js';
|
|
29
30
|
import { hitFacet, unpackHitFacet, windingNormal } from './HitFacet.js';
|
|
30
31
|
import { traverseBVHShadow, triangleSide, sideAccepts } from './BVHTraversal.js';
|
|
32
|
+
import { visibilityCell } from './LightVisibility.js';
|
|
31
33
|
import { handleMaterialTransparency, MaterialInteractionResult } from './MaterialTransmission.js';
|
|
32
34
|
import { sampleChromaticCollision, sampleHenyeyGreenstein, subsurfaceCoefficients, CollisionSample, MediumCoeffs } from './Subsurface.js';
|
|
33
35
|
import { ALL_MATERIAL_LAYERS } from './SceneResources.js';
|
|
@@ -44,7 +46,7 @@ import { sampleClearcoat, ClearcoatResult } from './Clearcoat.js';
|
|
|
44
46
|
import { refineDisplacedIntersection, DisplacementResult } from './Displacement.js';
|
|
45
47
|
import {
|
|
46
48
|
calculateEmissiveTriangleContribution, calculateEmissiveLightPdf, EmissiveSample, EmissiveSampleIndexed,
|
|
47
|
-
sampleEmissiveTriangleIndexed, fetchTriangleData, TriangleData,
|
|
49
|
+
sampleEmissiveTriangle, sampleEmissiveTriangleIndexed, fetchTriangleData, TriangleData,
|
|
48
50
|
} from './EmissiveSampling.js';
|
|
49
51
|
import { sampleLightBVHTriangle, sampleLightBVHTriangleIndexed, calculateLightBVHPdf } from './LightBVHSampling.js';
|
|
50
52
|
import {
|
|
@@ -134,6 +136,9 @@ export function buildShadeKernel( params ) {
|
|
|
134
136
|
// The material layers some material uses (MaterialDataManager.materialLayers); the rest compile out. Bidirectional
|
|
135
137
|
// always leaves diffuse transmission out: its connections and light tracing do not cross surfaces yet.
|
|
136
138
|
materialLayers: layers = ALL_MATERIAL_LAYERS,
|
|
139
|
+
// Fewer shadow rays a hit, emitters included: null (one per light kind) or a lightPick variant; path tracing only.
|
|
140
|
+
shadowRays = null,
|
|
141
|
+
cameraWorldMatrix = null,
|
|
137
142
|
} = params;
|
|
138
143
|
const diffuseTransmission = layers.diffuseTransmission;
|
|
139
144
|
|
|
@@ -145,6 +150,7 @@ export function buildShadeKernel( params ) {
|
|
|
145
150
|
const auxOn = gBuffer ? auxGBufferEnabled.greaterThan( uint( 0 ) ) : null;
|
|
146
151
|
|
|
147
152
|
const useEmissiveNEE = lightBuffer !== undefined;
|
|
153
|
+
const picksLights = shadowRays && ! bdpt;
|
|
148
154
|
const lamps = {
|
|
149
155
|
directional: directionalLightsBuffer, numDirectional: numDirectionalLights, area: areaLightsBuffer, numArea: numAreaLights,
|
|
150
156
|
point: pointLightsBuffer, numPoint: numPointLights, spot: spotLightsBuffer, numSpot: numSpotLights,
|
|
@@ -1607,7 +1613,85 @@ export function buildShadeKernel( params ) {
|
|
|
1607
1613
|
|
|
1608
1614
|
} ).Else( sampleLobes );
|
|
1609
1615
|
|
|
1610
|
-
const
|
|
1616
|
+
const emitterCandidate = () => {
|
|
1617
|
+
|
|
1618
|
+
const value = vec3( 0.0 ).toVar();
|
|
1619
|
+
const from = vec3( 0.0 ).toVar();
|
|
1620
|
+
const dir = vec3( 0.0, 1.0, 0.0 ).toVar();
|
|
1621
|
+
const dist = float( 0.0 ).toVar();
|
|
1622
|
+
if ( ! useEmissiveNEE ) return [ value, from, dir, dist ];
|
|
1623
|
+
|
|
1624
|
+
If( enableEmissiveTriangleSampling.equal( int( 1 ) ).and( emissiveTriangleCount.greaterThan( int( 0 ) ) ), () => {
|
|
1625
|
+
|
|
1626
|
+
const position = vec3( 0.0 ).toVar();
|
|
1627
|
+
const direction = vec3( 0.0, 1.0, 0.0 ).toVar();
|
|
1628
|
+
const emission = vec3( 0.0 ).toVar();
|
|
1629
|
+
const pdf = float( 0.0 ).toVar();
|
|
1630
|
+
const valid = tslBool( false ).toVar();
|
|
1631
|
+
const take = ( sample ) => {
|
|
1632
|
+
|
|
1633
|
+
const e = EmissiveSample.wrap( sample );
|
|
1634
|
+
position.assign( e.position );
|
|
1635
|
+
direction.assign( e.direction );
|
|
1636
|
+
emission.assign( e.emission );
|
|
1637
|
+
pdf.assign( e.pdf );
|
|
1638
|
+
valid.assign( e.valid );
|
|
1639
|
+
|
|
1640
|
+
};
|
|
1641
|
+
|
|
1642
|
+
If( lightBVHNodeCount.greaterThan( int( 0 ) ), () => {
|
|
1643
|
+
|
|
1644
|
+
take( sampleLightBVHTriangle(
|
|
1645
|
+
hitPoint, N, rngState, _pixelCoord, resolution, frame, dimBase,
|
|
1646
|
+
lightBuffer, lightBuffer, emissiveVec4Offset, triangleBuffer, bvhBuffer, transmitsDiffusely,
|
|
1647
|
+
) );
|
|
1648
|
+
|
|
1649
|
+
} ).Else( () => {
|
|
1650
|
+
|
|
1651
|
+
take( sampleEmissiveTriangle(
|
|
1652
|
+
hitPoint, N, rngState, _pixelCoord, resolution, frame, dimBase,
|
|
1653
|
+
lightBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower, triangleBuffer, bvhBuffer, transmitsDiffusely,
|
|
1654
|
+
) );
|
|
1655
|
+
|
|
1656
|
+
} );
|
|
1657
|
+
|
|
1658
|
+
If( valid.and( pdf.greaterThan( 0.0 ) ), () => {
|
|
1659
|
+
|
|
1660
|
+
const back = throughSurface( direction ).toVar();
|
|
1661
|
+
const NoL = transmitting ? abs( dot( N, direction ) ) : max( float( 0.0 ), dot( N, direction ) );
|
|
1662
|
+
const inFront = NoL.greaterThan( 0.0 ).and( dot( N, direction ).greaterThan( 0.0 ) )
|
|
1663
|
+
.and( dot( direction, NgeoFF ).greaterThan( 0.0 ) );
|
|
1664
|
+
|
|
1665
|
+
If( transmitting ? back.or( inFront ) : inFront, () => {
|
|
1666
|
+
|
|
1667
|
+
from.assign( originFor( direction, lightShadowOrigin( direction ) ) );
|
|
1668
|
+
const toSample = position.sub( from ).toVar();
|
|
1669
|
+
const span = length( toSample ).toVar();
|
|
1670
|
+
dir.assign( toSample.div( span ) );
|
|
1671
|
+
dist.assign( span.mul( SHADOW_END ) );
|
|
1672
|
+
|
|
1673
|
+
const bPdf = calculateMaterialPDF( V, direction, N, material );
|
|
1674
|
+
const misW = select( bPdf.greaterThan( 0.0 ), powerHeuristic( { pdf1: pdf, pdf2: bPdf } ), float( 1.0 ) );
|
|
1675
|
+
value.assign( emission.mul( evaluateMaterialResponse( V, direction, N, material ) ).mul( NoL )
|
|
1676
|
+
.div( pdf ).mul( emissiveBoost ).mul( misW ) );
|
|
1677
|
+
|
|
1678
|
+
} );
|
|
1679
|
+
|
|
1680
|
+
} );
|
|
1681
|
+
|
|
1682
|
+
} );
|
|
1683
|
+
|
|
1684
|
+
return [ value, from, dir, dist ];
|
|
1685
|
+
|
|
1686
|
+
};
|
|
1687
|
+
|
|
1688
|
+
const pickArgs = picksLights
|
|
1689
|
+
? [ ...emitterCandidate(), counters, visibilityCell( hitPoint, NgeoFF, vec3( cameraWorldMatrix[ 3 ] ) ) ]
|
|
1690
|
+
: [];
|
|
1691
|
+
const directLightingFn = picksLights
|
|
1692
|
+
? calculateDirectLightingPicked( transmitting, shadowRays )
|
|
1693
|
+
: ( transmitting ? calculateDirectLightingThroughSurfaces : calculateDirectLightingUnified );
|
|
1694
|
+
const directLighting = () => DirectLightingDual.wrap( directLightingFn(
|
|
1611
1695
|
hitPoint, N, NgeoFF, material, V,
|
|
1612
1696
|
brdfDir, brdfPdf, brdfValue,
|
|
1613
1697
|
bounceIndex, rngState,
|
|
@@ -1624,6 +1708,7 @@ export function buildShadeKernel( params ) {
|
|
|
1624
1708
|
tslBool( false ), // wantUnoccluded: false on real surfaces — dead-codes the unoccluded sum
|
|
1625
1709
|
terminatorLift, facetN, shadowTerminatorOffset,
|
|
1626
1710
|
hasSun, sunDirection, sunRadiance, sunParams,
|
|
1711
|
+
...pickArgs,
|
|
1627
1712
|
) ).shadowed;
|
|
1628
1713
|
// Bidirectional mode samples every light itself, below.
|
|
1629
1714
|
const directLight = bdpt ? null : directLighting().toVar();
|
|
@@ -1954,7 +2039,7 @@ export function buildShadeKernel( params ) {
|
|
|
1954
2039
|
|
|
1955
2040
|
} );
|
|
1956
2041
|
|
|
1957
|
-
} else if ( useEmissiveNEE ) {
|
|
2042
|
+
} else if ( useEmissiveNEE && ! picksLights ) {
|
|
1958
2043
|
|
|
1959
2044
|
If(
|
|
1960
2045
|
enableEmissiveTriangleSampling.equal( int( 1 ) )
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
ExtMapResult,
|
|
9
9
|
} from './Struct.js';
|
|
10
10
|
import { instanceRows, instanceDirToWorld, triangleRow } from './Common.js';
|
|
11
|
-
import { TEXTURE_CONSTANTS } from '../
|
|
11
|
+
import { TEXTURE_CONSTANTS } from '../Processor/TextureBuckets.js';
|
|
12
12
|
|
|
13
13
|
// ================================================================================
|
|
14
14
|
// CONSOLIDATED SHAPE-BUCKETED MATERIAL TEXTURES
|
package/src/core.js
CHANGED
|
@@ -21,15 +21,11 @@ export { RenderSettings, SETTING_SOURCE } from './RenderSettings.js';
|
|
|
21
21
|
export { configureAssets, getAssetConfig } from './AssetConfig.js';
|
|
22
22
|
export { configurePlatform, getPlatform } from './Platform.js';
|
|
23
23
|
export { LightManager } from './managers/LightManager.js';
|
|
24
|
-
export { listViewTransforms, getViewTransform, onRegistryChange } from './Color/ViewTransforms.js';
|
|
24
|
+
export { listViewTransforms, getViewTransform, onRegistryChange, DEFAULT_VIEW } from './Color/ViewTransforms.js';
|
|
25
25
|
export { ARCHIVE_FORMATS } from './Processor/archiveFormats.js';
|
|
26
|
-
export {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
getRenderProfile,
|
|
31
|
-
MAX_RESERVABLE_RENDER_SIZE,
|
|
32
|
-
TRIANGLE_DATA_LAYOUT,
|
|
33
|
-
} from './EngineDefaults.js';
|
|
26
|
+
export { ENGINE_DEFAULTS, MATERIAL_DEFAULTS } from './EngineDefaults.js';
|
|
27
|
+
export { SKY_DEFAULTS } from './managers/EnvironmentManager.js';
|
|
28
|
+
export { MAX_RESERVABLE_RENDER_SIZE } from './Processor/StorageTexturePool.js';
|
|
29
|
+
export { TRIANGLE_DATA_LAYOUT } from './Processor/BufferLayout.js';
|
|
34
30
|
export { Logger, createLogger, fmt, LOG_LEVELS } from './utils/Logger.js';
|
|
35
31
|
export { VERSION } from './version.js';
|
package/src/index.js
CHANGED
|
@@ -57,30 +57,19 @@ export { Logger, createLogger, fmt, LOG_LEVELS } from './utils/Logger.js';
|
|
|
57
57
|
// Configuration defaults and presets
|
|
58
58
|
export {
|
|
59
59
|
ENGINE_DEFAULTS,
|
|
60
|
-
ASVGF_QUALITY_PRESETS,
|
|
61
|
-
NRD_DEFAULTS,
|
|
62
|
-
NRD_QUALITY_PRESETS,
|
|
63
|
-
NRD_PRESET_KEYS,
|
|
64
|
-
NRD_HIT_DIST_A,
|
|
65
|
-
NRD_HIT_DIST_B,
|
|
66
|
-
CAMERA_PRESETS,
|
|
67
|
-
CAMERA_RANGES,
|
|
68
|
-
SKY_PRESETS,
|
|
69
|
-
DEFAULT_SUN_PATH,
|
|
70
|
-
AUTO_FOCUS_MODES,
|
|
71
|
-
AF_DEFAULTS,
|
|
72
|
-
TRIANGLE_DATA_LAYOUT,
|
|
73
|
-
BVH_LEAF_MARKERS,
|
|
74
|
-
TEXTURE_CONSTANTS,
|
|
75
|
-
DEFAULT_TEXTURE_MATRIX,
|
|
76
60
|
MATERIAL_DEFAULTS,
|
|
77
|
-
MEMORY_CONSTANTS,
|
|
78
61
|
PRODUCTION_RENDER_CONFIG,
|
|
79
62
|
INTERACTIVE_RENDER_CONFIG,
|
|
80
|
-
MAX_RESERVABLE_RENDER_SIZE,
|
|
81
|
-
RENDER_PROFILES,
|
|
82
|
-
getRenderProfile,
|
|
83
63
|
} from './EngineDefaults.js';
|
|
64
|
+
export { MAX_RESERVABLE_RENDER_SIZE } from './Processor/StorageTexturePool.js';
|
|
65
|
+
export {
|
|
66
|
+
DENOISER_DEFAULTS, ASVGF_QUALITY_PRESETS, NRD_DEFAULTS, NRD_QUALITY_PRESETS, NRD_PRESET_KEYS, NRD_HIT_DIST_A,
|
|
67
|
+
NRD_HIT_DIST_B,
|
|
68
|
+
} from './Stages/DenoiserSettings.js';
|
|
69
|
+
export { SKY_DEFAULTS, DEFAULT_SUN_PATH } from './managers/EnvironmentManager.js';
|
|
70
|
+
export { AUTO_EXPOSURE_DEFAULTS } from './Stages/AutoExposure.js';
|
|
71
|
+
export { AUTO_FOCUS_DEFAULTS } from './managers/CameraManager.js';
|
|
72
|
+
export { TRIANGLE_DATA_LAYOUT, BVH_LEAF_MARKERS } from './Processor/BufferLayout.js';
|
|
84
73
|
export { sunPosition, timeForSunElevation, dayOfYearForMonth } from './Processor/SunPosition.js';
|
|
85
74
|
|
|
86
75
|
// Settings & managers (for advanced consumers)
|
|
@@ -141,7 +130,7 @@ export { displayCanvasFit } from './Color/Displays.js';
|
|
|
141
130
|
export {
|
|
142
131
|
VIEW_TRANSFORMS, listViewTransforms, getViewTransform, addViewTransform, removeViewTransform,
|
|
143
132
|
onRegistryChange, getRegistryVersion, buildToneMapWGSL,
|
|
144
|
-
OCIO_VIEW_BASE, MAX_TABLE_TRANSFORMS,
|
|
133
|
+
OCIO_VIEW_BASE, MAX_TABLE_TRANSFORMS, DEFAULT_VIEW,
|
|
145
134
|
} from './Color/ViewTransforms.js';
|
|
146
135
|
export {
|
|
147
136
|
buildOcioView, addOcioView, addAllOcioViews,
|
|
@@ -12,7 +12,8 @@ import { gpuOnlyStorageAttribute } from '../TSL/patches.js';
|
|
|
12
12
|
import { LIGHT_FLOATS } from '../managers/UniformManager.js';
|
|
13
13
|
import { HIT_STRIDE_BIDIRECTIONAL, LIGHT_VERTEX_STRIDE, freeStorageAttribute } from '../Processor/PackedRayBuffer.js';
|
|
14
14
|
import { COUNTER, ENERGY_SCALE } from '../Processor/QueueManager.js';
|
|
15
|
-
import { CAMERA_PROJECTION_IDS
|
|
15
|
+
import { CAMERA_PROJECTION_IDS } from '../EngineDefaults.js';
|
|
16
|
+
import { BVH_MAX_INDEX } from '../Processor/BufferLayout.js';
|
|
16
17
|
import { BOUNCE_KERNELS, LIST_WG_SIZE } from '../Stages/PathTracer.js';
|
|
17
18
|
import { buildLightGenerateKernel, LIGHT_GENERATE_WG_SIZE } from '../TSL/LightGenerateKernel.js';
|
|
18
19
|
import { buildGuideKernel, buildGuideClearKernel, guidedDiscPdf, recordEscape, GUIDE_BINS, GUIDE_ROW_STRIDE, GUIDE_TEXTURE_WIDTH } from '../TSL/LightGuide.js';
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { EventDispatcher, MathUtils, Matrix4, OrthographicCamera, PerspectiveCamera, Quaternion, Vector3 } from 'three';
|
|
2
2
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
3
3
|
import { EngineEvents, } from '../EngineEvents.js';
|
|
4
|
-
import {
|
|
4
|
+
import { CAMERA_PROJECTION_IDS } from '../EngineDefaults.js';
|
|
5
5
|
import { viewDepth } from './InteractionManager.js';
|
|
6
6
|
import { ViewCamera } from './ViewCamera.js';
|
|
7
7
|
import { WalkControls } from './WalkControls.js';
|
|
8
8
|
import { toPortable, fromPortable } from '../SceneState/portable.js';
|
|
9
9
|
|
|
10
|
+
export const AUTO_FOCUS_DEFAULTS = {
|
|
11
|
+
autoFocusMode: 'auto', // 'manual' | 'auto'
|
|
12
|
+
afScreenPoint: { x: 0.5, y: 0.5 },
|
|
13
|
+
afSmoothingFactor: 0.15,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const AF_RESET_THRESHOLD = 0.05;
|
|
17
|
+
const AF_FALLBACK_DISTANCE = 10.0;
|
|
18
|
+
const AF_SNAP_THRESHOLD = 0.5;
|
|
19
|
+
|
|
10
20
|
const DEFAULT_CAMERA_SCALE = Object.freeze( new Vector3( 1, 1, 1 ) );
|
|
11
21
|
|
|
12
22
|
/** The height of an orthographic camera's view in world units, or null for any other camera. */
|
|
@@ -55,9 +65,9 @@ export class CameraManager extends EventDispatcher {
|
|
|
55
65
|
this._userCameraCounter = 0;
|
|
56
66
|
|
|
57
67
|
// Auto-focus state
|
|
58
|
-
this.autoFocusMode =
|
|
59
|
-
this.afScreenPoint = {
|
|
60
|
-
this.afSmoothingFactor =
|
|
68
|
+
this.autoFocusMode = AUTO_FOCUS_DEFAULTS.autoFocusMode;
|
|
69
|
+
this.afScreenPoint = { ...AUTO_FOCUS_DEFAULTS.afScreenPoint };
|
|
70
|
+
this.afSmoothingFactor = AUTO_FOCUS_DEFAULTS.afSmoothingFactor;
|
|
61
71
|
this._lastValidFocusDistance = null;
|
|
62
72
|
this._smoothedFocusDistance = null;
|
|
63
73
|
this._afPointDirty = false;
|
|
@@ -873,7 +883,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
873
883
|
|
|
874
884
|
// Nothing under the AF point on a fresh view: focus where the camera orbits.
|
|
875
885
|
const depth = this.controls ? viewDepth( this.controls.target, this.camera ) : 0;
|
|
876
|
-
rawDistance = depth > 0 ? depth :
|
|
886
|
+
rawDistance = depth > 0 ? depth : AF_FALLBACK_DISTANCE * ( assetLoader?.getSceneScale() || 1.0 );
|
|
877
887
|
this._lastValidFocusDistance = rawDistance;
|
|
878
888
|
|
|
879
889
|
}
|
|
@@ -893,7 +903,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
893
903
|
const changeFraction = Math.abs( rawDistance - this._smoothedFocusDistance )
|
|
894
904
|
/ this._smoothedFocusDistance;
|
|
895
905
|
|
|
896
|
-
if ( changeFraction >
|
|
906
|
+
if ( changeFraction > AF_SNAP_THRESHOLD ) {
|
|
897
907
|
|
|
898
908
|
this._smoothedFocusDistance = rawDistance;
|
|
899
909
|
|
|
@@ -921,7 +931,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
921
931
|
|
|
922
932
|
hardReset?.();
|
|
923
933
|
|
|
924
|
-
} else if ( changeRatio >
|
|
934
|
+
} else if ( changeRatio > AF_RESET_THRESHOLD ) {
|
|
925
935
|
|
|
926
936
|
softReset?.();
|
|
927
937
|
|