rayzee 9.0.0 → 9.1.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 +139 -8
- package/dist/{NeuralRetouch-CYOJ21Wn.js → NeuralRetouch-BRquqfmZ.js} +1 -1
- package/dist/{NeuralSuperRes-CQLCLpcC.js → NeuralSuperRes-CbNvQjCw.js} +3 -2
- package/dist/{ToneMapGPU-BVE852iN.js → ToneMapGPU-DpUZDMbQ.js} +2 -1
- package/dist/rayzee.es.js +10344 -5334
- package/dist/rayzee.umd.js +265 -265
- package/package.json +1 -1
- package/src/AssetConfig.js +4 -0
- package/src/EngineDefaults.js +38 -56
- package/src/EngineEvents.js +3 -0
- package/src/EngineIssues.js +6 -0
- package/src/Headless.js +3 -1
- package/src/Passes/AIUpscaler.js +2 -1
- package/src/Passes/OIDNDenoiser.js +3 -6
- package/src/PathTracerApp.js +290 -19
- package/src/Processor/ArchiveCache.js +120 -0
- package/src/Processor/ArchiveReader.js +179 -32
- package/src/Processor/AssetLoader.js +514 -76
- package/src/Processor/AtmosphereModel.js +465 -0
- package/src/Processor/BVHBuilder.js +4 -1
- package/src/Processor/BVHLeafFold.js +100 -0
- package/src/Processor/BVHRefitter.js +98 -214
- package/src/Processor/ChunkedRecords.js +189 -4
- package/src/Processor/EmissiveTriangleBuilder.js +8 -0
- package/src/Processor/GeometryExtractor.js +238 -40
- package/src/Processor/InstanceTable.js +55 -37
- package/src/Processor/PBRT/PBRTCurves.js +104 -27
- package/src/Processor/PBRT/PBRTParser.js +132 -22
- package/src/Processor/PBRT/PBRTSceneBuilder.js +192 -49
- package/src/Processor/PBRT/buffers.js +27 -0
- package/src/Processor/PBRT/index.js +55 -15
- package/src/Processor/PackedRayBuffer.js +10 -13
- package/src/Processor/ParallelBVHBuilder.js +36 -15
- package/src/Processor/PhysicalSky.js +308 -0
- package/src/Processor/QueueManager.js +1 -0
- package/src/Processor/SceneProcessor.js +1132 -181
- package/src/Processor/SplitBLAS.js +249 -0
- package/src/Processor/SunPosition.js +41 -0
- package/src/Processor/TextureCreator.js +21 -0
- package/src/Processor/Workers/AIUpscalerWorker.js +79 -52
- package/src/Processor/Workers/BVHWorker.js +17 -13
- package/src/Processor/ZipReader.js +207 -0
- package/src/RenderSettings.js +38 -0
- package/src/SceneState/SceneState.js +367 -0
- package/src/SceneState/portable.js +52 -0
- package/src/Stages/NormalDepth.js +7 -6
- package/src/Stages/PathTracer.js +108 -4
- package/src/Stages/PathTracerStage.js +251 -104
- package/src/Storage/BLASCache.js +190 -0
- package/src/Storage/CDFCache.js +77 -0
- package/src/Storage/DownloadCache.js +266 -0
- package/src/Storage/GeometrySpill.js +217 -0
- package/src/Storage/SceneGraphCodec.js +503 -0
- package/src/Storage/SpillStore.js +112 -0
- package/src/Storage/StorageManager.js +934 -0
- package/src/Storage/StorageOps.js +137 -0
- package/src/Storage/StorageWorker.js +37 -0
- package/src/Storage/events.js +29 -0
- package/src/Storage/identity.js +77 -0
- package/src/Storage/index.js +9 -0
- package/src/Storage/inlineTransport.js +24 -0
- package/src/Storage/locks.js +99 -0
- package/src/Storage/openStorage.js +87 -0
- package/src/Storage/sceneCachePolicy.js +12 -0
- package/src/Storage/shared.js +9 -0
- package/src/Storage/transport.js +62 -0
- package/src/TSL/Atmosphere.js +684 -0
- package/src/TSL/BVHTraversal.js +288 -239
- package/src/TSL/Common.js +11 -2
- package/src/TSL/Displacement.js +6 -6
- package/src/TSL/EmissiveSampling.js +5 -5
- package/src/TSL/EnvironmentCDF.js +176 -0
- package/src/TSL/GenerateKernel.js +3 -3
- package/src/TSL/HitFacet.js +4 -4
- package/src/TSL/LightsDirect.js +8 -8
- package/src/TSL/LightsSampling.js +55 -37
- package/src/TSL/Random.js +1 -0
- package/src/TSL/ShadeKernel.js +75 -19
- package/src/TSL/Sun.js +40 -0
- package/src/TSL/TextureSampling.js +6 -7
- package/src/index.js +18 -0
- package/src/managers/CameraManager.js +169 -1
- package/src/managers/EnvironmentManager.js +263 -33
- package/src/managers/GoboManager.js +19 -8
- package/src/managers/IESManager.js +3 -3
- package/src/managers/LightManager.js +98 -5
- package/src/managers/MaterialDataManager.js +22 -2
- package/src/managers/TransformManager.js +26 -0
- package/src/managers/UniformManager.js +5 -3
- package/src/managers/VideoRenderManager.js +3 -1
- package/src/managers/timeline/CameraTrack.js +22 -0
- package/src/managers/timeline/TimelineManager.js +14 -0
- package/src/version.js +3 -0
- package/src/Processor/ProceduralSky.js +0 -291
package/src/TSL/ShadeKernel.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ShadeKernel.js — wavefront material eval + bounce generation. 256×1 workgroup, 1D dispatch.
|
|
3
|
-
* 10 storage-buffer bindings: bvh,
|
|
4
|
-
* (at the device per-stage limit of 10; envCDF is a texture, not a storage buffer).
|
|
3
|
+
* 10 storage-buffer bindings: bvh, triGeo, triShade, mat, light, ray, hit (+ RNG state), gBuffer,
|
|
4
|
+
* counters, activeIndices (at the device per-stage limit of 10; envCDF is a texture, not a storage buffer).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
Fn, float, vec2, vec3, vec4, int, uint,
|
|
9
9
|
bool as tslBool,
|
|
10
|
-
If, Loop, normalize, max, min, exp, log, clamp, dot, length, select, smoothstep,
|
|
10
|
+
If, Loop, normalize, max, min, exp, log, clamp, dot, length, select, smoothstep, acos,
|
|
11
11
|
instanceIndex,
|
|
12
12
|
sampler,
|
|
13
13
|
atomicAdd, atomicLoad, atomicStore, uintBitsToFloat,
|
|
@@ -28,6 +28,7 @@ import { handleMaterialTransparency, MaterialInteractionResult } from './Materia
|
|
|
28
28
|
import { sampleChromaticCollision, sampleHenyeyGreenstein, subsurfaceCoefficients, CollisionSample, MediumCoeffs } from './Subsurface.js';
|
|
29
29
|
import { calculateIndirectLighting } from './LightsIndirect.js';
|
|
30
30
|
import { IndirectLightingResult, sampleCone } from './LightsCore.js';
|
|
31
|
+
import { sunRadianceToward } from './Sun.js';
|
|
31
32
|
import { regularizePathContribution, generateSampledDirection, computeNDCDepth, handleRussianRoulette } from './PathTracerCore.js';
|
|
32
33
|
import { evaluateSpecularDFG, baseFresnelParams } from './MaterialProperties.js';
|
|
33
34
|
import { NRD_HIT_DIST_A, NRD_HIT_DIST_B } from '../EngineDefaults.js';
|
|
@@ -63,6 +64,7 @@ import {
|
|
|
63
64
|
writeGBuffer, writeGBufferHitDist, readGBuffer, gbDecodeNormalDepth,
|
|
64
65
|
readRayRadiance,
|
|
65
66
|
readFeatureThroughput, writeFeatureThroughput,
|
|
67
|
+
readRngState, writeRngState,
|
|
66
68
|
} from '../Processor/PackedRayBuffer.js';
|
|
67
69
|
|
|
68
70
|
const WG_SIZE = 256;
|
|
@@ -79,7 +81,7 @@ export function buildShadeKernel( params ) {
|
|
|
79
81
|
bvhBuffer, triangleBuffer, materialBuffer,
|
|
80
82
|
envCDFTexture,
|
|
81
83
|
lightBuffer,
|
|
82
|
-
rayBufferRW,
|
|
84
|
+
rayBufferRW, hitBufferRW, gBufferRW,
|
|
83
85
|
counters,
|
|
84
86
|
activeIndicesRO,
|
|
85
87
|
envTexture, environmentIntensity, envMatrix,
|
|
@@ -111,6 +113,7 @@ export function buildShadeKernel( params ) {
|
|
|
111
113
|
// Aux G-buffer (normal/depth/albedo + surface ID) feeds only the denoiser/OIDN MRT. Gated by a
|
|
112
114
|
// live uniform (1 = denoiser on) so the wavefront skips these writes when nothing consumes them.
|
|
113
115
|
auxGBufferEnabled,
|
|
116
|
+
hasSun, sunDirection, sunRadiance, sunParams,
|
|
114
117
|
} = params;
|
|
115
118
|
|
|
116
119
|
const auxOn = auxGBufferEnabled.greaterThan( uint( 0 ) );
|
|
@@ -196,7 +199,7 @@ export function buildShadeKernel( params ) {
|
|
|
196
199
|
const currentRadiance = readRayRadiance( rayBufferRW, rayID ).toVar();
|
|
197
200
|
// One ray per pixel: rayID is the pixel index.
|
|
198
201
|
const pixelIndex = rayID;
|
|
199
|
-
const rngState =
|
|
202
|
+
const rngState = readRngState( hitBufferRW, rayID ).toVar();
|
|
200
203
|
|
|
201
204
|
// STBN keyed on (GLOBAL pixel, dimension, frame). pixelIndex is the LOCAL path slot; the global pixel
|
|
202
205
|
// = chunkRowBase·W + localSlot, so the blue-noise pattern stays spatially aligned across row-band chunks
|
|
@@ -216,13 +219,13 @@ export function buildShadeKernel( params ) {
|
|
|
216
219
|
// deferring continue (a missed persist = stale tint). 1 = untinted (direct hit → today's albedo).
|
|
217
220
|
const featCarry = readFeatureThroughput( rayBufferRW, rayID ).toVar();
|
|
218
221
|
|
|
219
|
-
const hitDist = readHitDistance(
|
|
220
|
-
const hitNormal = readHitNormal(
|
|
222
|
+
const hitDist = readHitDistance( hitBufferRW, rayID ).toVar();
|
|
223
|
+
const hitNormal = readHitNormal( hitBufferRW, rayID ).toVar();
|
|
221
224
|
// hitInfo.uv is the interpolated texture UV (not barycentrics)
|
|
222
|
-
const hitUV = readHitBarycentrics(
|
|
223
|
-
const hitMatIdx = readHitMaterialIndex(
|
|
224
|
-
const hitTriIdx = readHitTriangleIndex(
|
|
225
|
-
const hitInstance = readHitInstanceLeaf(
|
|
225
|
+
const hitUV = readHitBarycentrics( hitBufferRW, rayID ).toVar();
|
|
226
|
+
const hitMatIdx = readHitMaterialIndex( hitBufferRW, rayID ).toVar();
|
|
227
|
+
const hitTriIdx = readHitTriangleIndex( hitBufferRW, rayID ).toVar();
|
|
228
|
+
const hitInstance = readHitInstanceLeaf( hitBufferRW, rayID ).toVar();
|
|
226
229
|
|
|
227
230
|
// per-ray camera-bounce depth — advances ONLY on opaque scatter (free bounces don't); drives termination (maxBounces). Megakernel: effectiveBounces.
|
|
228
231
|
const cameraDepth = readPathBounces( rayBufferRW, rayID ).toVar();
|
|
@@ -287,6 +290,7 @@ export function buildShadeKernel( params ) {
|
|
|
287
290
|
enableEnvironmentLight,
|
|
288
291
|
tslBool( true ), // wantUnoccluded
|
|
289
292
|
vec3( 0.0 ), planeN, float( 0.0 ),
|
|
293
|
+
hasSun, sunDirection, sunRadiance, sunParams,
|
|
290
294
|
) ).toVar();
|
|
291
295
|
|
|
292
296
|
const lumShad = max( dot( dual.shadowed, REC709_LUMINANCE_COEFFICIENTS ), float( 0.0 ) );
|
|
@@ -357,7 +361,7 @@ export function buildShadeKernel( params ) {
|
|
|
357
361
|
writeRayRadiance( rayBufferRW, rayID, vec4( outRgb, outAlpha ) );
|
|
358
362
|
// DDFA: the plane's aux (written above) is a committed surface — lock it (cosmetic; ray dies here).
|
|
359
363
|
writeRayDirFlags( rayBufferRW, rayID, direction, flags.bitOr( uint( RAY_FLAG.AUX_LOCKED ) ).bitAnd( uint( ~ RAY_FLAG.ACTIVE ) ) );
|
|
360
|
-
|
|
364
|
+
writeRngState( hitBufferRW, rayID, rngState );
|
|
361
365
|
Return();
|
|
362
366
|
|
|
363
367
|
} );
|
|
@@ -469,6 +473,53 @@ export function buildShadeKernel( params ) {
|
|
|
469
473
|
currentRadiance.w
|
|
470
474
|
) );
|
|
471
475
|
|
|
476
|
+
// The sun disc; on a redirected ray, the partner of the sending vertex's sun NEE. A blurred
|
|
477
|
+
// backdrop gets a smooth falloff: a hard-edged cone reads as a white coin.
|
|
478
|
+
If( hasSun, () => {
|
|
479
|
+
|
|
480
|
+
const sunL = vec3( 0.0 ).toVar();
|
|
481
|
+
If( isBackdropView.and( backgroundBlurriness.greaterThan( 0.0 ) ), () => {
|
|
482
|
+
|
|
483
|
+
const blurAngle = backgroundBlurriness.mul( 1.3 ).toVar();
|
|
484
|
+
const angle = acos( clamp( dot( normalize( envDir ), sunDirection ), - 1.0, 1.0 ) );
|
|
485
|
+
If( angle.lessThan( blurAngle ), () => {
|
|
486
|
+
|
|
487
|
+
const falloff = float( 1.0 ).sub( angle.div( blurAngle ).pow( 2 ) ).pow( 2 );
|
|
488
|
+
// ∫ falloff dΩ = π θb² / 3 for a small cone.
|
|
489
|
+
sunL.assign( sunRadiance.mul( environmentIntensity ).mul( sunParams.y ).mul( falloff ).mul( 3 / Math.PI ).div( blurAngle.mul( blurAngle ) ) );
|
|
490
|
+
|
|
491
|
+
} );
|
|
492
|
+
|
|
493
|
+
} ).Else( () => {
|
|
494
|
+
|
|
495
|
+
sunL.assign( sunRadianceToward( normalize( envDir ), sunDirection, sunRadiance, sunParams ).mul( environmentIntensity ) );
|
|
496
|
+
|
|
497
|
+
} );
|
|
498
|
+
If( sunL.x.add( sunL.y ).add( sunL.z ).greaterThan( 0.0 ), () => {
|
|
499
|
+
|
|
500
|
+
const sunW = float( 1.0 ).toVar();
|
|
501
|
+
If( isBackdropView.not().and( flags.bitAnd( uint( RAY_FLAG.SUN_NEE ) ).notEqual( uint( 0 ) ) ), () => {
|
|
502
|
+
|
|
503
|
+
sunW.assign( powerHeuristic( { pdf1: readRayPdf( rayBufferRW, rayID ), pdf2: float( 1.0 ).div( sunParams.y ) } ) );
|
|
504
|
+
|
|
505
|
+
} );
|
|
506
|
+
const vertex = max( bounceIndex.sub( 1 ), int( 0 ) );
|
|
507
|
+
const sunScale = select( isBackdropView, backgroundIntensity,
|
|
508
|
+
sunW.mul( select( vertex.greaterThan( int( 0 ) ), globalIlluminationIntensity, float( 1.0 ) ) ) );
|
|
509
|
+
currentRadiance.assign( vec4(
|
|
510
|
+
currentRadiance.xyz.add(
|
|
511
|
+
regularizePathContribution(
|
|
512
|
+
throughput.mul( sunL ).mul( sunScale ),
|
|
513
|
+
select( isBackdropView, float( 0.0 ), float( vertex ) ), fireflyThreshold, int( accumFrame ),
|
|
514
|
+
),
|
|
515
|
+
),
|
|
516
|
+
currentRadiance.w
|
|
517
|
+
) );
|
|
518
|
+
|
|
519
|
+
} );
|
|
520
|
+
|
|
521
|
+
} );
|
|
522
|
+
|
|
472
523
|
} );
|
|
473
524
|
|
|
474
525
|
// Solid-color backdrop ('color' mode): a primary ray that doesn't show the env image and isn't
|
|
@@ -577,7 +628,7 @@ export function buildShadeKernel( params ) {
|
|
|
577
628
|
|
|
578
629
|
writeRayRadiance( rayBufferRW, rayID, currentRadiance );
|
|
579
630
|
writeRayDirFlags( rayBufferRW, rayID, direction, flags.bitAnd( uint( ~ RAY_FLAG.ACTIVE ) ) );
|
|
580
|
-
|
|
631
|
+
writeRngState( hitBufferRW, rayID, rngState );
|
|
581
632
|
Return();
|
|
582
633
|
|
|
583
634
|
} );
|
|
@@ -594,7 +645,7 @@ export function buildShadeKernel( params ) {
|
|
|
594
645
|
// fires the next hit's env/emissive MIS, down-weighting SSS-then-env/emitter views.
|
|
595
646
|
writeRayThroughputPdf( rayBufferRW, rayID, throughput, readRayPdf( rayBufferRW, rayID ) );
|
|
596
647
|
writeRayRadiance( rayBufferRW, rayID, currentRadiance );
|
|
597
|
-
|
|
648
|
+
writeRngState( hitBufferRW, rayID, rngState );
|
|
598
649
|
Return();
|
|
599
650
|
|
|
600
651
|
} );
|
|
@@ -735,7 +786,7 @@ export function buildShadeKernel( params ) {
|
|
|
735
786
|
|
|
736
787
|
writeRayRadiance( rayBufferRW, rayID, currentRadiance );
|
|
737
788
|
writeRayDirFlags( rayBufferRW, rayID, direction, flags.bitAnd( uint( ~ RAY_FLAG.ACTIVE ) ) );
|
|
738
|
-
|
|
789
|
+
writeRngState( hitBufferRW, rayID, rngState );
|
|
739
790
|
|
|
740
791
|
};
|
|
741
792
|
|
|
@@ -935,7 +986,7 @@ export function buildShadeKernel( params ) {
|
|
|
935
986
|
throughput.mulAssign( interaction.throughput );
|
|
936
987
|
|
|
937
988
|
// Off the side of the facet the new ray leaves on: reflection stays, transmission and alpha skip cross.
|
|
938
|
-
const Ng = unpackHitFacet( readHitFacet(
|
|
989
|
+
const Ng = unpackHitFacet( readHitFacet( hitBufferRW, rayID ) ).faceN;
|
|
939
990
|
const newOrigin = offsetRayOrigin( hitPoint, select( dot( Ng, interaction.direction ).lessThan( 0.0 ), Ng.negate(), Ng ) );
|
|
940
991
|
|
|
941
992
|
// SSS = free bounce (depth unchanged); transmission advances camera-bounce depth.
|
|
@@ -979,7 +1030,7 @@ export function buildShadeKernel( params ) {
|
|
|
979
1030
|
writeFeatureThroughput( rayBufferRW, rayID, featCarry );
|
|
980
1031
|
|
|
981
1032
|
} );
|
|
982
|
-
|
|
1033
|
+
writeRngState( hitBufferRW, rayID, rngState );
|
|
983
1034
|
Return();
|
|
984
1035
|
|
|
985
1036
|
} );
|
|
@@ -1066,7 +1117,7 @@ export function buildShadeKernel( params ) {
|
|
|
1066
1117
|
const Ngeo = normalize( hitNormal );
|
|
1067
1118
|
const NgeoFF = select( dot( Ngeo, V ).lessThan( 0.0 ), Ngeo.negate(), Ngeo ).toVar();
|
|
1068
1119
|
// The facet on the viewer's side: every ray spawned from here is offset off it (HitFacet.js).
|
|
1069
|
-
const facet = unpackHitFacet( readHitFacet(
|
|
1120
|
+
const facet = unpackHitFacet( readHitFacet( hitBufferRW, rayID ) );
|
|
1070
1121
|
const facetN = facet.faceN.toVar();
|
|
1071
1122
|
const terminatorLift = Ngeo.mul( facet.liftScale );
|
|
1072
1123
|
const lightShadowOrigin = L => shadowTerminatorOrigin( {
|
|
@@ -1188,6 +1239,7 @@ export function buildShadeKernel( params ) {
|
|
|
1188
1239
|
enableEnvironmentLight,
|
|
1189
1240
|
tslBool( false ), // wantUnoccluded: false on real surfaces — dead-codes the unoccluded sum
|
|
1190
1241
|
terminatorLift, facetN, shadowTerminatorOffset,
|
|
1242
|
+
hasSun, sunDirection, sunRadiance, sunParams,
|
|
1191
1243
|
) ).shadowed.toVar();
|
|
1192
1244
|
|
|
1193
1245
|
const giScale = select( bounceIndex.greaterThan( 0 ), globalIlluminationIntensity, float( 1.0 ) );
|
|
@@ -1367,6 +1419,10 @@ export function buildShadeKernel( params ) {
|
|
|
1367
1419
|
|
|
1368
1420
|
const newOrigin = offsetRayOrigin( hitPoint, select( dot( facetN, bounceDir ).lessThan( 0.0 ), facetN.negate(), facetN ) );
|
|
1369
1421
|
|
|
1422
|
+
// Whether this vertex's sun NEE could have drawn bounceDir, so a sun hit at the miss knows its MIS partner.
|
|
1423
|
+
const sunNEE = brdfIsTransmission.not().and( dot( N, bounceDir ).greaterThan( 0.0 ) );
|
|
1424
|
+
flags.assign( flags.sub( flags.bitAnd( uint( RAY_FLAG.SUN_NEE ) ) ).bitOr( select( sunNEE, uint( RAY_FLAG.SUN_NEE ), uint( 0 ) ) ) );
|
|
1425
|
+
|
|
1370
1426
|
// Opaque scatter: the only bounce that advances camera depth.
|
|
1371
1427
|
writeRayOriginMeta( rayBufferRW, rayID, newOrigin, cameraDepth.add( 1 ), sssSteps, transparentCount );
|
|
1372
1428
|
writeRayDirFlags( rayBufferRW, rayID, bounceDir, flags );
|
|
@@ -1379,7 +1435,7 @@ export function buildShadeKernel( params ) {
|
|
|
1379
1435
|
writeFeatureThroughput( rayBufferRW, rayID, featCarry );
|
|
1380
1436
|
|
|
1381
1437
|
} );
|
|
1382
|
-
|
|
1438
|
+
writeRngState( hitBufferRW, rayID, rngState );
|
|
1383
1439
|
|
|
1384
1440
|
} );
|
|
1385
1441
|
|
package/src/TSL/Sun.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Fn, float, vec3, sqrt, max, dot, cross, pow, cos, sin, abs, normalize, select, If } from 'three/tsl';
|
|
2
|
+
|
|
3
|
+
import { LIMB_DARKENING_EXPONENT } from '../Processor/AtmosphereModel.js';
|
|
4
|
+
|
|
5
|
+
const LIMB_EXPONENT = vec3( ...LIMB_DARKENING_EXPONENT );
|
|
6
|
+
// µ^α averages to 2 / (α + 2) over the disc, so this keeps the mean at `sunRadiance`.
|
|
7
|
+
const LIMB_NORM = vec3( ...LIMB_DARKENING_EXPONENT.map( a => ( a + 2 ) / 2 ) );
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Radiance the physical sky's sun sends along `dir`: limb darkened, and nothing below the sky's
|
|
11
|
+
* horizon. `sunParams` = ( cos half-angle, solid angle, 1 / sin² half-angle, horizon dip sin ).
|
|
12
|
+
*/
|
|
13
|
+
export const sunRadianceToward = /*@__PURE__*/ Fn( ( [ dir, sunDirection, sunRadiance, sunParams ] ) => {
|
|
14
|
+
|
|
15
|
+
const out = vec3( 0 ).toVar();
|
|
16
|
+
If( dot( dir, sunDirection ).greaterThanEqual( sunParams.x ).and( dir.y.greaterThanEqual( sunParams.w.negate() ) ), () => {
|
|
17
|
+
|
|
18
|
+
const c = cross( dir, sunDirection );
|
|
19
|
+
const mu = sqrt( max( float( 1 ).sub( dot( c, c ).mul( sunParams.z ) ), 1e-8 ) );
|
|
20
|
+
out.assign( sunRadiance.mul( pow( vec3( mu ), LIMB_EXPONENT ) ).mul( LIMB_NORM ) );
|
|
21
|
+
|
|
22
|
+
} );
|
|
23
|
+
return out;
|
|
24
|
+
|
|
25
|
+
} );
|
|
26
|
+
|
|
27
|
+
/** A direction uniform in solid angle over the sun disc. */
|
|
28
|
+
export const sampleSunDisc = /*@__PURE__*/ Fn( ( [ sunDirection, sunParams, xi ] ) => {
|
|
29
|
+
|
|
30
|
+
// 1 − cosθ straight from the solid angle: 1 − cos(half-angle) cancels to nothing in f32.
|
|
31
|
+
const oneMinusCos = xi.x.mul( sunParams.y.mul( 1 / ( 2 * Math.PI ) ) );
|
|
32
|
+
const cosT = float( 1 ).sub( oneMinusCos );
|
|
33
|
+
const sinT = sqrt( max( oneMinusCos.mul( float( 2 ).sub( oneMinusCos ) ), 0 ) );
|
|
34
|
+
const phi = xi.y.mul( 2 * Math.PI );
|
|
35
|
+
const w = sunDirection;
|
|
36
|
+
const u = normalize( cross( select( abs( w.x ).greaterThan( 0.9 ), vec3( 0, 1, 0 ), vec3( 1, 0, 0 ) ), w ) );
|
|
37
|
+
const v = cross( w, u );
|
|
38
|
+
return normalize( w.mul( cosT ).add( u.mul( cos( phi ) ).add( v.mul( sin( phi ) ) ).mul( sinT ) ) );
|
|
39
|
+
|
|
40
|
+
} );
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
MaterialSamples,
|
|
7
7
|
ExtMapResult,
|
|
8
8
|
} from './Struct.js';
|
|
9
|
-
import {
|
|
9
|
+
import { instanceRows, instanceDirToWorld, triangleRow } from './Common.js';
|
|
10
10
|
import { TEXTURE_CONSTANTS } from '../EngineDefaults.js';
|
|
11
11
|
|
|
12
12
|
// ================================================================================
|
|
@@ -414,13 +414,12 @@ const uvTransformJacobian = /*@__PURE__*/ wgslFn( `
|
|
|
414
414
|
*/
|
|
415
415
|
export const triangleUVTangent = Fn( ( [ triangleBuffer, triIndex, geometryNormal, transform, bvhBuffer, instanceLeaf ] ) => {
|
|
416
416
|
|
|
417
|
-
const
|
|
418
|
-
const
|
|
419
|
-
const
|
|
420
|
-
const e2 = uintBitsToFloat( getDatafromStorageBuffer( triangleBuffer, triIndex, int( 2 ), S ).xyz ).sub( pA );
|
|
417
|
+
const pA = uintBitsToFloat( triangleRow( triangleBuffer, triIndex, 0 ).xyz );
|
|
418
|
+
const e1 = uintBitsToFloat( triangleRow( triangleBuffer, triIndex, 1 ).xyz ).sub( pA );
|
|
419
|
+
const e2 = uintBitsToFloat( triangleRow( triangleBuffer, triIndex, 2 ).xyz ).sub( pA );
|
|
421
420
|
|
|
422
|
-
const uvAB = uintBitsToFloat(
|
|
423
|
-
const uvC = uintBitsToFloat(
|
|
421
|
+
const uvAB = uintBitsToFloat( triangleRow( triangleBuffer, triIndex, 3 ) );
|
|
422
|
+
const uvC = uintBitsToFloat( triangleRow( triangleBuffer, triIndex, 4 ).xy );
|
|
424
423
|
const d1 = uvAB.zw.sub( uvAB.xy );
|
|
425
424
|
const d2 = uvC.sub( uvAB.xy );
|
|
426
425
|
|
package/src/index.js
CHANGED
|
@@ -26,6 +26,22 @@ export { SETTING_SOURCE } from './RenderSettings.js';
|
|
|
26
26
|
// Asset URL / cache namespace overrides (call before constructing PathTracerApp)
|
|
27
27
|
export { configureAssets, getAssetConfig } from './AssetConfig.js';
|
|
28
28
|
|
|
29
|
+
// On-disk storage (origin private file system): caches, and areas a host defines for its own data
|
|
30
|
+
export {
|
|
31
|
+
openStorage, StorageManager, STORAGE_KIND, ENGINE_AREAS, fileIdentity, identityKey, sameIdentity,
|
|
32
|
+
DownloadCache, DOWNLOAD_POLICY, fetchFile, nameFromUrl, cachedObjectURL, acquireLock, heldLockNames,
|
|
33
|
+
} from './Storage/index.js';
|
|
34
|
+
|
|
35
|
+
// Archives read in place: one entry at a time, never the whole file in memory
|
|
36
|
+
export { openZip, readZipDirectory } from './Processor/ZipReader.js';
|
|
37
|
+
|
|
38
|
+
// Engine package version, as built
|
|
39
|
+
export { VERSION } from './version.js';
|
|
40
|
+
|
|
41
|
+
// A loaded scene's edits as plain data (app.exportSceneState / importSceneState)
|
|
42
|
+
export { SCENE_STATE_VERSION } from './SceneState/SceneState.js';
|
|
43
|
+
export { toPortable, fromPortable } from './SceneState/portable.js';
|
|
44
|
+
|
|
29
45
|
// glTF alphaMode derivation — the one definition, shared with hosts that edit materials
|
|
30
46
|
export { deriveAlphaMode, MATERIAL_VALUE_SOURCE } from './Processor/GeometryExtractor.js';
|
|
31
47
|
|
|
@@ -47,6 +63,7 @@ export {
|
|
|
47
63
|
CAMERA_PRESETS,
|
|
48
64
|
CAMERA_RANGES,
|
|
49
65
|
SKY_PRESETS,
|
|
66
|
+
DEFAULT_SUN_PATH,
|
|
50
67
|
AUTO_FOCUS_MODES,
|
|
51
68
|
AF_DEFAULTS,
|
|
52
69
|
TRIANGLE_DATA_LAYOUT,
|
|
@@ -61,6 +78,7 @@ export {
|
|
|
61
78
|
RENDER_PROFILES,
|
|
62
79
|
getRenderProfile,
|
|
63
80
|
} from './EngineDefaults.js';
|
|
81
|
+
export { sunPosition, timeForSunElevation, dayOfYearForMonth } from './Processor/SunPosition.js';
|
|
64
82
|
|
|
65
83
|
// Settings & managers (for advanced consumers)
|
|
66
84
|
export { RenderSettings } from './RenderSettings.js';
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { EventDispatcher, MathUtils, OrthographicCamera, PerspectiveCamera, Vector3 } from 'three';
|
|
1
|
+
import { EventDispatcher, MathUtils, OrthographicCamera, PerspectiveCamera, Quaternion, Vector3 } from 'three';
|
|
2
2
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
3
3
|
import { EngineEvents, } from '../EngineEvents.js';
|
|
4
4
|
import { AF_DEFAULTS, 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
|
+
import { toPortable, fromPortable } from '../SceneState/portable.js';
|
|
8
9
|
|
|
9
10
|
const DEFAULT_CAMERA_SCALE = Object.freeze( new Vector3( 1, 1, 1 ) );
|
|
10
11
|
|
|
@@ -557,6 +558,173 @@ export class CameraManager extends EventDispatcher {
|
|
|
557
558
|
|
|
558
559
|
}
|
|
559
560
|
|
|
561
|
+
// ── Saved sessions ────────────────────────────────────────────
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* The live view, every camera's own effects, the cameras the user added and which one is
|
|
565
|
+
* active, as plain data. The active camera's effects are the render settings themselves.
|
|
566
|
+
* @returns {Object}
|
|
567
|
+
*/
|
|
568
|
+
serialize() {
|
|
569
|
+
|
|
570
|
+
const camera = this.camera;
|
|
571
|
+
const saved = this._defaultCameraState;
|
|
572
|
+
|
|
573
|
+
return {
|
|
574
|
+
current: this.currentCameraIndex,
|
|
575
|
+
view: {
|
|
576
|
+
position: camera.position.toArray(),
|
|
577
|
+
quaternion: camera.quaternion.toArray(),
|
|
578
|
+
scale: camera.scale.toArray(),
|
|
579
|
+
fov: camera.fov,
|
|
580
|
+
near: camera.near,
|
|
581
|
+
far: camera.far,
|
|
582
|
+
zoom: camera.zoom,
|
|
583
|
+
orthographic: !! camera.orthographic,
|
|
584
|
+
orthoHalfHeight: camera.orthoHalfHeight,
|
|
585
|
+
target: this.controls.target.toArray(),
|
|
586
|
+
},
|
|
587
|
+
defaultView: saved ? {
|
|
588
|
+
position: saved.position.toArray(),
|
|
589
|
+
quaternion: saved.quaternion.toArray(),
|
|
590
|
+
scale: saved.scale.toArray(),
|
|
591
|
+
fov: saved.fov,
|
|
592
|
+
near: saved.near,
|
|
593
|
+
far: saved.far,
|
|
594
|
+
target: saved.target?.toArray() ?? null,
|
|
595
|
+
} : null,
|
|
596
|
+
navigationMode: this.navigationMode,
|
|
597
|
+
autoFocus: { mode: this.autoFocusMode, point: { ...this.afScreenPoint } },
|
|
598
|
+
cameras: this.cameras.map( ( cam, index ) => {
|
|
599
|
+
|
|
600
|
+
const entry = { index, name: cam.name ?? '', effects: toPortable( cam.userData?.__rayzeeEffects ) ?? null };
|
|
601
|
+
if ( index === 0 || ! cam.userData?.__rayzeeUserCamera ) return entry;
|
|
602
|
+
|
|
603
|
+
return {
|
|
604
|
+
...entry,
|
|
605
|
+
user: true,
|
|
606
|
+
orthographic: !! cam.isOrthographicCamera,
|
|
607
|
+
frustum: cam.isOrthographicCamera ? [ cam.left, cam.right, cam.top, cam.bottom ] : null,
|
|
608
|
+
fov: cam.fov ?? null,
|
|
609
|
+
aspect: cam.aspect ?? null,
|
|
610
|
+
near: cam.near,
|
|
611
|
+
far: cam.far,
|
|
612
|
+
zoom: cam.zoom,
|
|
613
|
+
position: cam.position.toArray(),
|
|
614
|
+
quaternion: cam.quaternion.toArray(),
|
|
615
|
+
scale: cam.scale.toArray(),
|
|
616
|
+
orbitTarget: cam.userData.__rayzeeOrbitTarget?.toArray() ?? null,
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
} ),
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Puts back what {@link serialize} recorded, against the cameras this load produced. A model
|
|
626
|
+
* camera is matched by index and name; one that no longer matches keeps its own effects.
|
|
627
|
+
* Restore render settings first: this sets the exact view after any projection change they made.
|
|
628
|
+
* @param {Object} state
|
|
629
|
+
* @returns {{mismatched: string[]}} model cameras whose saved effects were not applied
|
|
630
|
+
*/
|
|
631
|
+
restore( state ) {
|
|
632
|
+
|
|
633
|
+
const mismatched = [];
|
|
634
|
+
if ( ! state ) return { mismatched };
|
|
635
|
+
|
|
636
|
+
this.cameras = this.cameras.filter( cam => ! cam.userData?.__rayzeeUserCamera );
|
|
637
|
+
const indexMap = new Map();
|
|
638
|
+
|
|
639
|
+
for ( const entry of state.cameras ?? [] ) {
|
|
640
|
+
|
|
641
|
+
if ( entry.user ) {
|
|
642
|
+
|
|
643
|
+
const cam = entry.orthographic
|
|
644
|
+
? new OrthographicCamera( ...entry.frustum, entry.near, entry.far )
|
|
645
|
+
: new PerspectiveCamera( entry.fov, entry.aspect, entry.near, entry.far );
|
|
646
|
+
cam.name = entry.name;
|
|
647
|
+
cam.zoom = entry.zoom ?? 1;
|
|
648
|
+
cam.position.fromArray( entry.position );
|
|
649
|
+
cam.quaternion.fromArray( entry.quaternion );
|
|
650
|
+
cam.scale.fromArray( entry.scale );
|
|
651
|
+
cam.updateProjectionMatrix();
|
|
652
|
+
cam.updateMatrixWorld( true );
|
|
653
|
+
cam.userData.__rayzeeUserCamera = true;
|
|
654
|
+
cam.userData.__rayzeeEffects = fromPortable( entry.effects );
|
|
655
|
+
if ( entry.orbitTarget ) cam.userData.__rayzeeOrbitTarget = new Vector3().fromArray( entry.orbitTarget );
|
|
656
|
+
indexMap.set( entry.index, this.cameras.length );
|
|
657
|
+
this.cameras.push( cam );
|
|
658
|
+
continue;
|
|
659
|
+
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const cam = this.cameras[ entry.index ];
|
|
663
|
+
if ( ! cam || ( entry.index > 0 && ( cam.name ?? '' ) !== entry.name ) ) {
|
|
664
|
+
|
|
665
|
+
mismatched.push( entry.name );
|
|
666
|
+
continue;
|
|
667
|
+
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
indexMap.set( entry.index, entry.index );
|
|
671
|
+
if ( entry.effects ) cam.userData.__rayzeeEffects = fromPortable( entry.effects );
|
|
672
|
+
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
this._userCameraCounter = this.cameras.filter( cam => cam.userData?.__rayzeeUserCamera ).length;
|
|
676
|
+
this.currentCameraIndex = indexMap.get( state.current ) ?? 0;
|
|
677
|
+
|
|
678
|
+
const d = state.defaultView;
|
|
679
|
+
this._defaultCameraState = d ? {
|
|
680
|
+
position: new Vector3().fromArray( d.position ),
|
|
681
|
+
quaternion: new Quaternion().fromArray( d.quaternion ),
|
|
682
|
+
scale: new Vector3().fromArray( d.scale ),
|
|
683
|
+
fov: d.fov,
|
|
684
|
+
near: d.near,
|
|
685
|
+
far: d.far,
|
|
686
|
+
target: d.target ? new Vector3().fromArray( d.target ) : null,
|
|
687
|
+
} : null;
|
|
688
|
+
|
|
689
|
+
if ( state.navigationMode ) this.setNavigationMode( state.navigationMode );
|
|
690
|
+
|
|
691
|
+
const v = state.view;
|
|
692
|
+
if ( v ) {
|
|
693
|
+
|
|
694
|
+
const camera = this.camera;
|
|
695
|
+
camera.position.fromArray( v.position );
|
|
696
|
+
camera.quaternion.fromArray( v.quaternion );
|
|
697
|
+
camera.scale.fromArray( v.scale );
|
|
698
|
+
camera.fov = v.fov;
|
|
699
|
+
camera.near = v.near;
|
|
700
|
+
camera.far = v.far;
|
|
701
|
+
this._setView( v.orthographic, v.orthoHalfHeight );
|
|
702
|
+
camera.zoom = v.zoom ?? 1;
|
|
703
|
+
camera.updateProjectionMatrix();
|
|
704
|
+
camera.updateMatrixWorld( true );
|
|
705
|
+
this.controls.target.fromArray( v.target );
|
|
706
|
+
this.controls.update();
|
|
707
|
+
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if ( state.autoFocus ) {
|
|
711
|
+
|
|
712
|
+
this.setAutoFocusMode( state.autoFocus.mode );
|
|
713
|
+
this.setAFScreenPoint( state.autoFocus.point.x, state.autoFocus.point.y );
|
|
714
|
+
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
this.resetAutoFocus();
|
|
718
|
+
this._reportOrthoHeight();
|
|
719
|
+
this.dispatchEvent( {
|
|
720
|
+
type: 'CameraSwitched', cameraIndex: this.currentCameraIndex, effects: this._captureEffects(), fov: this.camera.fov,
|
|
721
|
+
cameraProjection: this._getSettings?.( 'cameraProjection' ),
|
|
722
|
+
} );
|
|
723
|
+
|
|
724
|
+
return { mismatched };
|
|
725
|
+
|
|
726
|
+
}
|
|
727
|
+
|
|
560
728
|
// ── Aliases (match Sub-API surface) ───────────────────────────
|
|
561
729
|
|
|
562
730
|
/** The active camera, a {@link ViewCamera}. */
|