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
|
@@ -31,7 +31,9 @@ const MAX_PARALLEL_WORKERS = 8;
|
|
|
31
31
|
* @param {number} depth - Maximum BVH depth
|
|
32
32
|
* @param {Function|null} progressCallback - Optional progress callback (0-100)
|
|
33
33
|
* @param {Object} config - Builder config (maxLeafSize, numBins, treelet settings, etc.)
|
|
34
|
-
* @returns {Promise<{bvhData: Float32Array, bvhRoot: true, reorderedTriangles
|
|
34
|
+
* @returns {Promise<{bvhData: Float32Array, bvhRoot: true, order?: Uint32Array, triangles?: Uint32Array, reorderedTriangles?: Uint32Array, splitStats: Object}>}
|
|
35
|
+
* the parallel build returns the input `triangles` and the BVH `order` of them (record i of the
|
|
36
|
+
* result is record order[i] of the input); its single-worker fallback returns `reorderedTriangles`.
|
|
35
37
|
*/
|
|
36
38
|
export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
37
39
|
|
|
@@ -45,9 +47,10 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
45
47
|
|
|
46
48
|
( async () => {
|
|
47
49
|
|
|
48
|
-
//
|
|
49
|
-
const
|
|
50
|
-
|
|
50
|
+
// A caller that already holds the records in a SharedArrayBuffer hands it over as is.
|
|
51
|
+
const owned = triangles.buffer instanceof SharedArrayBuffer && triangles.byteOffset === 0 && triangles.byteLength === triangles.buffer.byteLength;
|
|
52
|
+
const sharedTriangleData = owned ? triangles.buffer : new SharedArrayBuffer( triangles.byteLength );
|
|
53
|
+
if ( ! owned ) new Uint32Array( sharedTriangleData ).set( triangles );
|
|
51
54
|
triangles = null; // shared copy is the only one needed; the fallback rebuilds from it
|
|
52
55
|
|
|
53
56
|
const sharedCentroids = new SharedArrayBuffer( triangleCount * 3 * 4 );
|
|
@@ -55,7 +58,9 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
55
58
|
const sharedBMax = new SharedArrayBuffer( triangleCount * 3 * 4 );
|
|
56
59
|
const sharedIndices = new SharedArrayBuffer( triangleCount * 4 );
|
|
57
60
|
const sharedMortonCodes = new SharedArrayBuffer( triangleCount * 4 );
|
|
58
|
-
|
|
61
|
+
// No reordered copy: the caller writes records in `order` from `triangles` itself. At
|
|
62
|
+
// 14.9M triangles that copy was 1.2 GB, and the allocation that failed the build.
|
|
63
|
+
const sharedReorderBuffer = null;
|
|
59
64
|
|
|
60
65
|
// Phase 1: Coordinator worker
|
|
61
66
|
const coordinatorWorker = new BVHWorker();
|
|
@@ -96,11 +101,21 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
96
101
|
settled = true;
|
|
97
102
|
log.warn( `parallel build failed (${reason}), falling back to single worker` );
|
|
98
103
|
cleanup();
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
|
|
107
|
+
// Copy from SharedArrayBuffer to regular ArrayBuffer for transfer
|
|
108
|
+
const restoredBuffer = new ArrayBuffer( sharedTriangleData.byteLength );
|
|
109
|
+
new Uint32Array( restoredBuffer ).set( new Uint32Array( sharedTriangleData ) );
|
|
110
|
+
const restoredTriangles = new Uint32Array( restoredBuffer );
|
|
111
|
+
resolve( buildSingleWorker( restoredTriangles, depth, progressCallback, config ) );
|
|
112
|
+
|
|
113
|
+
} catch ( error ) {
|
|
114
|
+
|
|
115
|
+
// Out of memory for the copy as well: fail the build rather than leave it waiting.
|
|
116
|
+
reject( error );
|
|
117
|
+
|
|
118
|
+
}
|
|
104
119
|
|
|
105
120
|
};
|
|
106
121
|
|
|
@@ -150,8 +165,11 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
150
165
|
|
|
151
166
|
settled = true;
|
|
152
167
|
cleanup();
|
|
153
|
-
|
|
154
|
-
|
|
168
|
+
resolve( {
|
|
169
|
+
bvhData: msg.bvhData, bvhRoot: true,
|
|
170
|
+
order: new Uint32Array( sharedIndices ), triangles: new Uint32Array( sharedTriangleData ),
|
|
171
|
+
originalToBvh: msg.originalToBvh || null, splitStats: phase1Stats || {}
|
|
172
|
+
} );
|
|
155
173
|
return;
|
|
156
174
|
|
|
157
175
|
}
|
|
@@ -215,7 +233,8 @@ async function handlePhase2(
|
|
|
215
233
|
sharedTriangleData,
|
|
216
234
|
sharedIndices,
|
|
217
235
|
sharedReorderBuffer,
|
|
218
|
-
triangleCount
|
|
236
|
+
triangleCount,
|
|
237
|
+
foldLeaves: config.foldLeaves
|
|
219
238
|
}, [ topFlatData.buffer ] );
|
|
220
239
|
return;
|
|
221
240
|
|
|
@@ -293,7 +312,8 @@ async function handlePhase2(
|
|
|
293
312
|
sharedTriangleData,
|
|
294
313
|
sharedIndices,
|
|
295
314
|
sharedReorderBuffer,
|
|
296
|
-
triangleCount
|
|
315
|
+
triangleCount,
|
|
316
|
+
foldLeaves: config.foldLeaves
|
|
297
317
|
}, transferables );
|
|
298
318
|
|
|
299
319
|
};
|
|
@@ -464,7 +484,8 @@ function buildSingleWorker( triangles, depth, progressCallback, config ) {
|
|
|
464
484
|
reportProgress: !! progressCallback,
|
|
465
485
|
sharedReorderBuffer,
|
|
466
486
|
treeletOptimization: config.treeletOptimization,
|
|
467
|
-
reinsertionOptimization: config.reinsertionOptimization
|
|
487
|
+
reinsertionOptimization: config.reinsertionOptimization,
|
|
488
|
+
foldLeaves: config.foldLeaves
|
|
468
489
|
}, [ transferBuffer ] );
|
|
469
490
|
|
|
470
491
|
} )().catch( ( error ) => {
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RGBAFormat, RedFormat, FloatType, LinearFilter, RepeatWrapping, ClampToEdgeWrapping,
|
|
3
|
+
EquirectangularReflectionMapping, LinearSRGBColorSpace, DataTexture, Vector3, Vector4,
|
|
4
|
+
} from 'three';
|
|
5
|
+
import { StorageInstancedBufferAttribute } from 'three/webgpu';
|
|
6
|
+
import { storage, uniform } from 'three/tsl';
|
|
7
|
+
|
|
8
|
+
import { gpuOnlyStorageAttribute } from '../TSL/patches.js';
|
|
9
|
+
import { freeStorageAttribute } from './PackedRayBuffer.js';
|
|
10
|
+
import {
|
|
11
|
+
TRANSMITTANCE_W, TRANSMITTANCE_H, MS_MU_S, MS_H, MS_BANDS, MS_SECTORS, MS_CELLS, MS_VIEW_MU, MS_VIEW_PHI, MS_VIEWS, IRRADIANCE_SIZE, SKY_COLUMNS,
|
|
12
|
+
buildTransmittanceKernel, buildIncomingKernel, buildScatterKernel, buildIrradianceKernel, buildSkyViewKernels,
|
|
13
|
+
} from '../TSL/Atmosphere.js';
|
|
14
|
+
import { buildEnvironmentCDFKernels, cdfRowStride } from '../TSL/EnvironmentCDF.js';
|
|
15
|
+
import { getWorkingMatrix } from '../Color/WorkingMatrix.js';
|
|
16
|
+
import {
|
|
17
|
+
LAMBDA_COUNT, MIE_PHASE_G, SKY_RADIANCE_SCALE, SOLAR_SPECTRUM,
|
|
18
|
+
atmosphereCoefficients, spectrumToRec709, sunLight, horizonDipSin, phaseCellWeights,
|
|
19
|
+
} from './AtmosphereModel.js';
|
|
20
|
+
|
|
21
|
+
// Filled on the GPU only: created without pixels, so three.js never uploads any.
|
|
22
|
+
function gpuTexture( width, height, format ) {
|
|
23
|
+
|
|
24
|
+
const texture = new DataTexture( null, width, height, format, FloatType );
|
|
25
|
+
texture.source.dataReady = false;
|
|
26
|
+
texture.needsUpdate = true;
|
|
27
|
+
texture._isPhysicalSky = true;
|
|
28
|
+
return texture;
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Physically based clear sky, baked on the GPU into an equirect and its importance-sampling
|
|
34
|
+
* table. The sun disc is not in the texture: `bake()` returns it as a light.
|
|
35
|
+
*/
|
|
36
|
+
export class PhysicalSky {
|
|
37
|
+
|
|
38
|
+
constructor( width = 1024, height = 512, quality = {} ) {
|
|
39
|
+
|
|
40
|
+
this.width = width;
|
|
41
|
+
this.height = height;
|
|
42
|
+
this._quality = quality;
|
|
43
|
+
this.lastRenderTime = 0;
|
|
44
|
+
|
|
45
|
+
this._texture = gpuTexture( width, height, RGBAFormat );
|
|
46
|
+
this._texture.mapping = EquirectangularReflectionMapping;
|
|
47
|
+
this._texture.colorSpace = LinearSRGBColorSpace;
|
|
48
|
+
this._texture.minFilter = LinearFilter;
|
|
49
|
+
this._texture.magFilter = LinearFilter;
|
|
50
|
+
this._texture.wrapS = RepeatWrapping;
|
|
51
|
+
this._texture.wrapT = ClampToEdgeWrapping;
|
|
52
|
+
this._texture.generateMipmaps = false;
|
|
53
|
+
this._cdfTexture = gpuTexture( width + 1, height, RedFormat );
|
|
54
|
+
this._rec709Weights = spectrumToRec709( SKY_RADIANCE_SCALE );
|
|
55
|
+
|
|
56
|
+
this._attrs = null;
|
|
57
|
+
this._kernels = null;
|
|
58
|
+
this._airKey = null;
|
|
59
|
+
this._transmittanceBuilt = false;
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_init() {
|
|
64
|
+
|
|
65
|
+
const vec4s = n => Array.from( { length: n }, () => uniform( new Vector4() ) );
|
|
66
|
+
const coefficient = vec4s( 6 * 4 );
|
|
67
|
+
this._uniforms = {
|
|
68
|
+
coefficient,
|
|
69
|
+
rgb: vec4s( 12 ),
|
|
70
|
+
sunDirection: uniform( new Vector3( 0, 1, 0 ) ),
|
|
71
|
+
altitude: uniform( 0.05 ),
|
|
72
|
+
sunSinRadius: uniform( 0.0046 ),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const attr = count => gpuOnlyStorageAttribute( count, 4 );
|
|
76
|
+
this._attrs = {
|
|
77
|
+
transmittance: attr( TRANSMITTANCE_W * TRANSMITTANCE_H ),
|
|
78
|
+
incomingA: attr( MS_MU_S * MS_H * MS_CELLS * 4 ),
|
|
79
|
+
incomingB: attr( MS_MU_S * MS_H * MS_CELLS * 4 ),
|
|
80
|
+
transfer: attr( MS_MU_S * MS_H * MS_CELLS * 4 ),
|
|
81
|
+
multipleScattering: attr( MS_MU_S * MS_H * MS_VIEWS * 8 ),
|
|
82
|
+
psiSum: attr( MS_MU_S * MS_H * MS_VIEWS * 8 ),
|
|
83
|
+
groundE: attr( MS_MU_S * 4 ),
|
|
84
|
+
irradiance: attr( IRRADIANCE_SIZE * 4 ),
|
|
85
|
+
sky: attr( this.width * this.height ),
|
|
86
|
+
skyRows: attr( SKY_COLUMNS * this.height ),
|
|
87
|
+
cdfRows: attr( this.height ),
|
|
88
|
+
cdfPrefix: gpuOnlyStorageAttribute( this.width * this.height, 2 ),
|
|
89
|
+
cdfStats: attr( 2 ),
|
|
90
|
+
cdf: gpuOnlyStorageAttribute( cdfRowStride( this.width ) * this.height, 1 ),
|
|
91
|
+
phaseCells: new StorageInstancedBufferAttribute( phaseCellWeights( MS_VIEW_MU, MS_VIEW_PHI, MS_BANDS, MS_SECTORS ), 4 ),
|
|
92
|
+
};
|
|
93
|
+
const node = ( key, type = 'vec4' ) => storage( this._attrs[ key ], type, this._attrs[ key ].count );
|
|
94
|
+
|
|
95
|
+
const slice = i => coefficient.slice( i * 4, i * 4 + 4 );
|
|
96
|
+
const io = {
|
|
97
|
+
transmittance: node( 'transmittance' ),
|
|
98
|
+
transfer: node( 'transfer' ),
|
|
99
|
+
psiSum: node( 'psiSum' ),
|
|
100
|
+
groundE: node( 'groundE' ),
|
|
101
|
+
phaseCells: node( 'phaseCells' ),
|
|
102
|
+
multipleScattering: node( 'multipleScattering' ),
|
|
103
|
+
irradiance: node( 'irradiance' ),
|
|
104
|
+
sky: node( 'sky' ),
|
|
105
|
+
skyRows: node( 'skyRows' ),
|
|
106
|
+
coefficients: {
|
|
107
|
+
rayleigh: slice( 0 ), mieScattering: slice( 1 ), mieExtinction: slice( 2 ),
|
|
108
|
+
ozone: slice( 3 ), solar: slice( 4 ), albedo: slice( 5 ),
|
|
109
|
+
},
|
|
110
|
+
rgb: this._uniforms.rgb,
|
|
111
|
+
sunDirection: this._uniforms.sunDirection,
|
|
112
|
+
altitude: this._uniforms.altitude,
|
|
113
|
+
sunSinRadius: this._uniforms.sunSinRadius,
|
|
114
|
+
mieG: MIE_PHASE_G,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const q = this._quality;
|
|
118
|
+
const incomingA = node( 'incomingA' ), incomingB = node( 'incomingB' );
|
|
119
|
+
this._kernels = {
|
|
120
|
+
transmittance: buildTransmittanceKernel( io ),
|
|
121
|
+
air: [
|
|
122
|
+
buildIncomingKernel( io, { first: true, out: incomingA, ...q.incoming } ),
|
|
123
|
+
buildScatterKernel( io, { incoming: incomingA, stage: 'first' } ),
|
|
124
|
+
buildIncomingKernel( io, { first: false, out: incomingB, ...q.incoming } ),
|
|
125
|
+
buildScatterKernel( io, { incoming: incomingB, stage: 'middle' } ),
|
|
126
|
+
buildIncomingKernel( io, { first: false, out: incomingA, ...q.incoming } ),
|
|
127
|
+
buildScatterKernel( io, { incoming: incomingA, stage: 'last' } ),
|
|
128
|
+
buildIrradianceKernel( io, q.irradiance ),
|
|
129
|
+
],
|
|
130
|
+
sky: buildSkyViewKernels( io, { width: this.width, height: this.height, ...q.sky } ),
|
|
131
|
+
cdf: buildEnvironmentCDFKernels( {
|
|
132
|
+
pixels: io.sky, rows: node( 'cdfRows' ), prefix: node( 'cdfPrefix', 'vec2' ), stats: node( 'cdfStats' ),
|
|
133
|
+
cdf: node( 'cdf', 'float' ), width: this.width, height: this.height,
|
|
134
|
+
} ),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
this._setSpectrum( 4, SOLAR_SPECTRUM );
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Straight into the working space: pixels on the GPU cannot be converted afterwards.
|
|
142
|
+
_setColorWeights() {
|
|
143
|
+
|
|
144
|
+
const w = this._rec709Weights;
|
|
145
|
+
const m = getWorkingMatrix();
|
|
146
|
+
const out = [ 0, 0, 0, 0 ];
|
|
147
|
+
for ( let row = 0; row < 3; row ++ ) {
|
|
148
|
+
|
|
149
|
+
for ( let g = 0; g < 4; g ++ ) {
|
|
150
|
+
|
|
151
|
+
for ( let j = 0; j < 4; j ++ ) {
|
|
152
|
+
|
|
153
|
+
const k = g * 4 + j;
|
|
154
|
+
out[ j ] = m
|
|
155
|
+
? m[ row * 3 ] * w[ k ] + m[ row * 3 + 1 ] * w[ LAMBDA_COUNT + k ] + m[ row * 3 + 2 ] * w[ 2 * LAMBDA_COUNT + k ]
|
|
156
|
+
: w[ row * LAMBDA_COUNT + k ];
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this._uniforms.rgb[ row * 4 + g ].value.fromArray( out );
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
_copyToTextures( renderer ) {
|
|
169
|
+
|
|
170
|
+
const backend = renderer.backend;
|
|
171
|
+
renderer.initTexture( this._texture );
|
|
172
|
+
renderer.initTexture( this._cdfTexture );
|
|
173
|
+
const encoder = backend.device.createCommandEncoder( { label: 'PhysicalSky' } );
|
|
174
|
+
encoder.copyBufferToTexture(
|
|
175
|
+
{ buffer: backend.get( this._attrs.sky ).buffer, bytesPerRow: this.width * 16, rowsPerImage: this.height },
|
|
176
|
+
{ texture: backend.get( this._texture ).texture },
|
|
177
|
+
[ this.width, this.height ],
|
|
178
|
+
);
|
|
179
|
+
encoder.copyBufferToTexture(
|
|
180
|
+
{ buffer: backend.get( this._attrs.cdf ).buffer, bytesPerRow: cdfRowStride( this.width ) * 4, rowsPerImage: this.height },
|
|
181
|
+
{ texture: backend.get( this._cdfTexture ).texture },
|
|
182
|
+
[ this.width + 1, this.height ],
|
|
183
|
+
);
|
|
184
|
+
backend.device.queue.submit( [ encoder.finish() ] );
|
|
185
|
+
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
_setSpectrum( slot, values ) {
|
|
189
|
+
|
|
190
|
+
for ( let g = 0; g < 4; g ++ ) {
|
|
191
|
+
|
|
192
|
+
this._uniforms.coefficient[ slot * 4 + g ].value.set( values[ g * 4 ], values[ g * 4 + 1 ], values[ g * 4 + 2 ], values[ g * 4 + 3 ] );
|
|
193
|
+
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Queue the bake on the GPU; the textures fill ahead of any frame submitted later. Only the
|
|
200
|
+
* table's two normalisers come back, in `stats`.
|
|
201
|
+
*
|
|
202
|
+
* @param {import('three/webgpu').WebGPURenderer} renderer - initialised
|
|
203
|
+
* @param {Object} p
|
|
204
|
+
* @param {number[]} p.sunDirection - unit, y up, in the sky's own frame
|
|
205
|
+
* @param {number} p.turbidity
|
|
206
|
+
* @param {number} p.ozone - Dobson units
|
|
207
|
+
* @param {number} p.airDensity
|
|
208
|
+
* @param {number[]} p.groundAlbedo - linear Rec.709
|
|
209
|
+
* @param {number} p.altitude - metres
|
|
210
|
+
* @param {number} p.sunAngularDiameter - radians
|
|
211
|
+
* @param {number} [p.sunStrength=1]
|
|
212
|
+
* @returns {{ texture: DataTexture, cdfTexture: DataTexture, sun: Object, stats: Promise<{ totalSum: number, compensationDelta: number }> }}
|
|
213
|
+
*/
|
|
214
|
+
bake( renderer, p ) {
|
|
215
|
+
|
|
216
|
+
const start = performance.now();
|
|
217
|
+
if ( ! this._kernels ) this._init();
|
|
218
|
+
|
|
219
|
+
const air = {
|
|
220
|
+
airDensity: p.airDensity, turbidity: p.turbidity, ozone: p.ozone,
|
|
221
|
+
groundAlbedo: [ ...p.groundAlbedo ],
|
|
222
|
+
};
|
|
223
|
+
const coefficients = atmosphereCoefficients( air );
|
|
224
|
+
const altitude = Math.max( p.altitude, 1 ) / 1000;
|
|
225
|
+
const halfAngle = p.sunAngularDiameter / 2;
|
|
226
|
+
|
|
227
|
+
const u = this._uniforms;
|
|
228
|
+
u.sunDirection.value.fromArray( p.sunDirection ).normalize();
|
|
229
|
+
u.altitude.value = altitude;
|
|
230
|
+
u.sunSinRadius.value = Math.sin( halfAngle );
|
|
231
|
+
this._setColorWeights();
|
|
232
|
+
|
|
233
|
+
const dispatches = [];
|
|
234
|
+
if ( ! this._transmittanceBuilt ) {
|
|
235
|
+
|
|
236
|
+
dispatches.push( this._kernels.transmittance );
|
|
237
|
+
this._transmittanceBuilt = true;
|
|
238
|
+
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const airKey = JSON.stringify( [ air, u.sunSinRadius.value ] );
|
|
242
|
+
if ( airKey !== this._airKey ) {
|
|
243
|
+
|
|
244
|
+
this._setSpectrum( 0, coefficients.rayleigh );
|
|
245
|
+
this._setSpectrum( 1, coefficients.mieScattering );
|
|
246
|
+
this._setSpectrum( 2, coefficients.mieExtinction );
|
|
247
|
+
this._setSpectrum( 3, coefficients.ozone );
|
|
248
|
+
this._setSpectrum( 5, coefficients.albedo );
|
|
249
|
+
dispatches.push( ...this._kernels.air );
|
|
250
|
+
this._airKey = airKey;
|
|
251
|
+
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
dispatches.push( ...this._kernels.sky, ...this._kernels.cdf );
|
|
255
|
+
renderer.compute( dispatches );
|
|
256
|
+
this._copyToTextures( renderer );
|
|
257
|
+
|
|
258
|
+
const stats = renderer.getArrayBufferAsync( this._attrs.cdfStats ).then( buffer => {
|
|
259
|
+
|
|
260
|
+
const s = new Float32Array( buffer );
|
|
261
|
+
return { totalSum: s[ 4 ], compensationDelta: s[ 5 ] };
|
|
262
|
+
|
|
263
|
+
} );
|
|
264
|
+
|
|
265
|
+
const sun = sunLight( {
|
|
266
|
+
direction: [ u.sunDirection.value.x, u.sunDirection.value.y, u.sunDirection.value.z ],
|
|
267
|
+
altitude,
|
|
268
|
+
angularDiameter: p.sunAngularDiameter,
|
|
269
|
+
strength: p.sunStrength ?? 1,
|
|
270
|
+
coefficients,
|
|
271
|
+
} );
|
|
272
|
+
sun.cosHalfAngle = Math.cos( halfAngle );
|
|
273
|
+
sun.sinHalfAngle = Math.sin( halfAngle );
|
|
274
|
+
sun.horizonSin = horizonDipSin( altitude );
|
|
275
|
+
|
|
276
|
+
this.lastRenderTime = performance.now() - start;
|
|
277
|
+
return { texture: this._texture, cdfTexture: this._cdfTexture, sun, stats };
|
|
278
|
+
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** The last bake's pixels (RGBA, row 0 first), packed table and stats — for tests and debugging. */
|
|
282
|
+
async readBack( renderer ) {
|
|
283
|
+
|
|
284
|
+
const [ pixels, cdf, stats ] = await Promise.all(
|
|
285
|
+
[ 'sky', 'cdf', 'cdfStats' ].map( key => renderer.getArrayBufferAsync( this._attrs[ key ] ) )
|
|
286
|
+
);
|
|
287
|
+
return { pixels: new Float32Array( pixels ), cdf: new Float32Array( cdf ), cdfStride: cdfRowStride( this.width ), stats: new Float32Array( stats ) };
|
|
288
|
+
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
getLastRenderTime() {
|
|
292
|
+
|
|
293
|
+
return this.lastRenderTime;
|
|
294
|
+
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
dispose( renderer = null ) {
|
|
298
|
+
|
|
299
|
+
for ( const k of Object.values( this._kernels ?? {} ).flat() ) k.dispose();
|
|
300
|
+
for ( const a of Object.values( this._attrs ?? {} ) ) freeStorageAttribute( renderer, a );
|
|
301
|
+
this._kernels = null;
|
|
302
|
+
this._attrs = null;
|
|
303
|
+
this._texture.dispose();
|
|
304
|
+
this._cdfTexture.dispose();
|
|
305
|
+
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
}
|
|
@@ -50,6 +50,7 @@ export const RAY_FLAG = {
|
|
|
50
50
|
HAS_HIT_OPAQUE: 1 << 16, // bit 16: ray chain has hit non-transmissive geometry (transparent-bg alpha; megakernel hasHitOpaqueSurface)
|
|
51
51
|
AUX_LOCKED: 1 << 17, // bit 17: OIDN aux (normal/albedo) locked onto first non-specular hit (megakernel auxLocked)
|
|
52
52
|
REDIRECTED: 1 << 18, // bit 18: ray has been redirected (refraction/reflection/SSS/opaque scatter) since the camera, so env it reaches is transported light (sharp), NOT the direct backdrop. NOT set by pure alpha/transparent passthrough (direction unchanged) → env through cutout holes is still the backdrop (blur/intensity/show/color/ground-projection). Set via bitOr only (positive mask) so it never disturbs ACTIVE/bounce bits.
|
|
53
|
+
SUN_NEE: 1 << 19, // bit 19: the last opaque scatter's sun NEE could have drawn this direction (MIS partner for a sun hit on miss)
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
export class QueueManager {
|