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/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/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,7 +36,7 @@ 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,
|
|
@@ -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();
|
|
@@ -88,6 +88,7 @@ import {
|
|
|
88
88
|
sampleEquirectProbability,
|
|
89
89
|
} from './Environment.js';
|
|
90
90
|
import { shadowTerminatorOrigin } from './ShadowTerminator.js';
|
|
91
|
+
import { sunRadianceToward, sampleSunDisc } from './Sun.js';
|
|
91
92
|
|
|
92
93
|
const TWO_PI = 2.0 * PI;
|
|
93
94
|
|
|
@@ -798,6 +799,8 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
798
799
|
// The facet on the viewer's side, which shadow rays are offset off, and the shadow terminator lift
|
|
799
800
|
// (ShadowTerminator.js) for the light and environment ones
|
|
800
801
|
terminatorLift, facetNormal, terminatorCutoff,
|
|
802
|
+
// The physical sky's sun (Sun.js); its BSDF-sampled partner is the miss branch in ShadeKernel.
|
|
803
|
+
hasSun, sunDirection, sunRadiance, sunParams,
|
|
801
804
|
] ) => {
|
|
802
805
|
|
|
803
806
|
const totalContribution = vec3( 0.0 ).toVar();
|
|
@@ -987,55 +990,71 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
987
990
|
|
|
988
991
|
} );
|
|
989
992
|
// =====================================================================
|
|
990
|
-
// DETERMINISTIC ENVIRONMENT NEE
|
|
991
|
-
//
|
|
992
|
-
// estimator together with the implicit miss check in the main loop.
|
|
993
|
+
// DETERMINISTIC ENVIRONMENT NEE — the map, then the physical sky's sun, each MIS'd with its
|
|
994
|
+
// partner at the miss. One loop, so the shadow ray and BSDF evaluation are emitted once.
|
|
993
995
|
// =====================================================================
|
|
994
996
|
|
|
995
997
|
If( enableEnvironmentLight, () => {
|
|
996
998
|
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
+
const passes = select( hasSun.greaterThan( int( 0 ) ), int( 2 ), int( 1 ) );
|
|
1000
|
+
Loop( { start: int( 0 ), end: passes, type: 'int', condition: '<', name: 'envPass' }, ( { envPass } ) => {
|
|
999
1001
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
).toVar();
|
|
1002
|
+
const isSun = envPass.equal( int( 1 ) ).toVar();
|
|
1003
|
+
const radiance = vec3( 0.0 ).toVar();
|
|
1004
|
+
const direction = vec3( 0.0, 1.0, 0.0 ).toVar();
|
|
1005
|
+
const lightPdf = float( 0.0 ).toVar();
|
|
1005
1006
|
|
|
1006
|
-
|
|
1007
|
-
const envPdf = envSampleResult.w.toVar();
|
|
1007
|
+
If( isSun, () => {
|
|
1008
1008
|
|
|
1009
|
-
|
|
1009
|
+
const sunXi = getRandomSample2D( pixelCoord, int( 0 ), dimBase.add( int( 9 ) ), rngState, resolution, frame );
|
|
1010
|
+
direction.assign( sampleSunDisc( sunDirection, sunParams, sunXi ) );
|
|
1011
|
+
radiance.assign( sunRadianceToward( direction, sunDirection, sunRadiance, sunParams ).mul( environmentIntensity ) );
|
|
1012
|
+
lightPdf.assign( select( radiance.x.add( radiance.y ).add( radiance.z ).greaterThan( 0.0 ), float( 1.0 ).div( sunParams.y ), float( 0.0 ) ) );
|
|
1010
1013
|
|
|
1011
|
-
|
|
1014
|
+
} ).Else( () => {
|
|
1012
1015
|
|
|
1013
|
-
|
|
1016
|
+
const envRandom = getRandomSample2D( pixelCoord, int( 0 ), dimBase.add( int( 1 ) ), rngState, resolution, frame ).toVar();
|
|
1017
|
+
const envColor = vec3( 0.0 ).toVar();
|
|
1018
|
+
const envSampleResult = sampleEquirectProbability(
|
|
1019
|
+
envTexture, envCDFTexture,
|
|
1020
|
+
envMatrix, environmentIntensity, envTotalSum, envCompensationDelta, envResolution, envRandom, envColor
|
|
1021
|
+
).toVar();
|
|
1022
|
+
direction.assign( envSampleResult.xyz );
|
|
1023
|
+
radiance.assign( envColor );
|
|
1024
|
+
lightPdf.assign( envSampleResult.w );
|
|
1014
1025
|
|
|
1015
|
-
|
|
1026
|
+
} );
|
|
1016
1027
|
|
|
1017
|
-
|
|
1028
|
+
If( lightPdf.greaterThan( 0.0 ), () => {
|
|
1018
1029
|
|
|
1019
|
-
|
|
1020
|
-
// discrete-light path above.
|
|
1021
|
-
const envDots = DotProducts.wrap( computeDotProductsAniso( hitNormal, viewDir, envDirection, material ) );
|
|
1022
|
-
const brdfValue = evaluateMaterialResponseFromDots( material, envDots );
|
|
1023
|
-
const bPdf = calculateMaterialPDFFromDots( material, envDots ).toVar();
|
|
1024
|
-
|
|
1025
|
-
// Balance heuristic for env MIS — optimal for MIS-compensated PDFs (Karlík et al. 2019).
|
|
1026
|
-
// The implicit path uses material combinedPdf as prevBouncePdf at the miss check.
|
|
1027
|
-
const misW = select(
|
|
1028
|
-
bPdf.greaterThan( 0.0 ),
|
|
1029
|
-
balanceHeuristic( { pdf1: envPdf, pdf2: bPdf } ),
|
|
1030
|
-
float( 1.0 )
|
|
1031
|
-
).toVar();
|
|
1032
|
-
|
|
1033
|
-
// Base contribution WITHOUT visibility (deterministic estimator; no stochastic scaling).
|
|
1034
|
-
const baseContribution = envColor.mul( brdfValue ).mul( NoL ).mul( misW ).div( max( envPdf, 1e-10 ) );
|
|
1035
|
-
totalContribution.addAssign( baseContribution.mul( visibility ) );
|
|
1036
|
-
If( wantUnoccluded, () => {
|
|
1030
|
+
const NoL = max( float( 0.0 ), dot( hitNormal, direction ) ).toVar();
|
|
1037
1031
|
|
|
1038
|
-
|
|
1032
|
+
If( NoL.greaterThan( 0.0 ).and( isDirectionValid( { direction, surfaceNormal: geomNormal } ) ), () => {
|
|
1033
|
+
|
|
1034
|
+
const visibility = shadow( lightShadowOrigin( direction ), direction, float( 1e20 ) );
|
|
1035
|
+
|
|
1036
|
+
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
1037
|
+
|
|
1038
|
+
const envDots = DotProducts.wrap( computeDotProductsAniso( hitNormal, viewDir, direction, material ) );
|
|
1039
|
+
const brdfValue = evaluateMaterialResponseFromDots( material, envDots );
|
|
1040
|
+
const bPdf = calculateMaterialPDFFromDots( material, envDots ).toVar();
|
|
1041
|
+
|
|
1042
|
+
// The map pairs with the balance heuristic — optimal for its MIS-compensated pdf
|
|
1043
|
+
// (Karlík et al. 2019); the sun, a small bright source, with the power heuristic.
|
|
1044
|
+
const misW = select(
|
|
1045
|
+
bPdf.greaterThan( 0.0 ),
|
|
1046
|
+
select( isSun, powerHeuristic( { pdf1: lightPdf, pdf2: bPdf } ), balanceHeuristic( { pdf1: lightPdf, pdf2: bPdf } ) ),
|
|
1047
|
+
float( 1.0 )
|
|
1048
|
+
).toVar();
|
|
1049
|
+
|
|
1050
|
+
// Base contribution WITHOUT visibility (deterministic estimator; no stochastic scaling).
|
|
1051
|
+
const baseContribution = radiance.mul( brdfValue ).mul( NoL ).mul( misW ).div( max( lightPdf, 1e-10 ) );
|
|
1052
|
+
totalContribution.addAssign( baseContribution.mul( visibility ) );
|
|
1053
|
+
If( wantUnoccluded, () => {
|
|
1054
|
+
|
|
1055
|
+
unoccludedContribution.addAssign( baseContribution );
|
|
1056
|
+
|
|
1057
|
+
} );
|
|
1039
1058
|
|
|
1040
1059
|
} );
|
|
1041
1060
|
|
|
@@ -1047,7 +1066,6 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
1047
1066
|
|
|
1048
1067
|
} );
|
|
1049
1068
|
|
|
1050
|
-
|
|
1051
1069
|
// EMISSIVE TRIANGLE DIRECT LIGHTING
|
|
1052
1070
|
// NOTE: Emissive triangle sampling is handled separately in pathtracer_core.fs
|
|
1053
1071
|
// to bypass firefly suppression. Do not add it here to avoid double-counting.
|
package/src/TSL/Random.js
CHANGED
|
@@ -132,6 +132,7 @@ export const RandomPointInCircle = ( rngState ) => {
|
|
|
132
132
|
// +6 subsurface HG scatter dir (ShadeKernel)
|
|
133
133
|
// +7 ground-catcher cosine BSDF (ShadeKernel)
|
|
134
134
|
// +8 indirect-strategy direction (LightsIndirect)
|
|
135
|
+
// +9 physical-sky sun disc (LightsSampling)
|
|
135
136
|
// 1D +0 (free — was the {lights,BRDF} stochastic strategy pick)
|
|
136
137
|
// +1 emissive-triangle pick (EmissiveSampling / LightBVHSampling)
|
|
137
138
|
// +2 BSDF lobe selection (ShadeKernel)
|