rayzee 8.8.2 → 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 +210 -20
- package/dist/{NeuralRetouch-CYOJ21Wn.js → NeuralRetouch-BRquqfmZ.js} +1 -1
- package/dist/{NeuralSuperRes-Dc6jRYke.js → NeuralSuperRes-CbNvQjCw.js} +5 -2
- package/dist/{ToneMapGPU-BVE852iN.js → ToneMapGPU-DpUZDMbQ.js} +2 -1
- package/dist/rayzee.es.js +10795 -5324
- package/dist/rayzee.umd.js +279 -268
- package/package.json +1 -1
- package/src/AssetConfig.js +4 -0
- package/src/EngineDefaults.js +60 -76
- package/src/EngineEvents.js +7 -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/Passes/OIDNTemporalHistory.js +25 -7
- package/src/PathTracerApp.js +313 -25
- package/src/Processor/ArchiveCache.js +120 -0
- package/src/Processor/ArchiveReader.js +179 -32
- package/src/Processor/AssetLoader.js +523 -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 +210 -55
- 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 +49 -8
- package/src/SceneState/SceneState.js +367 -0
- package/src/SceneState/portable.js +52 -0
- package/src/Stages/MotionVector.js +8 -42
- package/src/Stages/NRD.js +30 -21
- package/src/Stages/NormalDepth.js +12 -23
- package/src/Stages/PathTracer.js +114 -6
- 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/CameraRay.js +99 -14
- package/src/TSL/Common.js +11 -2
- package/src/TSL/DebugKernel.js +2 -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 +5 -5
- 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 +22 -0
- package/src/managers/CameraManager.js +412 -56
- package/src/managers/DenoisingManager.js +4 -3
- package/src/managers/EnvironmentManager.js +263 -33
- package/src/managers/GoboManager.js +19 -8
- package/src/managers/IESManager.js +3 -3
- package/src/managers/InteractionManager.js +30 -2
- 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 +11 -7
- package/src/managers/VideoRenderManager.js +38 -17
- package/src/managers/ViewCamera.js +77 -0
- package/src/managers/WalkControls.js +262 -0
- package/src/managers/helpers/OutlineHelper.js +22 -2
- package/src/managers/timeline/CameraTrack.js +235 -0
- package/src/managers/timeline/TimelineManager.js +146 -0
- package/src/version.js +3 -0
- package/src/Processor/ProceduralSky.js +0 -291
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Color, Quaternion, Vector2, Vector3, Vector4 } from 'three';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* JSON-safe copies of setting and material values: colours, vectors and non-finite numbers are
|
|
5
|
+
* tagged so {@link fromPortable} gives back the same types. Anything else that is not plain data
|
|
6
|
+
* comes back undefined, so a caller can leave it out.
|
|
7
|
+
*/
|
|
8
|
+
export function toPortable( value ) {
|
|
9
|
+
|
|
10
|
+
if ( value === null || typeof value === 'boolean' || typeof value === 'string' ) return value;
|
|
11
|
+
if ( typeof value === 'number' ) return Number.isFinite( value ) ? value : { $num: String( value ) };
|
|
12
|
+
if ( typeof value !== 'object' ) return undefined;
|
|
13
|
+
if ( value.isColor ) return { $color: [ value.r, value.g, value.b ] };
|
|
14
|
+
if ( value.isQuaternion ) return { $quat: [ value.x, value.y, value.z, value.w ] };
|
|
15
|
+
if ( value.isVector2 || value.isVector3 || value.isVector4 ) return { $vec: value.toArray() };
|
|
16
|
+
if ( ArrayBuffer.isView( value ) ) return Array.from( value );
|
|
17
|
+
if ( Array.isArray( value ) ) return value.map( v => toPortable( v ) ?? null );
|
|
18
|
+
|
|
19
|
+
const proto = Object.getPrototypeOf( value );
|
|
20
|
+
if ( proto !== Object.prototype && proto !== null ) return undefined;
|
|
21
|
+
|
|
22
|
+
const out = {};
|
|
23
|
+
for ( const [ key, v ] of Object.entries( value ) ) {
|
|
24
|
+
|
|
25
|
+
const p = toPortable( v );
|
|
26
|
+
if ( p !== undefined ) out[ key ] = p;
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return out;
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function fromPortable( value ) {
|
|
35
|
+
|
|
36
|
+
if ( value === null || typeof value !== 'object' ) return value;
|
|
37
|
+
if ( Array.isArray( value ) ) return value.map( fromPortable );
|
|
38
|
+
if ( '$num' in value ) return Number( value.$num );
|
|
39
|
+
if ( value.$color ) return new Color( ...value.$color );
|
|
40
|
+
if ( value.$quat ) return new Quaternion( ...value.$quat );
|
|
41
|
+
if ( value.$vec ) {
|
|
42
|
+
|
|
43
|
+
const Vector = [ null, null, Vector2, Vector3, Vector4 ][ value.$vec.length ];
|
|
44
|
+
return new Vector( ...value.$vec );
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const out = {};
|
|
49
|
+
for ( const [ key, v ] of Object.entries( value ) ) out[ key ] = fromPortable( v );
|
|
50
|
+
return out;
|
|
51
|
+
|
|
52
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { Fn, vec2, vec3, vec4, float, int, uint, ivec2, uvec2, uniform, If,
|
|
1
|
+
import { Fn, vec2, vec3, vec4, float, int, uint, ivec2, uvec2, uniform, If,
|
|
2
2
|
textureLoad, textureStore, workgroupId, localId } from 'three/tsl';
|
|
3
3
|
import { RenderTarget, TextureNode, StorageTexture } from 'three/webgpu';
|
|
4
4
|
import { HalfFloatType, RGBAFormat, NearestFilter, Matrix4, Box2, Vector2 } from 'three';
|
|
5
5
|
import { RenderStage, StageExecutionMode } from '../Pipeline/RenderStage.js';
|
|
6
6
|
import { MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
|
|
7
|
+
import { cameraRayPoint, cameraRayUniforms } from '../TSL/CameraRay.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* WebGPU Motion Vector Stage (Compute Shader)
|
|
@@ -20,8 +21,8 @@ import { MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
|
|
|
20
21
|
*
|
|
21
22
|
* Algorithm:
|
|
22
23
|
* 1. Read normalDepth from NormalDepth (linear depth in alpha)
|
|
23
|
-
* 2. Reconstruct camera ray
|
|
24
|
-
* 3. World position =
|
|
24
|
+
* 2. Reconstruct the camera ray NormalDepth traced for the pixel
|
|
25
|
+
* 3. World position = rayOrigin + rayDir * linearDepth
|
|
25
26
|
* 4. Project to previous frame: prevVP * worldPos
|
|
26
27
|
* 5. Motion = currentUV - prevUV
|
|
27
28
|
*
|
|
@@ -78,6 +79,7 @@ export class MotionVector extends RenderStage {
|
|
|
78
79
|
// Synced from PathTracer each frame (same source as NormalDepth)
|
|
79
80
|
this.cameraWorldMatrix = uniform( new Matrix4(), 'mat4' );
|
|
80
81
|
this.cameraProjectionMatrixInverse = uniform( new Matrix4(), 'mat4' );
|
|
82
|
+
this.cameraRay = cameraRayUniforms();
|
|
81
83
|
this.prevVP = uniform( new Matrix4(), 'mat4' );
|
|
82
84
|
this.isFirstFrameU = uniform( 1.0 ); // 1.0 = true, 0.0 = false
|
|
83
85
|
this.deltaTime = uniform( 1.0 / 60.0 );
|
|
@@ -175,29 +177,7 @@ export class MotionVector extends RenderStage {
|
|
|
175
177
|
// Sky / background (depth >= 65504 sentinel) — no motion
|
|
176
178
|
If( linearDepth.lessThan( float( 6e4 ) ), () => {
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
// Negate Y to match PathTracer convention
|
|
180
|
-
const ndcX = float( gx ).add( 0.5 ).div( resW ).mul( 2.0 ).sub( 1.0 );
|
|
181
|
-
const ndcY = float( gy ).add( 0.5 ).div( resH ).mul( 2.0 ).sub( 1.0 ).negate();
|
|
182
|
-
const ndcPos = vec3( ndcX, ndcY, 1.0 );
|
|
183
|
-
|
|
184
|
-
// Camera-space ray direction via inverse projection
|
|
185
|
-
const rayDirCS = cpi.mul( vec4( ndcPos, 1.0 ) );
|
|
186
|
-
|
|
187
|
-
// Transform to world space (rotation only, via mat3 of world matrix)
|
|
188
|
-
const rayDirWorld = normalize(
|
|
189
|
-
mat3(
|
|
190
|
-
cwm[ 0 ].xyz,
|
|
191
|
-
cwm[ 1 ].xyz,
|
|
192
|
-
cwm[ 2 ].xyz
|
|
193
|
-
).mul( rayDirCS.xyz.div( rayDirCS.w ) )
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
// Camera position (translation column of world matrix)
|
|
197
|
-
const camPos = vec3( cwm[ 3 ] );
|
|
198
|
-
|
|
199
|
-
// World position = camera origin + ray direction * linear depth
|
|
200
|
-
const worldPos = camPos.add( rayDirWorld.mul( linearDepth ) );
|
|
180
|
+
const worldPos = cameraRayPoint( currentUV, linearDepth, cwm, cpi, this.cameraRay );
|
|
201
181
|
|
|
202
182
|
// Project to previous frame
|
|
203
183
|
const prevClip = prevVP.mul( vec4( worldPos, 1.0 ) );
|
|
@@ -278,27 +258,12 @@ export class MotionVector extends RenderStage {
|
|
|
278
258
|
// Skip first frame and sky (depth >= 65504 sentinel)
|
|
279
259
|
If( isFirstFrameU.lessThan( 0.5 ).and( linearDepth.lessThan( float( 6e4 ) ) ), () => {
|
|
280
260
|
|
|
281
|
-
// Pixel coordinate → NDC
|
|
282
|
-
// Negate Y to match PathTracer convention
|
|
283
|
-
const ndcX = float( gx ).add( 0.5 ).div( resW ).mul( 2.0 ).sub( 1.0 );
|
|
284
|
-
const ndcY = float( gy ).add( 0.5 ).div( resH ).mul( 2.0 ).sub( 1.0 ).negate();
|
|
285
|
-
const ndcPos = vec3( ndcX, ndcY, 1.0 );
|
|
286
|
-
const rayDirCS = cpi.mul( vec4( ndcPos, 1.0 ) );
|
|
287
|
-
const rayDirWorld = normalize(
|
|
288
|
-
mat3(
|
|
289
|
-
cwm[ 0 ].xyz,
|
|
290
|
-
cwm[ 1 ].xyz,
|
|
291
|
-
cwm[ 2 ].xyz
|
|
292
|
-
).mul( rayDirCS.xyz.div( rayDirCS.w ) )
|
|
293
|
-
);
|
|
294
|
-
const camPos = vec3( cwm[ 3 ] );
|
|
295
|
-
const worldPos = camPos.add( rayDirWorld.mul( linearDepth ) );
|
|
296
|
-
|
|
297
261
|
// Current pixel UV
|
|
298
262
|
const currentUV = vec2(
|
|
299
263
|
float( gx ).add( 0.5 ).div( resW ),
|
|
300
264
|
float( gy ).add( 0.5 ).div( resH )
|
|
301
265
|
);
|
|
266
|
+
const worldPos = cameraRayPoint( currentUV, linearDepth, cwm, cpi, this.cameraRay );
|
|
302
267
|
|
|
303
268
|
// Project to previous frame
|
|
304
269
|
const prevClip = prevVP.mul( vec4( worldPos, 1.0 ) );
|
|
@@ -370,6 +335,7 @@ export class MotionVector extends RenderStage {
|
|
|
370
335
|
if ( pt && pt.uniforms ) {
|
|
371
336
|
|
|
372
337
|
// Sync from PathTracer (same source as NormalDepth)
|
|
338
|
+
this.cameraRay.sync( pt.uniforms );
|
|
373
339
|
worldMatrix = pt.uniforms.get( 'cameraWorldMatrix' ).value;
|
|
374
340
|
viewMatrix = pt.uniforms.get( 'cameraViewMatrix' ).value;
|
|
375
341
|
projMatrix = pt.uniforms.get( 'cameraProjectionMatrix' ).value;
|
package/src/Stages/NRD.js
CHANGED
|
@@ -6,7 +6,7 @@ import { DataTexture, HalfFloatType, FloatType, RGBAFormat, LinearFilter, Matrix
|
|
|
6
6
|
import { RenderStage, StageExecutionMode } from '../Pipeline/RenderStage.js';
|
|
7
7
|
import { createStorageTexture } from '../Processor/StorageTexturePool.js';
|
|
8
8
|
import { sanitizeRGB, sanitize1, FP16_MAX } from '../TSL/Common.js';
|
|
9
|
-
import {
|
|
9
|
+
import { cameraRayPoint, cameraRayUniforms, isOrthographic } from '../TSL/CameraRay.js';
|
|
10
10
|
import {
|
|
11
11
|
ALBEDO_EPS, GBUFFER_MISS_THRESHOLD as MISS_THRESHOLD, NRD_DEFAULTS,
|
|
12
12
|
NRD_HIT_DIST_A, NRD_HIT_DIST_B,
|
|
@@ -168,10 +168,10 @@ export class NRD extends RenderStage {
|
|
|
168
168
|
this.prevCamView = uniform( new Matrix4(), 'mat4' );
|
|
169
169
|
this.camPos = uniform( new Vector3(), 'vec3' );
|
|
170
170
|
this.prevCamPos = uniform( new Vector3(), 'vec3' );
|
|
171
|
-
|
|
172
|
-
this.
|
|
173
|
-
this.
|
|
174
|
-
this.
|
|
171
|
+
// Surface to camera while orthographic, where it is the same for every pixel.
|
|
172
|
+
this.viewVector = uniform( new Vector3(), 'vec3' );
|
|
173
|
+
this.prevViewVector = uniform( new Vector3(), 'vec3' );
|
|
174
|
+
this.cameraRay = cameraRayUniforms();
|
|
175
175
|
|
|
176
176
|
// Declared here, valued by _syncUniforms below — which is the only place a setting becomes a
|
|
177
177
|
// uniform, so there is no second copy of the defaults to keep in step.
|
|
@@ -322,11 +322,20 @@ export class NRD extends RenderStage {
|
|
|
322
322
|
// Same ray the depth was measured along — NormalDepth generates it with this helper too.
|
|
323
323
|
_worldPosAt( uv, dist, cwm, cpi ) {
|
|
324
324
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
325
|
+
return cameraRayPoint( uv, dist, cwm, cpi, this.cameraRay );
|
|
326
|
+
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// NRD's gOrthoMode: a constant view vector, and a pixel's footprint that no longer grows with depth.
|
|
330
|
+
_viewVector( X, camPos, orthoV ) {
|
|
331
|
+
|
|
332
|
+
return select( isOrthographic( this.cameraRay.cameraProjection ), orthoV, normalize( camPos.sub( X ) ) );
|
|
333
|
+
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
_frustumSize( viewZ ) {
|
|
337
|
+
|
|
338
|
+
return this.minRectDimMulUnproject.mul( select( isOrthographic( this.cameraRay.cameraProjection ), float( 1.0 ), viewZ ) );
|
|
330
339
|
|
|
331
340
|
}
|
|
332
341
|
|
|
@@ -547,9 +556,9 @@ export class NRD extends RenderStage {
|
|
|
547
556
|
|
|
548
557
|
If( maxRadiusU.greaterThan( 0.0 ), () => {
|
|
549
558
|
|
|
550
|
-
const V =
|
|
559
|
+
const V = this._viewVector( X, camPos, this.viewVector );
|
|
551
560
|
const NoV = dot( N, V ).abs().toVar();
|
|
552
|
-
const frustumSize = this.
|
|
561
|
+
const frustumSize = this._frustumSize( viewZ ).toVar();
|
|
553
562
|
|
|
554
563
|
const k = inputFrames.add( 1.0 );
|
|
555
564
|
const accumSpeedEff = isPre
|
|
@@ -701,9 +710,9 @@ export class NRD extends RenderStage {
|
|
|
701
710
|
const pixelUv = this._pixelUv( gx, gy );
|
|
702
711
|
const X = this._worldPosAt( pixelUv, dist, cwm, cpi ).toVar();
|
|
703
712
|
const viewZ = camView.mul( vec4( X, 1.0 ) ).z.abs().toVar();
|
|
704
|
-
const V =
|
|
713
|
+
const V = this._viewVector( X, camPos, this.viewVector );
|
|
705
714
|
const NoV = dot( N, V ).abs().toVar();
|
|
706
|
-
const frustumSize = this.
|
|
715
|
+
const frustumSize = this._frustumSize( viewZ ).toVar();
|
|
707
716
|
|
|
708
717
|
// Unnormalized, as NRD requires — its length carries the normal variance. A miss neighbour's
|
|
709
718
|
// shadingNormal is a literal (0,0,0), which decodes to a fixed fake direction, so substitute
|
|
@@ -753,7 +762,7 @@ export class NRD extends RenderStage {
|
|
|
753
762
|
|
|
754
763
|
const accumSpeed = this._applyCustomWeights( prevAccum, occW, float( 0.0 ) ).toVar();
|
|
755
764
|
|
|
756
|
-
const Vprev =
|
|
765
|
+
const Vprev = this._viewVector( X, prevCamPos, this.prevViewVector );
|
|
757
766
|
const NoVprev = dot( N, Vprev ).abs();
|
|
758
767
|
const sizeQuality = NoVprev.add( 1e-3 ).div( NoV.add( 1e-3 ) ).toVar();
|
|
759
768
|
sizeQuality.assign( mix( float( 0.1 ), float( 1.0 ), saturate( sizeQuality.mul( sizeQuality ) ) ) );
|
|
@@ -853,7 +862,7 @@ export class NRD extends RenderStage {
|
|
|
853
862
|
const pixelUv = this._pixelUv( gx, gy );
|
|
854
863
|
const X = this._worldPosAt( pixelUv, dist, cwm, cpi ).toVar();
|
|
855
864
|
const viewZ = camView.mul( vec4( X, 1.0 ) ).z.abs();
|
|
856
|
-
const frustumSize = this.
|
|
865
|
+
const frustumSize = this._frustumSize( viewZ ).toVar();
|
|
857
866
|
|
|
858
867
|
const diff = textureLoad( inTex, coord ).toVar();
|
|
859
868
|
const nonLinear = float( 1.0 ).div( frameNumEff.add( 1.0 ) ).toVar();
|
|
@@ -1200,6 +1209,7 @@ export class NRD extends RenderStage {
|
|
|
1200
1209
|
|
|
1201
1210
|
this.prevCamView.value.copy( this.camView.value );
|
|
1202
1211
|
this.prevCamPos.value.copy( this.camPos.value );
|
|
1212
|
+
this.prevViewVector.value.copy( this.viewVector.value );
|
|
1203
1213
|
|
|
1204
1214
|
}
|
|
1205
1215
|
|
|
@@ -1207,20 +1217,19 @@ export class NRD extends RenderStage {
|
|
|
1207
1217
|
this.camProjInv.value.copy( projInv );
|
|
1208
1218
|
this.camView.value.copy( view );
|
|
1209
1219
|
this.camPos.value.setFromMatrixPosition( world );
|
|
1210
|
-
this.
|
|
1211
|
-
this.
|
|
1212
|
-
this.panoLatRange.value.copy( pt.uniforms.get( 'panoLatRange' ).value );
|
|
1213
|
-
this.panoLevelHorizon.value = pt.uniforms.get( 'panoLevelHorizon' ).value;
|
|
1220
|
+
this.viewVector.value.setFromMatrixColumn( world, 2 ).normalize();
|
|
1221
|
+
this.cameraRay.sync( pt.uniforms );
|
|
1214
1222
|
|
|
1215
1223
|
if ( ! this._camInitialized ) {
|
|
1216
1224
|
|
|
1217
1225
|
this.prevCamView.value.copy( view );
|
|
1218
1226
|
this.prevCamPos.value.copy( this.camPos.value );
|
|
1227
|
+
this.prevViewVector.value.copy( this.viewVector.value );
|
|
1219
1228
|
this._camInitialized = true;
|
|
1220
1229
|
|
|
1221
1230
|
}
|
|
1222
1231
|
|
|
1223
|
-
// NRD gUnproject; frustum size follows the shorter edge.
|
|
1232
|
+
// NRD gUnproject; frustum size follows the shorter edge. Orthographic, it is world units per pixel.
|
|
1224
1233
|
const projY = proj.elements[ 5 ] || 1;
|
|
1225
1234
|
const unproject = 1 / ( 0.5 * this.resH.value * projY );
|
|
1226
1235
|
this.minRectDimMulUnproject.value = Math.min( this.resW.value, this.resH.value ) * unproject;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fn, vec2,
|
|
1
|
+
import { Fn, vec2, vec4, float, int, uint, uvec2, uvec4, uniform, min, 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';
|
|
@@ -6,7 +6,7 @@ import { RenderStage, StageExecutionMode } from '../Pipeline/RenderStage.js';
|
|
|
6
6
|
import { MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
|
|
7
7
|
import { Ray, HitInfo, RayTracingMaterial, UVCache } from '../TSL/Struct.js';
|
|
8
8
|
import { traverseBVH } from '../TSL/BVHTraversal.js';
|
|
9
|
-
import {
|
|
9
|
+
import { cameraRayOf, cameraRayUniforms } from '../TSL/CameraRay.js';
|
|
10
10
|
import { getMaterial } from '../TSL/Common.js';
|
|
11
11
|
import { computeUVCache, processNormal, processBump, processMetalnessRoughness, triangleUVTangent, buildBucketTextureNodes, refreshBucketTextureNodes, setMaterialBucketTextures } from '../TSL/TextureSampling.js';
|
|
12
12
|
|
|
@@ -53,10 +53,7 @@ export class NormalDepth extends RenderStage {
|
|
|
53
53
|
this.cameraProjectionMatrixInverse = uniform( new Matrix4(), 'mat4' );
|
|
54
54
|
// Mirrored from the path tracer each frame — these rays must use the same camera model
|
|
55
55
|
// as the colour buffer or the denoiser's normal/depth edge-stops fight the image.
|
|
56
|
-
this.
|
|
57
|
-
this.panoLonRange = uniform( new Vector2(), 'vec2' );
|
|
58
|
-
this.panoLatRange = uniform( new Vector2(), 'vec2' );
|
|
59
|
-
this.panoLevelHorizon = uniform( 1, 'int' );
|
|
56
|
+
this.cameraRay = cameraRayUniforms();
|
|
60
57
|
this.resolutionWidth = uniform( options.width || 1 );
|
|
61
58
|
this.resolutionHeight = uniform( options.height || 1 );
|
|
62
59
|
|
|
@@ -151,7 +148,7 @@ export class NormalDepth extends RenderStage {
|
|
|
151
148
|
if ( ! pt ) return false;
|
|
152
149
|
|
|
153
150
|
const matAttr = pt.materialData?.materialStorageAttr;
|
|
154
|
-
const triSwapped = pt.
|
|
151
|
+
const triSwapped = pt.triangleGeoAttr && pt.triangleGeoAttr !== this._lastTriAttr;
|
|
155
152
|
const bvhSwapped = pt.bvhStorageAttr && pt.bvhStorageAttr !== this._lastBvhAttr;
|
|
156
153
|
const matSwapped = matAttr && matAttr !== this._lastMatAttr;
|
|
157
154
|
|
|
@@ -170,12 +167,13 @@ export class NormalDepth extends RenderStage {
|
|
|
170
167
|
|
|
171
168
|
}
|
|
172
169
|
|
|
173
|
-
if ( pt.
|
|
170
|
+
if ( pt.triangleGeoAttr && ! this._triStorageNode ) {
|
|
174
171
|
|
|
175
172
|
// uvec4, matching PathTracerStage: packed lanes must keep their exact bit pattern.
|
|
176
|
-
this._triStorageNode =
|
|
177
|
-
pt.
|
|
178
|
-
|
|
173
|
+
this._triStorageNode = {
|
|
174
|
+
geo: storage( pt.triangleGeoAttr, 'uvec4', pt.triangleGeoAttr.count ).toReadOnly(),
|
|
175
|
+
shade: storage( pt.triangleShadeAttr, 'uvec4', pt.triangleShadeAttr.count ).toReadOnly(),
|
|
176
|
+
};
|
|
179
177
|
|
|
180
178
|
}
|
|
181
179
|
|
|
@@ -196,7 +194,7 @@ export class NormalDepth extends RenderStage {
|
|
|
196
194
|
// In-place bucket swaps (model change) — graph closes over the nodes, only .value changes.
|
|
197
195
|
if ( this._linearBuckets ) refreshBucketTextureNodes( this._linearBuckets, pt.materialData?.linearBuckets );
|
|
198
196
|
|
|
199
|
-
this._lastTriAttr = pt.
|
|
197
|
+
this._lastTriAttr = pt.triangleGeoAttr || this._lastTriAttr;
|
|
200
198
|
this._lastBvhAttr = pt.bvhStorageAttr || this._lastBvhAttr;
|
|
201
199
|
this._lastMatAttr = matAttr || this._lastMatAttr;
|
|
202
200
|
|
|
@@ -236,13 +234,7 @@ export class NormalDepth extends RenderStage {
|
|
|
236
234
|
// No jitter — deterministic per-pixel ray so the temporal gate
|
|
237
235
|
// sees stable per-pixel normals across frames.
|
|
238
236
|
const uv = vec2( float( gx ).add( 0.5 ).div( resW ), float( gy ).add( 0.5 ).div( resH ) );
|
|
239
|
-
const
|
|
240
|
-
uv, camWorldMat, camProjInvMat,
|
|
241
|
-
this.cameraProjection, this.panoLonRange, this.panoLatRange, this.panoLevelHorizon
|
|
242
|
-
);
|
|
243
|
-
const rayOrigin = vec3( camWorldMat[ 3 ] );
|
|
244
|
-
|
|
245
|
-
const ray = Ray( { origin: rayOrigin, direction: rayDirWorld } );
|
|
237
|
+
const ray = Ray( cameraRayOf( uv, camWorldMat, camProjInvMat, this.cameraRay ) );
|
|
246
238
|
const hit = HitInfo.wrap( traverseBVH( ray, bvhStorage, triStorage ) );
|
|
247
239
|
|
|
248
240
|
const encodedNormal = hit.normal.mul( 0.5 ).add( 0.5 );
|
|
@@ -339,10 +331,7 @@ export class NormalDepth extends RenderStage {
|
|
|
339
331
|
|
|
340
332
|
this.cameraWorldMatrix.value.copy( pt.uniforms.get( 'cameraWorldMatrix' ).value );
|
|
341
333
|
this.cameraProjectionMatrixInverse.value.copy( pt.uniforms.get( 'cameraProjectionMatrixInverse' ).value );
|
|
342
|
-
this.
|
|
343
|
-
this.panoLonRange.value.copy( pt.uniforms.get( 'panoLonRange' ).value );
|
|
344
|
-
this.panoLatRange.value.copy( pt.uniforms.get( 'panoLatRange' ).value );
|
|
345
|
-
this.panoLevelHorizon.value = pt.uniforms.get( 'panoLevelHorizon' ).value;
|
|
334
|
+
this.cameraRay.sync( pt.uniforms );
|
|
346
335
|
|
|
347
336
|
}
|
|
348
337
|
|
package/src/Stages/PathTracer.js
CHANGED
|
@@ -188,7 +188,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
188
188
|
} );
|
|
189
189
|
|
|
190
190
|
// Scene geometry (triangle data, two-level BVH, light BVH + emissive)
|
|
191
|
-
t.register( 'geometry', () => [ this.
|
|
191
|
+
t.register( 'geometry', () => [ this.triangleGeoAttr, this.triangleShadeAttr, this.bvhStorageAttr, this.lightStorageAttr ] );
|
|
192
192
|
|
|
193
193
|
// Material storage buffer + per-property texture arrays
|
|
194
194
|
t.register( 'materials', () => {
|
|
@@ -547,6 +547,107 @@ export class PathTracer extends PathTracerStage {
|
|
|
547
547
|
|
|
548
548
|
}
|
|
549
549
|
|
|
550
|
+
/**
|
|
551
|
+
* The accumulation as it stands, for a checkpoint: the three MRT attachments (aux only while it
|
|
552
|
+
* holds samples), the per-pixel convergence buffers, and the counters that decide the next
|
|
553
|
+
* sample — together what a later frame reads, so restoring them continues the same sequence.
|
|
554
|
+
* @returns {Promise<Object>} typed arrays, tight rows, top row first as the textures hold them
|
|
555
|
+
*/
|
|
556
|
+
async captureAccumulation() {
|
|
557
|
+
|
|
558
|
+
const pool = this.storageTextures;
|
|
559
|
+
const width = pool.renderWidth;
|
|
560
|
+
const height = pool.renderHeight;
|
|
561
|
+
const pixels = width * height;
|
|
562
|
+
|
|
563
|
+
const readAttachment = async index => {
|
|
564
|
+
|
|
565
|
+
const padded = await this.renderer.readRenderTargetPixelsAsync( pool.readTarget, 0, 0, width, height, index );
|
|
566
|
+
const stride = Math.ceil( width * 16 / 256 ) * 64;
|
|
567
|
+
if ( stride === width * 4 ) return new Float32Array( padded.buffer, padded.byteOffset, pixels * 4 ).slice();
|
|
568
|
+
const tight = new Float32Array( pixels * 4 );
|
|
569
|
+
for ( let y = 0; y < height; y ++ ) tight.set( padded.subarray( y * stride, y * stride + width * 4 ), y * width * 4 );
|
|
570
|
+
return tight;
|
|
571
|
+
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
const readBuffer = async ( attr, Type ) => attr ? new Type( await this.renderer.getArrayBufferAsync( attr, null, 0, pixels * 4 ) ) : null;
|
|
575
|
+
const aux = this._auxGBufferEnabled && this._auxSamples > 0;
|
|
576
|
+
|
|
577
|
+
// Every copy is submitted here, in this task, before any await: a frame rendered between two of
|
|
578
|
+
// them would give a checkpoint whose parts disagree.
|
|
579
|
+
const state = {
|
|
580
|
+
frameCount: this.frameCount,
|
|
581
|
+
seedTick: this._seedTick,
|
|
582
|
+
auxSamples: aux ? this._auxSamples : 0,
|
|
583
|
+
convergedFraction: this._convergedFraction,
|
|
584
|
+
convergedGeometryFraction: this._convergedGeometryFraction,
|
|
585
|
+
isComplete: this.isComplete,
|
|
586
|
+
};
|
|
587
|
+
const reads = [
|
|
588
|
+
readAttachment( 0 ),
|
|
589
|
+
aux ? readAttachment( 1 ) : null,
|
|
590
|
+
aux ? readAttachment( 2 ) : null,
|
|
591
|
+
readBuffer( this._m2Attr, Float32Array ),
|
|
592
|
+
readBuffer( this._streakAttr, Uint32Array ),
|
|
593
|
+
readBuffer( this._frozenMaskAttr, Uint32Array ),
|
|
594
|
+
];
|
|
595
|
+
const [ color, normalDepth, albedo, m2, streak, frozenMask ] = await Promise.all( reads );
|
|
596
|
+
|
|
597
|
+
return { width, height, state, color, normalDepth, albedo, m2, streak, frozenMask };
|
|
598
|
+
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Writes a {@link captureAccumulation} back, so the next frame continues it. The render size must
|
|
603
|
+
* match; nothing may reset accumulation after this.
|
|
604
|
+
*/
|
|
605
|
+
restoreAccumulation( checkpoint ) {
|
|
606
|
+
|
|
607
|
+
const pool = this.storageTextures;
|
|
608
|
+
const { width, height, state } = checkpoint;
|
|
609
|
+
if ( pool.renderWidth !== width || pool.renderHeight !== height ) {
|
|
610
|
+
|
|
611
|
+
throw new Error( `checkpoint is ${width}×${height}; the render is ${pool.renderWidth}×${pool.renderHeight}` );
|
|
612
|
+
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const backend = this.renderer.backend;
|
|
616
|
+
const device = backend.device;
|
|
617
|
+
if ( ! backend.get( pool.readTarget.textures[ 0 ] ).texture ) this.renderer.initRenderTarget( pool.readTarget );
|
|
618
|
+
|
|
619
|
+
const writeAttachment = ( index, data ) => device.queue.writeTexture(
|
|
620
|
+
{ texture: backend.get( pool.readTarget.textures[ index ] ).texture },
|
|
621
|
+
data,
|
|
622
|
+
{ bytesPerRow: width * 16, rowsPerImage: height },
|
|
623
|
+
{ width, height },
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
const writeBuffer = ( attr, data ) => {
|
|
627
|
+
|
|
628
|
+
if ( attr && data ) device.queue.writeBuffer( backend.get( attr ).buffer, 0, data );
|
|
629
|
+
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
writeAttachment( 0, checkpoint.color );
|
|
633
|
+
if ( checkpoint.normalDepth ) writeAttachment( 1, checkpoint.normalDepth );
|
|
634
|
+
if ( checkpoint.albedo ) writeAttachment( 2, checkpoint.albedo );
|
|
635
|
+
writeBuffer( this._m2Attr, checkpoint.m2 );
|
|
636
|
+
writeBuffer( this._streakAttr, checkpoint.streak );
|
|
637
|
+
writeBuffer( this._frozenMaskAttr, checkpoint.frozenMask );
|
|
638
|
+
|
|
639
|
+
this.frameCount = state.frameCount;
|
|
640
|
+
this.frame.value = state.frameCount;
|
|
641
|
+
this._seedTick = state.seedTick;
|
|
642
|
+
this.hasPreviousAccumulated.value = state.frameCount > 0 ? 1 : 0;
|
|
643
|
+
this._auxSamples = checkpoint.normalDepth ? state.auxSamples : 0;
|
|
644
|
+
this._auxSeedPending = this._auxGBufferEnabled && ! checkpoint.normalDepth;
|
|
645
|
+
this._convergedFraction = state.convergedFraction ?? 0;
|
|
646
|
+
this._convergedGeometryFraction = state.convergedGeometryFraction ?? 0;
|
|
647
|
+
this.isComplete = false;
|
|
648
|
+
|
|
649
|
+
}
|
|
650
|
+
|
|
550
651
|
reset() {
|
|
551
652
|
|
|
552
653
|
super.reset();
|
|
@@ -1147,7 +1248,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
1147
1248
|
|
|
1148
1249
|
const genParams = {
|
|
1149
1250
|
rayBufferRW: pb.rayBuffer.rw,
|
|
1150
|
-
|
|
1251
|
+
hitBufferRW: pb.hitBuffer.rw,
|
|
1151
1252
|
gBufferRW,
|
|
1152
1253
|
resolution: this.resolution,
|
|
1153
1254
|
// RNG axis only (baseSeed + stratified jitter) — takes the seed counter, not the
|
|
@@ -1163,9 +1264,11 @@ export class PathTracer extends PathTracerStage {
|
|
|
1163
1264
|
focalLength: this.focalLength,
|
|
1164
1265
|
aperture: this.aperture,
|
|
1165
1266
|
focusDistance: this.focusDistance,
|
|
1166
|
-
|
|
1267
|
+
unitsPerMetre: this.unitsPerMetre,
|
|
1167
1268
|
apertureScale: this.apertureScale,
|
|
1168
1269
|
anamorphicRatio: this.anamorphicRatio,
|
|
1270
|
+
dofMode: this.dofMode,
|
|
1271
|
+
dofBlur: this.dofBlur,
|
|
1169
1272
|
renderWidth: this._wfRenderWidth,
|
|
1170
1273
|
renderHeight: this._wfRenderHeight,
|
|
1171
1274
|
chunkRowBase: this._wfChunkRowBase,
|
|
@@ -1456,8 +1559,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
1456
1559
|
envCDFTexture: freshEnvCDF,
|
|
1457
1560
|
lightBuffer: freshLight,
|
|
1458
1561
|
rayBufferRW: pb.rayBuffer.rw,
|
|
1459
|
-
|
|
1460
|
-
hitBufferRO: pb.hitBuffer.ro,
|
|
1562
|
+
hitBufferRW: pb.hitBuffer.rw,
|
|
1461
1563
|
counters,
|
|
1462
1564
|
activeIndicesRO: this._sortMaterials ? qm.getSortedRO() : qm.getActiveReadRO(),
|
|
1463
1565
|
envTexture: freshEnvTex,
|
|
@@ -1510,6 +1612,10 @@ export class PathTracer extends PathTracerStage {
|
|
|
1510
1612
|
maxRayCount: this._wfMaxRayCount,
|
|
1511
1613
|
chunkRowBase: this._wfChunkRowBase,
|
|
1512
1614
|
auxGBufferEnabled: this._auxGBufferUniform,
|
|
1615
|
+
hasSun: this.hasSun,
|
|
1616
|
+
sunDirection: this.sunDirection,
|
|
1617
|
+
sunRadiance: this.sunRadiance,
|
|
1618
|
+
sunParams: this.sunParams,
|
|
1513
1619
|
} );
|
|
1514
1620
|
this._kernelManager.register( 'shade',
|
|
1515
1621
|
shadeFn().compute(
|
|
@@ -1643,9 +1749,11 @@ export class PathTracer extends PathTracerStage {
|
|
|
1643
1749
|
focalLength: this.focalLength,
|
|
1644
1750
|
aperture: this.aperture,
|
|
1645
1751
|
focusDistance: this.focusDistance,
|
|
1646
|
-
|
|
1752
|
+
unitsPerMetre: this.unitsPerMetre,
|
|
1647
1753
|
apertureScale: this.apertureScale,
|
|
1648
1754
|
anamorphicRatio: this.anamorphicRatio,
|
|
1755
|
+
dofMode: this.dofMode,
|
|
1756
|
+
dofBlur: this.dofBlur,
|
|
1649
1757
|
bvhBuffer: freshBvh,
|
|
1650
1758
|
triangleBuffer: freshTri,
|
|
1651
1759
|
materialBuffer: freshMat,
|