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
package/src/TSL/CameraRay.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CameraRay.js — primary camera ray generation.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* cameraProjectionMatrixInverse),
|
|
6
|
-
* kernel, so switching modes writes a uniform and resets
|
|
4
|
+
* Three projections behind one `cameraProjection` uniform (CAMERA_PROJECTION_IDS): pinhole (NDC through
|
|
5
|
+
* cameraProjectionMatrixInverse), equirectangular 360 panorama, and orthographic (parallel rays from the
|
|
6
|
+
* camera's image plane). All live in the same kernel, so switching modes writes a uniform and resets
|
|
7
|
+
* accumulation — it never recompiles WGSL.
|
|
7
8
|
*
|
|
8
9
|
* Equirect mapping, for uv ∈ [0,1] with v = 0 at the top row:
|
|
9
10
|
* lon = mix( lonMin, lonMax, u ), lat = mix( latMax, latMin, v )
|
|
@@ -12,15 +13,81 @@
|
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import {
|
|
15
|
-
Fn, vec3, vec4, float, int,
|
|
16
|
-
If, normalize, mat3, mix, sin, cos, cross, dot,
|
|
16
|
+
Fn, vec3, vec4, float, int, uniform,
|
|
17
|
+
If, normalize, mat3, mix, sin, cos, cross, dot, select,
|
|
17
18
|
} from 'three/tsl';
|
|
19
|
+
import { Vector2 } from 'three';
|
|
18
20
|
|
|
19
21
|
import { Ray } from './Struct.js';
|
|
20
22
|
import { constructTBN } from './Common.js';
|
|
21
23
|
import { RandomPointInCircle } from './Random.js';
|
|
24
|
+
import { CAMERA_PROJECTION_IDS } from '../EngineDefaults.js';
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
const PANORAMA = int( CAMERA_PROJECTION_IDS.equirectangular );
|
|
27
|
+
const ORTHOGRAPHIC = int( CAMERA_PROJECTION_IDS.orthographic );
|
|
28
|
+
|
|
29
|
+
export const isOrthographic = cameraProjection => cameraProjection.equal( ORTHOGRAPHIC );
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The projection uniforms a stage re-tracing the path tracer's primary rays needs. `sync` copies the path
|
|
33
|
+
* tracer's, once a frame.
|
|
34
|
+
*/
|
|
35
|
+
export function cameraRayUniforms() {
|
|
36
|
+
|
|
37
|
+
const u = {
|
|
38
|
+
cameraProjection: uniform( 0, 'int' ),
|
|
39
|
+
panoLonRange: uniform( new Vector2(), 'vec2' ),
|
|
40
|
+
panoLatRange: uniform( new Vector2(), 'vec2' ),
|
|
41
|
+
panoLevelHorizon: uniform( 1, 'int' ),
|
|
42
|
+
/** @param {import('../managers/UniformManager.js').UniformManager} source */
|
|
43
|
+
sync( source ) {
|
|
44
|
+
|
|
45
|
+
u.cameraProjection.value = source.get( 'cameraProjection' ).value;
|
|
46
|
+
u.panoLonRange.value.copy( source.get( 'panoLonRange' ).value );
|
|
47
|
+
u.panoLatRange.value.copy( source.get( 'panoLatRange' ).value );
|
|
48
|
+
u.panoLevelHorizon.value = source.get( 'panoLevelHorizon' ).value;
|
|
49
|
+
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
return u;
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Pixel uv's primary ray, for a stage holding {@link cameraRayUniforms}. */
|
|
57
|
+
export const cameraRayOf = ( uv01, cameraWorldMatrix, cameraProjectionMatrixInverse, u ) => ( {
|
|
58
|
+
origin: cameraRayOrigin( uv01, cameraWorldMatrix, cameraProjectionMatrixInverse, u.cameraProjection ),
|
|
59
|
+
direction: cameraRayDirection(
|
|
60
|
+
uv01, cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
61
|
+
u.cameraProjection, u.panoLonRange, u.panoLatRange, u.panoLevelHorizon
|
|
62
|
+
),
|
|
63
|
+
} );
|
|
64
|
+
|
|
65
|
+
/** The point `distance` along pixel uv's primary ray — where a depth the path tracer measured lies. */
|
|
66
|
+
export const cameraRayPoint = ( uv01, distance, cameraWorldMatrix, cameraProjectionMatrixInverse, u ) => {
|
|
67
|
+
|
|
68
|
+
const { origin, direction } = cameraRayOf( uv01, cameraWorldMatrix, cameraProjectionMatrixInverse, u );
|
|
69
|
+
return origin.add( direction.mul( distance ) );
|
|
70
|
+
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/** World-space primary ray origin for pixel uv: the camera, or its image plane when orthographic. */
|
|
74
|
+
export const cameraRayOrigin = Fn( ( [ uv01, cameraWorldMatrix, cameraProjectionMatrixInverse, cameraProjection ] ) => {
|
|
75
|
+
|
|
76
|
+
const origin = vec3( cameraWorldMatrix[ 3 ] ).toVar();
|
|
77
|
+
|
|
78
|
+
If( isOrthographic( cameraProjection ), () => {
|
|
79
|
+
|
|
80
|
+
// An orthographic inverse maps x and y alone, whatever the depth.
|
|
81
|
+
const onPlane = cameraProjectionMatrixInverse.mul( vec4( uv01.x.mul( 2.0 ).sub( 1.0 ), float( 1.0 ).sub( uv01.y.mul( 2.0 ) ), 0.0, 1.0 ) );
|
|
82
|
+
origin.assign( cameraWorldMatrix.mul( vec4( onPlane.xy, 0.0, 1.0 ) ).xyz );
|
|
83
|
+
|
|
84
|
+
} );
|
|
85
|
+
|
|
86
|
+
return origin;
|
|
87
|
+
|
|
88
|
+
} );
|
|
89
|
+
|
|
90
|
+
/** World-space primary ray direction for pixel uv, from {@link cameraRayOrigin}. */
|
|
24
91
|
export const cameraRayDirection = Fn( ( [
|
|
25
92
|
uv01, cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
26
93
|
cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon
|
|
@@ -28,7 +95,7 @@ export const cameraRayDirection = Fn( ( [
|
|
|
28
95
|
|
|
29
96
|
const direction = vec3( 0.0 ).toVar();
|
|
30
97
|
|
|
31
|
-
If( cameraProjection.equal(
|
|
98
|
+
If( cameraProjection.equal( PANORAMA ), () => {
|
|
32
99
|
|
|
33
100
|
const lon = mix( panoLonRange.x, panoLonRange.y, uv01.x ).toVar();
|
|
34
101
|
const lat = mix( panoLatRange.y, panoLatRange.x, uv01.y ).toVar();
|
|
@@ -68,6 +135,10 @@ export const cameraRayDirection = Fn( ( [
|
|
|
68
135
|
.sub( back.mul( cos( lon ).mul( cosLat ) ) )
|
|
69
136
|
) );
|
|
70
137
|
|
|
138
|
+
} ).ElseIf( isOrthographic( cameraProjection ), () => {
|
|
139
|
+
|
|
140
|
+
direction.assign( normalize( vec3( cameraWorldMatrix[ 2 ] ) ).negate() );
|
|
141
|
+
|
|
71
142
|
} ).Else( () => {
|
|
72
143
|
|
|
73
144
|
const ndcPos = vec3( uv01.x.mul( 2.0 ).sub( 1.0 ), float( 1.0 ).sub( uv01.y.mul( 2.0 ) ), 1.0 );
|
|
@@ -89,10 +160,10 @@ export const generateRayFromCamera = Fn( ( [
|
|
|
89
160
|
uv01, rngState,
|
|
90
161
|
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
91
162
|
cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon,
|
|
92
|
-
enableDOF, focalLength, aperture, focusDistance,
|
|
163
|
+
enableDOF, focalLength, aperture, focusDistance, unitsPerMetre, apertureScale, anamorphicRatio, dofMode, dofBlur
|
|
93
164
|
] ) => {
|
|
94
165
|
|
|
95
|
-
const rayOriginWorld =
|
|
166
|
+
const rayOriginWorld = cameraRayOrigin( uv01, cameraWorldMatrix, cameraProjectionMatrixInverse, cameraProjection ).toVar();
|
|
96
167
|
// .toVar() so the two reads below don't inline the whole projection graph twice.
|
|
97
168
|
const rayDirectionWorld = cameraRayDirection(
|
|
98
169
|
uv01, cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
@@ -102,10 +173,20 @@ export const generateRayFromCamera = Fn( ( [
|
|
|
102
173
|
const resultOrigin = rayOriginWorld.toVar();
|
|
103
174
|
const resultDirection = rayDirectionWorld.toVar();
|
|
104
175
|
|
|
105
|
-
|
|
176
|
+
const lookMode = dofMode.equal( int( 1 ) );
|
|
177
|
+
const lensOpen = lookMode.and( dofBlur.greaterThan( 0.0 ) ).or( lookMode.not().and( focalLength.greaterThan( 0.0 ) ).and( aperture.lessThan( 64.0 ) ) );
|
|
106
178
|
|
|
107
|
-
|
|
108
|
-
|
|
179
|
+
If( enableDOF.and( lensOpen ).and( focusDistance.greaterThan( 0.001 ) ), () => {
|
|
180
|
+
|
|
181
|
+
// Orthographic 1: the blur then does not change with where the camera stands.
|
|
182
|
+
const halfViewHeight = select( cameraProjection.equal( PANORAMA ), panoLatRange.y.sub( panoLatRange.x ).mul( 0.5 ),
|
|
183
|
+
select( isOrthographic( cameraProjection ), float( 1.0 ), cameraProjectionMatrixInverse[ 1 ].y ) );
|
|
184
|
+
const apertureRadius = select( lookMode,
|
|
185
|
+
// A far background blurs by dofBlur of the image height, whatever the scene's scale.
|
|
186
|
+
dofBlur.mul( focusDistance ).mul( halfViewHeight ),
|
|
187
|
+
// f/N is the aperture's diameter, not its radius.
|
|
188
|
+
focalLength.div( aperture.mul( 2.0 ) ).mul( 0.001 ).mul( unitsPerMetre ).mul( apertureScale )
|
|
189
|
+
);
|
|
109
190
|
|
|
110
191
|
const randomPoint = RandomPointInCircle( rngState );
|
|
111
192
|
// Anamorphic squeeze — stretch horizontally for oval bokeh
|
|
@@ -113,8 +194,9 @@ export const generateRayFromCamera = Fn( ( [
|
|
|
113
194
|
const lensY = randomPoint.y;
|
|
114
195
|
|
|
115
196
|
const lensOffset = vec3( 0.0 ).toVar();
|
|
197
|
+
const focusAlongRay = focusDistance.toVar();
|
|
116
198
|
|
|
117
|
-
If( cameraProjection.equal(
|
|
199
|
+
If( cameraProjection.equal( PANORAMA ), () => {
|
|
118
200
|
|
|
119
201
|
// Every pixel points a different way, so the lens plane is built around the ray. The
|
|
120
202
|
// camera's right/up would skew bokeh into a slit away from the image centre.
|
|
@@ -126,10 +208,13 @@ export const generateRayFromCamera = Fn( ( [
|
|
|
126
208
|
const camUp = normalize( vec3( cameraWorldMatrix[ 1 ] ) );
|
|
127
209
|
lensOffset.assign( camRight.mul( lensX ).add( camUp.mul( lensY ) ) );
|
|
128
210
|
|
|
211
|
+
// A flat focal plane: focusDistance is depth along the view axis, not distance along the ray.
|
|
212
|
+
focusAlongRay.assign( focusDistance.div( dot( rayDirectionWorld, normalize( vec3( cameraWorldMatrix[ 2 ] ) ) ).negate() ) );
|
|
213
|
+
|
|
129
214
|
} );
|
|
130
215
|
|
|
131
216
|
resultOrigin.assign( rayOriginWorld.add( lensOffset.mul( apertureRadius ) ) );
|
|
132
|
-
resultDirection.assign( normalize( rayOriginWorld.add( rayDirectionWorld.mul(
|
|
217
|
+
resultDirection.assign( normalize( rayOriginWorld.add( rayDirectionWorld.mul( focusAlongRay ) ).sub( resultOrigin ) ) );
|
|
133
218
|
|
|
134
219
|
} );
|
|
135
220
|
|
package/src/TSL/Common.js
CHANGED
|
@@ -329,8 +329,17 @@ export const getDatafromStorageBuffer = Fn( ( [ buffer, stride, sampleIndex, dat
|
|
|
329
329
|
|
|
330
330
|
} );
|
|
331
331
|
|
|
332
|
-
/**
|
|
333
|
-
|
|
332
|
+
/**
|
|
333
|
+
* The five uvec4 rows of a triangle record (TRIANGLE_DATA_LAYOUT) live in two GPU buffers: rows
|
|
334
|
+
* 0–2, positions with their packed normals, in `tris.geo`; rows 3–4, UVs, flags and mesh index,
|
|
335
|
+
* in `tris.shade`. One buffer of 80 B a triangle hit the 4 GB storage-buffer limit at 53.6M.
|
|
336
|
+
*/
|
|
337
|
+
export const TRI_GEO_ROWS = 3;
|
|
338
|
+
export const TRI_SHADE_ROWS = 2;
|
|
339
|
+
|
|
340
|
+
export const triangleRow = ( tris, triIndex, row ) => row < TRI_GEO_ROWS
|
|
341
|
+
? getDatafromStorageBuffer( tris.geo, triIndex, int( row ), int( TRI_GEO_ROWS ) )
|
|
342
|
+
: getDatafromStorageBuffer( tris.shade, triIndex, int( row - TRI_GEO_ROWS ), int( TRI_SHADE_ROWS ) );
|
|
334
343
|
|
|
335
344
|
// Unit normal to an octahedral snorm16 pair; GPU twin of packNormalOct in EngineDefaults.
|
|
336
345
|
export const packNormalOct = /*@__PURE__*/ wgslFn( `
|
package/src/TSL/DebugKernel.js
CHANGED
|
@@ -27,7 +27,7 @@ export function buildDebugKernel( params ) {
|
|
|
27
27
|
resolution, renderWidth, renderHeight,
|
|
28
28
|
cameraWorldMatrix, cameraProjectionMatrixInverse, cameraProjectionMatrix, cameraViewMatrix,
|
|
29
29
|
cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon,
|
|
30
|
-
enableDOF, focalLength, aperture, focusDistance,
|
|
30
|
+
enableDOF, focalLength, aperture, focusDistance, unitsPerMetre, apertureScale, anamorphicRatio, dofMode, dofBlur,
|
|
31
31
|
bvhBuffer, triangleBuffer, materialBuffer,
|
|
32
32
|
envTexture, environmentMatrix, environmentIntensity, enableEnvironmentLight,
|
|
33
33
|
visMode, debugVisScale,
|
|
@@ -50,7 +50,7 @@ export function buildDebugKernel( params ) {
|
|
|
50
50
|
pixelCoord.div( resolution ), seed,
|
|
51
51
|
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
52
52
|
cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon,
|
|
53
|
-
enableDOF, focalLength, aperture, focusDistance,
|
|
53
|
+
enableDOF, focalLength, aperture, focusDistance, unitsPerMetre, apertureScale, anamorphicRatio, dofMode, dofBlur,
|
|
54
54
|
) );
|
|
55
55
|
|
|
56
56
|
const color = vec4( 1.0, 0.0, 1.0, 1.0 ).toVar();
|
package/src/TSL/Displacement.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Fn, float, vec2, int, If, Loop, abs, normalize, dot, max, uintBitsToFloat } from 'three/tsl';
|
|
2
2
|
|
|
3
3
|
import { struct } from './patches.js';
|
|
4
|
-
import {
|
|
4
|
+
import { triangleRow, instanceRows, instanceDirToWorld } from './Common.js';
|
|
5
5
|
import { sampleDisplacementMap, bucketTexelSize, getLinearBucketTextures } from './TextureSampling.js';
|
|
6
6
|
|
|
7
7
|
// Ray-displacement intersection configuration
|
|
@@ -42,12 +42,12 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
42
42
|
// Fetch triangle vertex data
|
|
43
43
|
const triIdx = hitInfo.triangleIndex;
|
|
44
44
|
|
|
45
|
-
const pA = uintBitsToFloat(
|
|
46
|
-
const pB = uintBitsToFloat(
|
|
47
|
-
const pC = uintBitsToFloat(
|
|
45
|
+
const pA = uintBitsToFloat( triangleRow( triangleBuffer, triIdx, 0 ).xyz ).toVar();
|
|
46
|
+
const pB = uintBitsToFloat( triangleRow( triangleBuffer, triIdx, 1 ).xyz );
|
|
47
|
+
const pC = uintBitsToFloat( triangleRow( triangleBuffer, triIdx, 2 ).xyz );
|
|
48
48
|
|
|
49
|
-
const uvData1 = uintBitsToFloat(
|
|
50
|
-
const uvData2 = uintBitsToFloat(
|
|
49
|
+
const uvData1 = uintBitsToFloat( triangleRow( triangleBuffer, triIdx, 3 ) ).toVar();
|
|
50
|
+
const uvData2 = uintBitsToFloat( triangleRow( triangleBuffer, triIdx, 4 ).xy );
|
|
51
51
|
|
|
52
52
|
const uvA = uvData1.xy.toVar();
|
|
53
53
|
const uvB = uvData1.zw;
|
|
@@ -30,7 +30,7 @@ import { struct } from './patches.js';
|
|
|
30
30
|
import {
|
|
31
31
|
MIN_PDF, getDatafromStorageBuffer, powerHeuristic, MATERIAL_SLOTS, MATERIAL_SLOT,
|
|
32
32
|
computeDotProductsAniso, instanceRows, instanceNormalToWorld, instancePointToWorld,
|
|
33
|
-
unpackTriangleNormal,
|
|
33
|
+
unpackTriangleNormal, triangleRow, SHADOW_END
|
|
34
34
|
} from './Common.js';
|
|
35
35
|
import { TRI_MATERIAL_MASK } from '../EngineDefaults.js';
|
|
36
36
|
import { getRandomSample1D, getRandomSample2D } from './Random.js';
|
|
@@ -298,11 +298,11 @@ export const TriangleData = struct( {
|
|
|
298
298
|
export const fetchTriangleData = Fn( ( [ triangleIndex, triangleBuffer, bvhBuffer, instanceLeaf ] ) => {
|
|
299
299
|
|
|
300
300
|
// Positions carry their packed normal in .w
|
|
301
|
-
const pos0 =
|
|
302
|
-
const pos1 =
|
|
303
|
-
const pos2 =
|
|
301
|
+
const pos0 = triangleRow( triangleBuffer, triangleIndex, 0 ).toVar();
|
|
302
|
+
const pos1 = triangleRow( triangleBuffer, triangleIndex, 1 ).toVar();
|
|
303
|
+
const pos2 = triangleRow( triangleBuffer, triangleIndex, 2 ).toVar();
|
|
304
304
|
|
|
305
|
-
const uvMat =
|
|
305
|
+
const uvMat = triangleRow( triangleBuffer, triangleIndex, 4 );
|
|
306
306
|
|
|
307
307
|
const v0 = uintBitsToFloat( pos0.xyz ).toVar(), v1 = uintBitsToFloat( pos1.xyz ).toVar(), v2 = uintBitsToFloat( pos2.xyz ).toVar();
|
|
308
308
|
const n0 = unpackTriangleNormal( pos0.w ).toVar(), n1 = unpackTriangleNormal( pos1.w ).toVar(), n2 = unpackTriangleNormal( pos2.w ).toVar();
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Fn, float, int, uint, vec2, vec4, If, Loop, Return, instanceIndex, max, sin, dot, select } from 'three/tsl';
|
|
2
|
+
|
|
3
|
+
import { REC709_LUMINANCE_COEFFICIENTS } from './Common.js';
|
|
4
|
+
|
|
5
|
+
/** Floats per row of the packed table: width + 1 texels, padded to the 256 bytes a buffer→texture copy needs. */
|
|
6
|
+
export const cdfRowStride = width => Math.ceil( ( width + 1 ) * 4 / 256 ) * 64;
|
|
7
|
+
|
|
8
|
+
// First index in [0, n) whose value is ≥ target, n − 1 when none is — as the CPU builder's search.
|
|
9
|
+
function lowerBound( n, valueAt, target ) {
|
|
10
|
+
|
|
11
|
+
const lo = int( 0 ).toVar();
|
|
12
|
+
const hi = int( n - 1 ).toVar();
|
|
13
|
+
Loop( { start: int( 0 ), end: int( Math.ceil( Math.log2( n ) ) + 1 ), type: 'int', condition: '<', name: 'step' }, () => {
|
|
14
|
+
|
|
15
|
+
If( lo.lessThan( hi ), () => {
|
|
16
|
+
|
|
17
|
+
const mid = lo.add( hi ).div( 2 ).toVar();
|
|
18
|
+
If( valueAt( mid ).lessThan( target ), () => {
|
|
19
|
+
|
|
20
|
+
lo.assign( mid.add( 1 ) );
|
|
21
|
+
|
|
22
|
+
} ).Else( () => {
|
|
23
|
+
|
|
24
|
+
hi.assign( mid );
|
|
25
|
+
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
} );
|
|
29
|
+
|
|
30
|
+
} );
|
|
31
|
+
return lo;
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// select(), never mix(): a + t·(b − a) is not b at t = 1, and the search compares a prefix sum
|
|
36
|
+
// against the row total it ends on.
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The GPU twin of `EquirectHDRInfo.computeCDF`, into the packed (width + 1) × height table the
|
|
40
|
+
* sampler reads. `stats[ 1 ]` ends as ( totalSum, compensationDelta, compensated, 0 ).
|
|
41
|
+
* @returns {Array} kernels, dispatched in order
|
|
42
|
+
*/
|
|
43
|
+
export function buildEnvironmentCDFKernels( { pixels, rows, prefix, stats, cdf, width, height } ) {
|
|
44
|
+
|
|
45
|
+
const stride = cdfRowStride( width );
|
|
46
|
+
const sinTheta = y => sin( float( y ).add( 0.5 ).mul( Math.PI / height ) );
|
|
47
|
+
const weight = ( y, x, sinT ) => dot( pixels.element( y.mul( width ).add( x ) ).xyz, REC709_LUMINANCE_COEFFICIENTS ).mul( sinT );
|
|
48
|
+
const firstThreadOnly = () => If( instanceIndex.greaterThan( uint( 0 ) ), () => {
|
|
49
|
+
|
|
50
|
+
Return();
|
|
51
|
+
|
|
52
|
+
} );
|
|
53
|
+
|
|
54
|
+
const rowTotals = Fn( () => {
|
|
55
|
+
|
|
56
|
+
const y = instanceIndex;
|
|
57
|
+
If( y.greaterThanEqual( uint( height ) ), () => {
|
|
58
|
+
|
|
59
|
+
Return();
|
|
60
|
+
|
|
61
|
+
} );
|
|
62
|
+
|
|
63
|
+
const sinT = sinTheta( y ).toVar();
|
|
64
|
+
const total = float( 0 ).toVar();
|
|
65
|
+
Loop( { start: int( 0 ), end: int( width ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
66
|
+
|
|
67
|
+
total.addAssign( weight( y, uint( i ), sinT ) );
|
|
68
|
+
|
|
69
|
+
} );
|
|
70
|
+
rows.element( y ).assign( vec4( total, 0, 0, 0 ) );
|
|
71
|
+
|
|
72
|
+
} )().compute( height, [ 64 ] );
|
|
73
|
+
|
|
74
|
+
const mean = Fn( () => {
|
|
75
|
+
|
|
76
|
+
firstThreadOnly();
|
|
77
|
+
const total = float( 0 ).toVar();
|
|
78
|
+
Loop( { start: int( 0 ), end: int( height ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
79
|
+
|
|
80
|
+
total.addAssign( rows.element( i ).x );
|
|
81
|
+
|
|
82
|
+
} );
|
|
83
|
+
stats.element( 0 ).assign( vec4( total, total.div( width * height ), 0, 0 ) );
|
|
84
|
+
|
|
85
|
+
} )().compute( 1, [ 1 ] );
|
|
86
|
+
|
|
87
|
+
const prefixSums = Fn( () => {
|
|
88
|
+
|
|
89
|
+
const y = instanceIndex;
|
|
90
|
+
If( y.greaterThanEqual( uint( height ) ), () => {
|
|
91
|
+
|
|
92
|
+
Return();
|
|
93
|
+
|
|
94
|
+
} );
|
|
95
|
+
|
|
96
|
+
const sinT = sinTheta( y ).toVar();
|
|
97
|
+
const delta = stats.element( 0 ).y.toVar();
|
|
98
|
+
const raw = float( 0 ).toVar();
|
|
99
|
+
const compensated = float( 0 ).toVar();
|
|
100
|
+
Loop( { start: int( 0 ), end: int( width ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
101
|
+
|
|
102
|
+
const w = weight( y, uint( i ), sinT ).toVar();
|
|
103
|
+
raw.addAssign( w );
|
|
104
|
+
compensated.addAssign( max( w.sub( delta ), 0 ) );
|
|
105
|
+
prefix.element( y.mul( width ).add( uint( i ) ) ).assign( vec2( raw, compensated ) );
|
|
106
|
+
|
|
107
|
+
} );
|
|
108
|
+
rows.element( y ).assign( vec4( raw, compensated, 0, 0 ) );
|
|
109
|
+
|
|
110
|
+
} )().compute( height, [ 64 ] );
|
|
111
|
+
|
|
112
|
+
const marginal = Fn( () => {
|
|
113
|
+
|
|
114
|
+
firstThreadOnly();
|
|
115
|
+
const compensatedTotal = float( 0 ).toVar();
|
|
116
|
+
Loop( { start: int( 0 ), end: int( height ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
117
|
+
|
|
118
|
+
compensatedTotal.addAssign( rows.element( i ).y );
|
|
119
|
+
|
|
120
|
+
} );
|
|
121
|
+
const useCompensated = compensatedTotal.greaterThan( 0 ).toVar();
|
|
122
|
+
|
|
123
|
+
const cumulative = float( 0 ).toVar();
|
|
124
|
+
Loop( { start: int( 0 ), end: int( height ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
125
|
+
|
|
126
|
+
const r = rows.element( i ).toVar();
|
|
127
|
+
cumulative.addAssign( select( useCompensated, r.y, r.x ) );
|
|
128
|
+
rows.element( i ).assign( vec4( r.x, r.y, cumulative, 0 ) );
|
|
129
|
+
|
|
130
|
+
} );
|
|
131
|
+
|
|
132
|
+
const s = stats.element( 0 ).toVar();
|
|
133
|
+
stats.element( 1 ).assign( select( useCompensated, vec4( compensatedTotal, s.y, 1, 0 ), vec4( s.x, 0, 0, 0 ) ) );
|
|
134
|
+
|
|
135
|
+
Loop( { start: int( 0 ), end: int( height ), type: 'int', condition: '<', name: 'row' }, ( { row } ) => {
|
|
136
|
+
|
|
137
|
+
const k = select(
|
|
138
|
+
cumulative.greaterThan( 0 ),
|
|
139
|
+
float( lowerBound( height, j => rows.element( j ).z, float( row ).add( 1 ).div( height ).mul( cumulative ) ) ),
|
|
140
|
+
float( height - 1 ),
|
|
141
|
+
);
|
|
142
|
+
cdf.element( uint( row ).mul( stride ).add( width ) ).assign( k.add( 0.5 ).div( height ) );
|
|
143
|
+
|
|
144
|
+
} );
|
|
145
|
+
|
|
146
|
+
} )().compute( 1, [ 1 ] );
|
|
147
|
+
|
|
148
|
+
const conditional = Fn( () => {
|
|
149
|
+
|
|
150
|
+
const idx = instanceIndex;
|
|
151
|
+
If( idx.greaterThanEqual( uint( width * height ) ), () => {
|
|
152
|
+
|
|
153
|
+
Return();
|
|
154
|
+
|
|
155
|
+
} );
|
|
156
|
+
|
|
157
|
+
const x = idx.mod( width );
|
|
158
|
+
const y = idx.div( width );
|
|
159
|
+
const useCompensated = stats.element( 1 ).z.greaterThan( 0 ).toVar();
|
|
160
|
+
const r = rows.element( y ).toVar();
|
|
161
|
+
const rowTotal = select( useCompensated, r.y, r.x ).toVar();
|
|
162
|
+
const base = y.mul( width ).toVar();
|
|
163
|
+
const k = lowerBound( width, j => {
|
|
164
|
+
|
|
165
|
+
const p = prefix.element( base.add( uint( j ) ) );
|
|
166
|
+
return select( useCompensated, p.y, p.x );
|
|
167
|
+
|
|
168
|
+
}, float( x ).add( 1 ).div( width ).mul( rowTotal ) );
|
|
169
|
+
const column = select( rowTotal.greaterThan( 0 ), float( k ), float( width - 1 ) );
|
|
170
|
+
cdf.element( y.mul( stride ).add( x ) ).assign( column.add( 0.5 ).div( width ) );
|
|
171
|
+
|
|
172
|
+
} )().compute( width * height, [ 64 ] );
|
|
173
|
+
|
|
174
|
+
return [ rowTotals, mean, prefixSums, marginal, conditional ];
|
|
175
|
+
|
|
176
|
+
}
|
|
@@ -28,7 +28,7 @@ import { RAY_FLAG, COUNTER } from '../Processor/QueueManager.js';
|
|
|
28
28
|
import {
|
|
29
29
|
writeRayOriginMeta, writeRayDirFlags, writeRayThroughputPdf,
|
|
30
30
|
writeRayRadiance, writeGBuffer,
|
|
31
|
-
writeMediumStack, writeFeatureThroughput,
|
|
31
|
+
writeMediumStack, writeFeatureThroughput, writeRngState,
|
|
32
32
|
} from '../Processor/PackedRayBuffer.js';
|
|
33
33
|
|
|
34
34
|
const WG_SIZE = 16;
|
|
@@ -36,11 +36,11 @@ const WG_SIZE = 16;
|
|
|
36
36
|
export function buildGenerateKernel( params ) {
|
|
37
37
|
|
|
38
38
|
const {
|
|
39
|
-
rayBufferRW,
|
|
39
|
+
rayBufferRW, hitBufferRW, gBufferRW,
|
|
40
40
|
resolution, frame,
|
|
41
41
|
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
42
42
|
cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon,
|
|
43
|
-
enableDOF, focalLength, aperture, focusDistance,
|
|
43
|
+
enableDOF, focalLength, aperture, focusDistance, unitsPerMetre, apertureScale, anamorphicRatio, dofMode, dofBlur,
|
|
44
44
|
renderWidth,
|
|
45
45
|
chunkRowBase, chunkRows, // row band offset (global first row) + row count for this chunk
|
|
46
46
|
transmissiveBounces, // per-ray refraction budget (megakernel parity: PathTracerCore.js:606)
|
|
@@ -78,7 +78,7 @@ export function buildGenerateKernel( params ) {
|
|
|
78
78
|
jitteredUV, seed,
|
|
79
79
|
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
80
80
|
cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon,
|
|
81
|
-
enableDOF, focalLength, aperture, focusDistance,
|
|
81
|
+
enableDOF, focalLength, aperture, focusDistance, unitsPerMetre, apertureScale, anamorphicRatio, dofMode, dofBlur,
|
|
82
82
|
) );
|
|
83
83
|
|
|
84
84
|
writeRayOriginMeta( rayBufferRW, rayID, ray.origin, int( 0 ), int( 0 ) );
|
|
@@ -104,7 +104,7 @@ export function buildGenerateKernel( params ) {
|
|
|
104
104
|
|
|
105
105
|
writeMediumStack( rayBufferRW, rayID, uint( 0 ), uint( transmissiveBounces ), float( 1.0 ), float( 1.0 ), float( 1.0 ) );
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
writeRngState( hitBufferRW, rayID, seed );
|
|
108
108
|
|
|
109
109
|
};
|
|
110
110
|
|
package/src/TSL/HitFacet.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
packHalf2x16, unpackHalf2x16,
|
|
16
16
|
} from 'three/tsl';
|
|
17
17
|
import {
|
|
18
|
-
|
|
18
|
+
unpackTriangleNormal, triangleRow, instanceRows, instanceFaceNormalToWorld,
|
|
19
19
|
} from './Common.js';
|
|
20
20
|
import { shadowTerminatorLift } from './ShadowTerminator.js';
|
|
21
21
|
|
|
@@ -30,9 +30,9 @@ export function hitFacet( { triangleBuffer, bvhBuffer, triIdx, instanceLeaf, hit
|
|
|
30
30
|
|
|
31
31
|
If( didHit, () => {
|
|
32
32
|
|
|
33
|
-
const recA =
|
|
34
|
-
const recB =
|
|
35
|
-
const recC =
|
|
33
|
+
const recA = triangleRow( triangleBuffer, triIdx, 0 ).toVar();
|
|
34
|
+
const recB = triangleRow( triangleBuffer, triIdx, 1 ).toVar();
|
|
35
|
+
const recC = triangleRow( triangleBuffer, triIdx, 2 ).toVar();
|
|
36
36
|
const V0 = uintBitsToFloat( recA.xyz ).toVar();
|
|
37
37
|
const V1 = uintBitsToFloat( recB.xyz ).toVar();
|
|
38
38
|
const V2 = uintBitsToFloat( recC.xyz ).toVar();
|
package/src/TSL/LightsDirect.js
CHANGED
|
@@ -27,8 +27,8 @@ import {
|
|
|
27
27
|
|
|
28
28
|
import { Ray, ShadowMaterial, HitInfo } from './Struct.js';
|
|
29
29
|
import {
|
|
30
|
-
REC709_LUMINANCE_COEFFICIENTS, getShadowMaterial,
|
|
31
|
-
instanceFaceNormalToWorld,
|
|
30
|
+
REC709_LUMINANCE_COEFFICIENTS, getShadowMaterial, instanceRows,
|
|
31
|
+
instanceFaceNormalToWorld, triangleRow, getAlphaShadowsUniform, shadowFlagsSettle, offsetRayOrigin,
|
|
32
32
|
} from './Common.js';
|
|
33
33
|
import { fresnelDielectric } from './Fresnel.js';
|
|
34
34
|
import { calculateBeerLawAbsorption } from './MaterialTransmission.js';
|
|
@@ -92,7 +92,7 @@ export const traceShadowRay = Fn( ( [
|
|
|
92
92
|
// Opaque fast-path: check the per-triangle blocker bit, set at extraction time when
|
|
93
93
|
// alphaMode/transparent/transmission/opacity all indicate a fully opaque surface.
|
|
94
94
|
// Short-circuits the 7-slot getShadowMaterial fetch and the whole alpha decision tree.
|
|
95
|
-
const flags =
|
|
95
|
+
const flags = triangleRow( triangleBuffer, shadowHit.triangleIndex, 4 ).z;
|
|
96
96
|
If( shadowFlagsSettle( flags ), () => {
|
|
97
97
|
|
|
98
98
|
transmittance.assign( 0.0 );
|
|
@@ -125,8 +125,8 @@ export const traceShadowRay = Fn( ( [
|
|
|
125
125
|
const baryU = shadowHit.uv.x;
|
|
126
126
|
const baryV = shadowHit.uv.y;
|
|
127
127
|
const baryW = float( 1.0 ).sub( baryU ).sub( baryV );
|
|
128
|
-
const uvData1 = uintBitsToFloat(
|
|
129
|
-
const uvData2 = uintBitsToFloat(
|
|
128
|
+
const uvData1 = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 3 ) );
|
|
129
|
+
const uvData2 = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 4 ).xy );
|
|
130
130
|
const hitUV = uvData1.xy.mul( baryW ).add( uvData1.zw.mul( baryU ) ).add( uvData2.mul( baryV ) );
|
|
131
131
|
const albedoUV = getTransformedUV( { uv: hitUV, transform: shadowMaterial.albedoTransform } );
|
|
132
132
|
texAlpha.assign( sampleBucket( _shadowAlbedoMaps, shadowMaterial.albedoMapIndex, albedoUV ).a );
|
|
@@ -181,9 +181,9 @@ export const traceShadowRay = Fn( ( [
|
|
|
181
181
|
// Deferred geometric-normal compute — refetch triangle positions and
|
|
182
182
|
// derive the normal here so opaque/alpha-cutout shadow hits don't pay
|
|
183
183
|
// the cross+normalize cost in BVH traversal.
|
|
184
|
-
const pA = uintBitsToFloat(
|
|
185
|
-
const pB = uintBitsToFloat(
|
|
186
|
-
const pC = uintBitsToFloat(
|
|
184
|
+
const pA = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 0 ).xyz );
|
|
185
|
+
const pB = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 1 ).xyz );
|
|
186
|
+
const pC = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 2 ).xyz );
|
|
187
187
|
// Positions are in the blocker's object space; the cross product is a normal, so
|
|
188
188
|
// it rides the inverse-transpose back out rather than the forward matrix.
|
|
189
189
|
const objNormal = normalize( cross( pB.sub( pA ), pC.sub( pA ) ) ).toVar();
|