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
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
// Physical sky bake kernels; 16 spectral bins travel as four vec4s. The transmittance table holds
|
|
2
|
+
// density-weighted path lengths, so it depends only on the planet.
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Fn, float, int, uint, vec3, vec4,
|
|
6
|
+
If, Loop, Return,
|
|
7
|
+
instanceIndex,
|
|
8
|
+
exp, log, sqrt, max, min, clamp, abs, mix, step, floor, dot, sin, cos, tan, asin, acos, atan, smoothstep,
|
|
9
|
+
} from 'three/tsl';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
GROUND_RADIUS as RG, TOP_RADIUS as RT,
|
|
13
|
+
RAYLEIGH_SCALE_HEIGHT, MIE_SCALE_HEIGHT, OZONE_PEAK_ALTITUDE, OZONE_HALF_WIDTH,
|
|
14
|
+
} from '../Processor/AtmosphereModel.js';
|
|
15
|
+
|
|
16
|
+
export const TRANSMITTANCE_W = 256;
|
|
17
|
+
export const TRANSMITTANCE_H = 64;
|
|
18
|
+
// Multiple-scattering tables over sun elevation × altitude, then incoming cells (zenith bands
|
|
19
|
+
// uniform in cos × azimuth sectors from the sun over [0, π]) or viewing directions.
|
|
20
|
+
export const MS_MU_S = 64;
|
|
21
|
+
export const MS_H = 16;
|
|
22
|
+
export const MS_BANDS = 16;
|
|
23
|
+
export const MS_SECTORS = 8;
|
|
24
|
+
export const MS_CELLS = MS_BANDS * MS_SECTORS;
|
|
25
|
+
export const MS_VIEW_MU = 16;
|
|
26
|
+
export const MS_VIEW_PHI = 8;
|
|
27
|
+
export const MS_VIEWS = MS_VIEW_MU * MS_VIEW_PHI;
|
|
28
|
+
export const IRRADIANCE_SIZE = 128;
|
|
29
|
+
|
|
30
|
+
const H_TOP = Math.sqrt( RT * RT - RG * RG );
|
|
31
|
+
const SHELL = RT - RG;
|
|
32
|
+
const PI = Math.PI;
|
|
33
|
+
const G4 = [ 0, 1, 2, 3 ];
|
|
34
|
+
|
|
35
|
+
const spectrum = f => G4.map( f );
|
|
36
|
+
|
|
37
|
+
function densities( h ) {
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
r: exp( h.mul( - 1 / RAYLEIGH_SCALE_HEIGHT ) ),
|
|
41
|
+
m: exp( h.mul( - 1 / MIE_SCALE_HEIGHT ) ),
|
|
42
|
+
o: max( float( 0 ), float( 1 ).sub( abs( h.sub( OZONE_PEAK_ALTITUDE ) ).div( OZONE_HALF_WIDTH ) ) ),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function extinction( c, d ) {
|
|
48
|
+
|
|
49
|
+
return spectrum( g => c.rayleigh[ g ].mul( d.r ).add( c.mieExtinction[ g ].mul( d.m ) ).add( c.ozone[ g ].mul( d.o ) ) );
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Energy-conserving segment weight ∫₀^dt e^{−σx} dx = dt·(1 − e^{−σdt})/(σdt), stable as σdt → 0.
|
|
54
|
+
function segment( sigmaT, dt ) {
|
|
55
|
+
|
|
56
|
+
return spectrum( g => {
|
|
57
|
+
|
|
58
|
+
const x = sigmaT[ g ].mul( dt );
|
|
59
|
+
const e = exp( x.negate() );
|
|
60
|
+
const big = float( 1 ).sub( e ).div( max( x, 1e-12 ) );
|
|
61
|
+
const small = float( 1 ).sub( x.mul( 0.5 ) );
|
|
62
|
+
return { weight: mix( small, big, step( 1e-3, x ) ).mul( dt ), transmittance: e };
|
|
63
|
+
|
|
64
|
+
} );
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function bilinear( buffer, width, height, fx, fy, stride = 1, offset = 0 ) {
|
|
69
|
+
|
|
70
|
+
const x = clamp( fx, 0, width - 1 );
|
|
71
|
+
const y = clamp( fy, 0, height - 1 );
|
|
72
|
+
const x0 = floor( x ), y0 = floor( y );
|
|
73
|
+
const tx = x.sub( x0 ), ty = y.sub( y0 );
|
|
74
|
+
const ix0 = uint( x0 ), iy0 = uint( y0 );
|
|
75
|
+
const ix1 = min( ix0.add( 1 ), uint( width - 1 ) );
|
|
76
|
+
const iy1 = min( iy0.add( 1 ), uint( height - 1 ) );
|
|
77
|
+
const at = ( ix, iy ) => buffer.element( iy.mul( width ).add( ix ).mul( stride ).add( offset ) );
|
|
78
|
+
return mix( mix( at( ix0, iy0 ), at( ix1, iy0 ), tx ), mix( at( ix0, iy1 ), at( ix1, iy1 ), tx ), ty );
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Bruneton's (x_mu, x_r) parameterisation, from altitude so low heights keep their precision.
|
|
83
|
+
function transmittanceCoord( h, mu ) {
|
|
84
|
+
|
|
85
|
+
const r = h.add( RG );
|
|
86
|
+
const rho = sqrt( max( h.mul( h.add( 2 * RG ) ), 0 ) );
|
|
87
|
+
const rmu = r.mul( mu );
|
|
88
|
+
const dTop = rmu.negate().add( sqrt( max( rmu.mul( rmu ).add( H_TOP * H_TOP ).sub( rho.mul( rho ) ), 0 ) ) );
|
|
89
|
+
const dMin = float( SHELL ).sub( h );
|
|
90
|
+
const dMax = rho.add( H_TOP );
|
|
91
|
+
return {
|
|
92
|
+
x: dTop.sub( dMin ).div( max( dMax.sub( dMin ), 1e-6 ) ).mul( TRANSMITTANCE_W - 1 ),
|
|
93
|
+
y: rho.div( H_TOP ).mul( TRANSMITTANCE_H - 1 ),
|
|
94
|
+
rho, r,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Transmittance from altitude `h` towards the sun at zenith cosine `muS`, faded out as the disc
|
|
101
|
+
* sinks behind the planet.
|
|
102
|
+
*/
|
|
103
|
+
function sunTransmittance( lut, c, h, muS, sunSinRadius ) {
|
|
104
|
+
|
|
105
|
+
const tc = transmittanceCoord( h, muS );
|
|
106
|
+
const depth = bilinear( lut, TRANSMITTANCE_W, TRANSMITTANCE_H, tc.x, tc.y ).xyz;
|
|
107
|
+
const visible = smoothstep( sunSinRadius.negate(), sunSinRadius, muS.add( tc.rho.div( tc.r ) ) );
|
|
108
|
+
return spectrum( g => exp( c.rayleigh[ g ].mul( depth.x ).add( c.mieExtinction[ g ].mul( depth.y ) ).add( c.ozone[ g ].mul( depth.z ) ).negate() ).mul( visible ) );
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Altitude on a squared axis: the air that scatters most is packed into the first few km.
|
|
113
|
+
const msAltitude = j => float( j ).div( MS_H - 1 ).pow( 2 ).mul( SHELL );
|
|
114
|
+
const msAltitudeCoord = h => sqrt( clamp( h.div( SHELL ), 0, 1 ) ).mul( MS_H - 1 );
|
|
115
|
+
|
|
116
|
+
// Sun elevation on an arctangent axis centred just below the horizon: through twilight the
|
|
117
|
+
// scattered light falls by orders of magnitude per few degrees, and it is interpolated in log.
|
|
118
|
+
const SUN_CENTRE = - 3 * PI / 180;
|
|
119
|
+
const SUN_WIDTH = 15 * PI / 180;
|
|
120
|
+
const SUN_U0 = 0.5 + Math.atan( ( - PI / 2 - SUN_CENTRE ) / SUN_WIDTH ) / PI;
|
|
121
|
+
const SUN_U1 = 0.5 + Math.atan( ( PI / 2 - SUN_CENTRE ) / SUN_WIDTH ) / PI;
|
|
122
|
+
const sunAxis = ( i, n ) => sin( clamp( tan( float( i ).div( n - 1 ).mul( SUN_U1 - SUN_U0 ).add( SUN_U0 - 0.5 ).mul( PI ) ).mul( SUN_WIDTH ).add( SUN_CENTRE ), - PI / 2, PI / 2 ) );
|
|
123
|
+
const sunCoord = ( muS, n ) => atan( asin( clamp( muS, - 1, 1 ) ).sub( SUN_CENTRE ).div( SUN_WIDTH ) ).div( PI ).add( 0.5 - SUN_U0 ).div( SUN_U1 - SUN_U0 ).mul( n - 1 );
|
|
124
|
+
const LOG_FLOOR = 1e-30;
|
|
125
|
+
const toLog = v => log( max( v, vec4( LOG_FLOOR ) ) );
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Ψ for Rayleigh (0–3) and Mie (4–7) toward a view of zenith cosine `muV` and cosine of azimuth
|
|
129
|
+
* from the sun `cosPhi`, interpolated in log over all four axes.
|
|
130
|
+
*/
|
|
131
|
+
function sampleMultipleScattering( psi, h, muS, muV, cosPhi ) {
|
|
132
|
+
|
|
133
|
+
const axes = [
|
|
134
|
+
[ clamp( sunCoord( muS, MS_MU_S ), 0, MS_MU_S - 1 ), MS_MU_S ],
|
|
135
|
+
[ clamp( msAltitudeCoord( h ), 0, MS_H - 1 ), MS_H ],
|
|
136
|
+
[ clamp( muV.add( 1 ).mul( 0.5 * ( MS_VIEW_MU - 1 ) ), 0, MS_VIEW_MU - 1 ), MS_VIEW_MU ],
|
|
137
|
+
[ clamp( acos( clamp( cosPhi, - 1, 1 ) ).mul( ( MS_VIEW_PHI - 1 ) / PI ), 0, MS_VIEW_PHI - 1 ), MS_VIEW_PHI ],
|
|
138
|
+
].map( ( [ f, n ] ) => {
|
|
139
|
+
|
|
140
|
+
const i0 = floor( f );
|
|
141
|
+
const t = f.sub( i0 ).toVar();
|
|
142
|
+
const a0 = uint( i0 ).toVar();
|
|
143
|
+
return { t, i0: a0, i1: min( a0.add( 1 ), uint( n - 1 ) ).toVar() };
|
|
144
|
+
|
|
145
|
+
} );
|
|
146
|
+
|
|
147
|
+
// Row-major (h, μs, µv, φ): stride of each axis in texels.
|
|
148
|
+
const strides = [ MS_VIEWS, MS_MU_S * MS_VIEWS, MS_VIEW_PHI, 1 ];
|
|
149
|
+
const corners = [];
|
|
150
|
+
for ( let k = 0; k < 16; k ++ ) {
|
|
151
|
+
|
|
152
|
+
let index = uint( 0 );
|
|
153
|
+
axes.forEach( ( axis, a ) => {
|
|
154
|
+
|
|
155
|
+
index = index.add( ( ( k >> a ) & 1 ? axis.i1 : axis.i0 ).mul( strides[ a ] ) );
|
|
156
|
+
|
|
157
|
+
} );
|
|
158
|
+
corners.push( index.mul( 8 ).toVar() );
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return Array.from( { length: 8 }, ( _, c ) => {
|
|
163
|
+
|
|
164
|
+
let level = corners.map( base => psi.element( base.add( c ) ) );
|
|
165
|
+
for ( let a = 0; a < 4; a ++ ) {
|
|
166
|
+
|
|
167
|
+
const next = [];
|
|
168
|
+
for ( let k = 0; k < level.length; k += 2 ) next.push( mix( level[ k ], level[ k + 1 ], axes[ a ].t ) );
|
|
169
|
+
level = next;
|
|
170
|
+
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return exp( level[ 0 ] );
|
|
174
|
+
|
|
175
|
+
} );
|
|
176
|
+
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function skyIrradiance( irrLUT, muS ) {
|
|
180
|
+
|
|
181
|
+
const fx = sunCoord( muS, IRRADIANCE_SIZE );
|
|
182
|
+
return spectrum( g => exp( bilinear( irrLUT, IRRADIANCE_SIZE, 1, fx, float( 0 ), 4, g ) ) );
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Ray from altitude h along zenith cosine mu: distance to the ground (if it hits) or to the top.
|
|
187
|
+
function rayExtent( h, mu ) {
|
|
188
|
+
|
|
189
|
+
const r = h.add( RG ).toVar();
|
|
190
|
+
const b = r.mul( mu ).toVar();
|
|
191
|
+
const cGround = h.mul( h.add( 2 * RG ) ).toVar();
|
|
192
|
+
const discGround = b.mul( b ).sub( cGround );
|
|
193
|
+
const hitsGround = mu.lessThan( 0 ).and( discGround.greaterThanEqual( 0 ) ).toVar();
|
|
194
|
+
const tGround = cGround.div( max( b.negate().add( sqrt( max( discGround, 0 ) ) ), 1e-9 ) );
|
|
195
|
+
const tTop = b.negate().add( sqrt( max( b.mul( b ).add( float( RT ).sub( r ).mul( r.add( RT ) ) ), 0 ) ) );
|
|
196
|
+
return { r, b, cGround, hitsGround, tMax: hitsGround.select( tGround, tTop ).toVar() };
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Cosine of a direction's azimuth from the sun, in the local horizontal plane.
|
|
201
|
+
const azimuthCos = ( nu, mu, muS ) => nu.sub( mu.mul( muS ) ).div( max( sqrt( float( 1 ).sub( mu.mul( mu ) ).mul( float( 1 ).sub( muS.mul( muS ) ) ) ), 1e-4 ) );
|
|
202
|
+
|
|
203
|
+
// Altitude, sun zenith cosine and the ray's own zenith cosine at distance t along the ray.
|
|
204
|
+
function alongRay( ray, t, muS0, nu ) {
|
|
205
|
+
|
|
206
|
+
const q = t.mul( t.add( ray.b.mul( 2 ) ) );
|
|
207
|
+
const rt = sqrt( ray.r.mul( ray.r ).add( q ) );
|
|
208
|
+
const h = max( ray.cGround.add( q ).div( rt.add( RG ) ), 0 );
|
|
209
|
+
return {
|
|
210
|
+
h,
|
|
211
|
+
muS: clamp( ray.r.mul( muS0 ).add( t.mul( nu ) ).div( rt ), - 1, 1 ),
|
|
212
|
+
mu: clamp( ray.b.add( t ).div( rt ), - 1, 1 ),
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const rayleighPhase = nu => nu.mul( nu ).add( 1 ).mul( 3 / ( 16 * PI ) );
|
|
218
|
+
|
|
219
|
+
// Cornette-Shanks.
|
|
220
|
+
function miePhase( nu, g ) {
|
|
221
|
+
|
|
222
|
+
const g2 = g * g;
|
|
223
|
+
const k = 3 / ( 8 * PI ) * ( 1 - g2 ) / ( 2 + g2 );
|
|
224
|
+
const denom = max( float( 1 + g2 ).sub( nu.mul( 2 * g ) ), 1e-6 );
|
|
225
|
+
return nu.mul( nu ).add( 1 ).mul( k ).div( denom.mul( sqrt( denom ) ) );
|
|
226
|
+
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {Object} io
|
|
231
|
+
* @param {Node} io.transmittance - storage vec4, TRANSMITTANCE_W × TRANSMITTANCE_H
|
|
232
|
+
*/
|
|
233
|
+
export function buildTransmittanceKernel( { transmittance }, steps = 256 ) {
|
|
234
|
+
|
|
235
|
+
const count = TRANSMITTANCE_W * TRANSMITTANCE_H;
|
|
236
|
+
|
|
237
|
+
return Fn( () => {
|
|
238
|
+
|
|
239
|
+
const idx = instanceIndex;
|
|
240
|
+
If( idx.greaterThanEqual( uint( count ) ), () => {
|
|
241
|
+
|
|
242
|
+
Return();
|
|
243
|
+
|
|
244
|
+
} );
|
|
245
|
+
|
|
246
|
+
const xMu = float( idx.mod( TRANSMITTANCE_W ) ).div( TRANSMITTANCE_W - 1 );
|
|
247
|
+
const xR = float( idx.div( TRANSMITTANCE_W ) ).div( TRANSMITTANCE_H - 1 );
|
|
248
|
+
const rho = xR.mul( H_TOP ).toVar();
|
|
249
|
+
const r = sqrt( rho.mul( rho ).add( RG * RG ) ).toVar();
|
|
250
|
+
const dMin = float( RT ).sub( r );
|
|
251
|
+
const dMax = rho.add( H_TOP );
|
|
252
|
+
const d = dMin.add( xMu.mul( dMax.sub( dMin ) ) ).toVar();
|
|
253
|
+
const mu = clamp( float( H_TOP * H_TOP ).sub( rho.mul( rho ) ).sub( d.mul( d ) ).div( max( r.mul( d ).mul( 2 ), 1e-9 ) ), - 1, 1 ).toVar();
|
|
254
|
+
const rho2 = rho.mul( rho ).toVar();
|
|
255
|
+
const rmu2 = r.mul( mu ).mul( 2 ).toVar();
|
|
256
|
+
|
|
257
|
+
const depth = vec3( 0 ).toVar();
|
|
258
|
+
Loop( { start: int( 0 ), end: int( steps ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
259
|
+
|
|
260
|
+
const f0 = float( i ).div( steps );
|
|
261
|
+
const f1 = float( i ).add( 1 ).div( steps );
|
|
262
|
+
const t0 = d.mul( f0.mul( f0 ) );
|
|
263
|
+
const t1 = d.mul( f1.mul( f1 ) );
|
|
264
|
+
const t = t0.add( t1 ).mul( 0.5 );
|
|
265
|
+
const q = t.mul( t.add( rmu2 ) );
|
|
266
|
+
const h = max( rho2.add( q ).div( sqrt( r.mul( r ).add( q ) ).add( RG ) ), 0 );
|
|
267
|
+
const dens = densities( h );
|
|
268
|
+
depth.addAssign( vec3( dens.r, dens.m, dens.o ).mul( t1.sub( t0 ) ) );
|
|
269
|
+
|
|
270
|
+
} );
|
|
271
|
+
|
|
272
|
+
transmittance.element( idx ).assign( vec4( depth, 0 ) );
|
|
273
|
+
|
|
274
|
+
} )().compute( count, [ 64 ] );
|
|
275
|
+
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Cells are π/32 sr each; a band's cells share its centre cosine.
|
|
279
|
+
const CELL_SOLID_ANGLE = 4 * PI / MS_CELLS;
|
|
280
|
+
|
|
281
|
+
// Direction of ray `k` of a cell's `samples`², azimuth from the sun in the plane of +x.
|
|
282
|
+
function cellRay( band, sector, k, samples ) {
|
|
283
|
+
|
|
284
|
+
const u = float( band ).add( float( k.div( samples ) ).add( 0.5 ).div( samples ) ).div( MS_BANDS );
|
|
285
|
+
const cosT = u.mul( 2 ).sub( 1 );
|
|
286
|
+
const sinT = sqrt( max( float( 1 ).sub( cosT.mul( cosT ) ), 0 ) );
|
|
287
|
+
const phi = float( sector ).add( float( k.mod( samples ) ).add( 0.5 ).div( samples ) ).mul( PI / MS_SECTORS );
|
|
288
|
+
return vec3( sinT.mul( cos( phi ) ), cosT, sinT.mul( sin( phi ) ) );
|
|
289
|
+
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Light of one scattering order arriving at the ground, per unit solar irradiance.
|
|
293
|
+
function groundIrradiance( groundE, muS ) {
|
|
294
|
+
|
|
295
|
+
const fx = sunCoord( muS, MS_MU_S );
|
|
296
|
+
return spectrum( g => bilinear( groundE, MS_MU_S, 1, fx, float( 0 ), 4, g ) );
|
|
297
|
+
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* One scattering order's radiance arriving at each (μs, h) from each incoming cell, per unit solar
|
|
302
|
+
* irradiance; order n + 1 scatters order n's Ψ and ground light. Order 1 also writes f_ms per cell.
|
|
303
|
+
*/
|
|
304
|
+
export function buildIncomingKernel( io, { first, out, cellSamples = 2, steps = 20 } ) {
|
|
305
|
+
|
|
306
|
+
const { transmittance, transfer, multipleScattering: psi, groundE, coefficients: c, sunSinRadius, mieG } = io;
|
|
307
|
+
const count = MS_MU_S * MS_H * MS_CELLS;
|
|
308
|
+
const rays = cellSamples * cellSamples;
|
|
309
|
+
|
|
310
|
+
return Fn( () => {
|
|
311
|
+
|
|
312
|
+
const idx = instanceIndex;
|
|
313
|
+
If( idx.greaterThanEqual( uint( count ) ), () => {
|
|
314
|
+
|
|
315
|
+
Return();
|
|
316
|
+
|
|
317
|
+
} );
|
|
318
|
+
|
|
319
|
+
const cell = idx.mod( MS_CELLS );
|
|
320
|
+
const texel = idx.div( MS_CELLS );
|
|
321
|
+
const muS = sunAxis( texel.mod( MS_MU_S ), MS_MU_S ).toVar();
|
|
322
|
+
const h0 = max( msAltitude( texel.div( MS_MU_S ) ), 1e-3 ).toVar();
|
|
323
|
+
const sun = vec3( sqrt( max( float( 1 ).sub( muS.mul( muS ) ), 0 ) ), muS, 0 ).toVar();
|
|
324
|
+
|
|
325
|
+
const L = spectrum( () => vec4( 0 ).toVar() );
|
|
326
|
+
const F = first ? spectrum( () => vec4( 0 ).toVar() ) : null;
|
|
327
|
+
|
|
328
|
+
Loop( { start: int( 0 ), end: int( rays ), type: 'int', condition: '<', name: 'ray' }, ( { ray: k } ) => {
|
|
329
|
+
|
|
330
|
+
const dir = cellRay( cell.div( MS_SECTORS ), cell.mod( MS_SECTORS ), k, cellSamples ).toVar();
|
|
331
|
+
const nu = dot( dir, sun ).toVar();
|
|
332
|
+
const phaseR = first ? rayleighPhase( nu ).toVar() : null;
|
|
333
|
+
const phaseM = first ? miePhase( nu, mieG ).toVar() : null;
|
|
334
|
+
const held = first ? null : Array.from( { length: 8 }, () => vec4( 0 ).toVar() );
|
|
335
|
+
|
|
336
|
+
const ray = rayExtent( h0, dir.y );
|
|
337
|
+
const throughput = spectrum( () => vec4( 1 ).toVar() );
|
|
338
|
+
|
|
339
|
+
Loop( { start: int( 0 ), end: int( steps ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
340
|
+
|
|
341
|
+
const f0 = float( i ).div( steps );
|
|
342
|
+
const f1 = float( i ).add( 1 ).div( steps );
|
|
343
|
+
const t0 = ray.tMax.mul( f0.mul( f0 ) );
|
|
344
|
+
const dt = ray.tMax.mul( f1.mul( f1 ) ).sub( t0 );
|
|
345
|
+
const at = alongRay( ray, t0.add( dt.mul( 0.5 ) ), muS, nu );
|
|
346
|
+
const dens = densities( at.h );
|
|
347
|
+
const sR = spectrum( g => c.rayleigh[ g ].mul( dens.r ) );
|
|
348
|
+
const sM = spectrum( g => c.mieScattering[ g ].mul( dens.m ) );
|
|
349
|
+
const seg = segment( extinction( c, dens ), dt );
|
|
350
|
+
|
|
351
|
+
let source;
|
|
352
|
+
if ( first ) {
|
|
353
|
+
|
|
354
|
+
const tSun = sunTransmittance( transmittance, c, at.h, at.muS, sunSinRadius );
|
|
355
|
+
source = spectrum( g => tSun[ g ].mul( sR[ g ].mul( phaseR ).add( sM[ g ].mul( phaseM ) ) ) );
|
|
356
|
+
|
|
357
|
+
} else {
|
|
358
|
+
|
|
359
|
+
If( i.mod( 2 ).equal( 0 ), () => {
|
|
360
|
+
|
|
361
|
+
const next = sampleMultipleScattering( psi, at.h, at.muS, at.mu, azimuthCos( nu, at.mu, at.muS ) );
|
|
362
|
+
for ( let j = 0; j < 8; j ++ ) held[ j ].assign( next[ j ] );
|
|
363
|
+
|
|
364
|
+
} );
|
|
365
|
+
source = spectrum( g => sR[ g ].mul( held[ g ] ).add( sM[ g ].mul( held[ 4 + g ] ) ) );
|
|
366
|
+
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
for ( const g of G4 ) {
|
|
370
|
+
|
|
371
|
+
L[ g ].addAssign( throughput[ g ].mul( source[ g ] ).mul( seg[ g ].weight ) );
|
|
372
|
+
if ( F ) F[ g ].addAssign( throughput[ g ].mul( sR[ g ].add( sM[ g ] ) ).mul( seg[ g ].weight ) );
|
|
373
|
+
throughput[ g ].mulAssign( seg[ g ].transmittance );
|
|
374
|
+
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
} );
|
|
378
|
+
|
|
379
|
+
If( ray.hitsGround, () => {
|
|
380
|
+
|
|
381
|
+
const muG = clamp( ray.r.mul( muS ).add( ray.tMax.mul( nu ) ).div( RG ), - 1, 1 );
|
|
382
|
+
let lit;
|
|
383
|
+
if ( first ) {
|
|
384
|
+
|
|
385
|
+
const tSun = sunTransmittance( transmittance, c, float( 0 ), muG, sunSinRadius );
|
|
386
|
+
lit = spectrum( g => tSun[ g ].mul( max( muG, 0 ) ) );
|
|
387
|
+
|
|
388
|
+
} else {
|
|
389
|
+
|
|
390
|
+
lit = groundIrradiance( groundE, muG );
|
|
391
|
+
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
for ( const g of G4 ) L[ g ].addAssign( throughput[ g ].mul( lit[ g ] ).mul( c.albedo[ g ] ).mul( 1 / PI ) );
|
|
395
|
+
|
|
396
|
+
} );
|
|
397
|
+
|
|
398
|
+
} );
|
|
399
|
+
|
|
400
|
+
for ( const g of G4 ) {
|
|
401
|
+
|
|
402
|
+
out.element( idx.mul( 4 ).add( g ) ).assign( L[ g ].div( rays ) );
|
|
403
|
+
if ( F ) transfer.element( idx.mul( 4 ).add( g ) ).assign( F[ g ].div( rays ) );
|
|
404
|
+
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
} )().compute( count, [ 64 ] );
|
|
408
|
+
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Ψₙ(μs, h, view) for Rayleigh and Mie, in log, summed in `psiSum`; the last stage adds the tail
|
|
413
|
+
* Ψₙ·f/(1 − f). Earlier stages also write order n's ground irradiance.
|
|
414
|
+
*/
|
|
415
|
+
export function buildScatterKernel( io, { incoming, stage } ) {
|
|
416
|
+
|
|
417
|
+
const { transfer, phaseCells, multipleScattering: psi, psiSum, groundE } = io;
|
|
418
|
+
const count = MS_MU_S * MS_H * MS_VIEWS;
|
|
419
|
+
const groups = MS_CELLS / 4;
|
|
420
|
+
const last = stage === 'last';
|
|
421
|
+
|
|
422
|
+
return Fn( () => {
|
|
423
|
+
|
|
424
|
+
const idx = instanceIndex;
|
|
425
|
+
If( idx.greaterThanEqual( uint( count ) ), () => {
|
|
426
|
+
|
|
427
|
+
Return();
|
|
428
|
+
|
|
429
|
+
} );
|
|
430
|
+
|
|
431
|
+
const view = idx.mod( MS_VIEWS );
|
|
432
|
+
const texel = idx.div( MS_VIEWS );
|
|
433
|
+
const cells = texel.mul( MS_CELLS );
|
|
434
|
+
|
|
435
|
+
let tail = null;
|
|
436
|
+
if ( last ) {
|
|
437
|
+
|
|
438
|
+
const f = spectrum( () => vec4( 0 ).toVar() );
|
|
439
|
+
Loop( { start: int( 0 ), end: int( MS_CELLS ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
440
|
+
|
|
441
|
+
for ( const g of G4 ) f[ g ].addAssign( transfer.element( cells.add( uint( i ) ).mul( 4 ).add( g ) ) );
|
|
442
|
+
|
|
443
|
+
} );
|
|
444
|
+
tail = spectrum( g => {
|
|
445
|
+
|
|
446
|
+
const fg = min( f[ g ].div( MS_CELLS ), 0.95 );
|
|
447
|
+
return fg.div( float( 1 ).sub( fg ) ).toVar();
|
|
448
|
+
|
|
449
|
+
} );
|
|
450
|
+
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
for ( let lobe = 0; lobe < 2; lobe ++ ) {
|
|
454
|
+
|
|
455
|
+
const acc = spectrum( () => vec4( 0 ).toVar() );
|
|
456
|
+
Loop( { start: int( 0 ), end: int( groups ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
457
|
+
|
|
458
|
+
const w = phaseCells.element( uint( lobe * MS_VIEWS ).add( view ).mul( groups ).add( uint( i ) ) ).toVar();
|
|
459
|
+
for ( let k = 0; k < 4; k ++ ) {
|
|
460
|
+
|
|
461
|
+
const cellIdx = cells.add( uint( i ).mul( 4 ).add( k ) ).mul( 4 );
|
|
462
|
+
const wk = w.element( int( k ) );
|
|
463
|
+
for ( const g of G4 ) acc[ g ].addAssign( incoming.element( cellIdx.add( g ) ).mul( wk ) );
|
|
464
|
+
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
} );
|
|
468
|
+
|
|
469
|
+
for ( const g of G4 ) {
|
|
470
|
+
|
|
471
|
+
const slot = idx.mul( 8 ).add( lobe * 4 + g );
|
|
472
|
+
if ( stage === 'first' ) {
|
|
473
|
+
|
|
474
|
+
psiSum.element( slot ).assign( acc[ g ] );
|
|
475
|
+
psi.element( slot ).assign( toLog( acc[ g ] ) );
|
|
476
|
+
|
|
477
|
+
} else if ( stage === 'middle' ) {
|
|
478
|
+
|
|
479
|
+
psiSum.element( slot ).addAssign( acc[ g ] );
|
|
480
|
+
psi.element( slot ).assign( toLog( acc[ g ] ) );
|
|
481
|
+
|
|
482
|
+
} else {
|
|
483
|
+
|
|
484
|
+
psi.element( slot ).assign( toLog( psiSum.element( slot ).add( acc[ g ].mul( tail[ g ].add( 1 ) ) ) ) );
|
|
485
|
+
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if ( ! last ) {
|
|
493
|
+
|
|
494
|
+
// One thread per sun elevation sums the upper cells at the ground row.
|
|
495
|
+
If( texel.lessThan( uint( MS_MU_S ) ).and( view.equal( uint( 0 ) ) ), () => {
|
|
496
|
+
|
|
497
|
+
const E = spectrum( () => vec4( 0 ).toVar() );
|
|
498
|
+
Loop( { start: int( MS_CELLS / 2 ), end: int( MS_CELLS ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
499
|
+
|
|
500
|
+
const band = uint( i ).div( MS_SECTORS );
|
|
501
|
+
const cosT = float( band ).add( 0.5 ).div( MS_BANDS ).mul( 2 ).sub( 1 );
|
|
502
|
+
for ( const g of G4 ) E[ g ].addAssign( incoming.element( cells.add( uint( i ) ).mul( 4 ).add( g ) ).mul( cosT ) );
|
|
503
|
+
|
|
504
|
+
} );
|
|
505
|
+
for ( const g of G4 ) groundE.element( texel.mul( 4 ).add( g ) ).assign( E[ g ].mul( CELL_SOLID_ANGLE ) );
|
|
506
|
+
|
|
507
|
+
} );
|
|
508
|
+
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
} )().compute( count, [ 64 ] );
|
|
512
|
+
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Single plus multiple scattering along one view ray, per unit solar irradiance. Ψ is smooth
|
|
516
|
+
// along the ray, so it is looked up every MS_STRIDE steps and held in between.
|
|
517
|
+
const MS_STRIDE = 4;
|
|
518
|
+
|
|
519
|
+
function inscatter( { transmittance, multipleScattering, coefficients: c, sunSinRadius, mieG }, ray, muS0, nu, steps ) {
|
|
520
|
+
|
|
521
|
+
const L = spectrum( () => vec4( 0 ).toVar() );
|
|
522
|
+
const throughput = spectrum( () => vec4( 1 ).toVar() );
|
|
523
|
+
const psi = multipleScattering ? Array.from( { length: 8 }, () => vec4( 0 ).toVar() ) : null;
|
|
524
|
+
const phaseR = rayleighPhase( nu ).toVar();
|
|
525
|
+
const phaseM = miePhase( nu, mieG ).toVar();
|
|
526
|
+
const tMax = ray.tMax;
|
|
527
|
+
|
|
528
|
+
Loop( { start: int( 0 ), end: int( steps ), type: 'int', condition: '<' }, ( { i } ) => {
|
|
529
|
+
|
|
530
|
+
// Squared spacing puts the samples where the air is densest, next to the viewer.
|
|
531
|
+
const f0 = float( i ).div( steps );
|
|
532
|
+
const f1 = float( i ).add( 1 ).div( steps );
|
|
533
|
+
const t0 = tMax.mul( f0.mul( f0 ) );
|
|
534
|
+
const t1 = tMax.mul( f1.mul( f1 ) );
|
|
535
|
+
const dt = t1.sub( t0 );
|
|
536
|
+
const t = t0.add( dt.mul( 0.5 ) );
|
|
537
|
+
|
|
538
|
+
const at = alongRay( ray, t, muS0, nu );
|
|
539
|
+
const dens = densities( at.h );
|
|
540
|
+
const sR = spectrum( g => c.rayleigh[ g ].mul( dens.r ) );
|
|
541
|
+
const sM = spectrum( g => c.mieScattering[ g ].mul( dens.m ) );
|
|
542
|
+
const tSun = sunTransmittance( transmittance, c, at.h, at.muS, sunSinRadius );
|
|
543
|
+
const seg = segment( extinction( c, dens ), dt );
|
|
544
|
+
|
|
545
|
+
if ( psi ) {
|
|
546
|
+
|
|
547
|
+
If( i.mod( MS_STRIDE ).equal( 0 ), () => {
|
|
548
|
+
|
|
549
|
+
const next = sampleMultipleScattering( multipleScattering, at.h, at.muS, at.mu, azimuthCos( nu, at.mu, at.muS ) );
|
|
550
|
+
for ( let k = 0; k < 8; k ++ ) psi[ k ].assign( next[ k ] );
|
|
551
|
+
|
|
552
|
+
} );
|
|
553
|
+
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
for ( const g of G4 ) {
|
|
557
|
+
|
|
558
|
+
let S = tSun[ g ].mul( sR[ g ].mul( phaseR ).add( sM[ g ].mul( phaseM ) ) );
|
|
559
|
+
if ( psi ) S = S.add( sR[ g ].mul( psi[ g ] ) ).add( sM[ g ].mul( psi[ 4 + g ] ) );
|
|
560
|
+
L[ g ].addAssign( throughput[ g ].mul( S ).mul( seg[ g ].weight ) );
|
|
561
|
+
throughput[ g ].mulAssign( seg[ g ].transmittance );
|
|
562
|
+
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
} );
|
|
566
|
+
|
|
567
|
+
return { L, throughput };
|
|
568
|
+
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Sky irradiance on level ground per unit solar irradiance, against the sun's zenith cosine.
|
|
573
|
+
*/
|
|
574
|
+
export function buildIrradianceKernel( io, { sqrtDirections = 12, steps = 32 } = {} ) {
|
|
575
|
+
|
|
576
|
+
const { irradiance: out } = io;
|
|
577
|
+
const count = IRRADIANCE_SIZE;
|
|
578
|
+
const directions = sqrtDirections * sqrtDirections;
|
|
579
|
+
const h0 = float( 1e-3 );
|
|
580
|
+
|
|
581
|
+
return Fn( () => {
|
|
582
|
+
|
|
583
|
+
const idx = instanceIndex;
|
|
584
|
+
If( idx.greaterThanEqual( uint( count ) ), () => {
|
|
585
|
+
|
|
586
|
+
Return();
|
|
587
|
+
|
|
588
|
+
} );
|
|
589
|
+
|
|
590
|
+
const muS = sunAxis( idx, IRRADIANCE_SIZE ).toVar();
|
|
591
|
+
const sun = vec3( sqrt( max( float( 1 ).sub( muS.mul( muS ) ), 0 ) ), muS, 0 ).toVar();
|
|
592
|
+
const E = spectrum( () => vec4( 0 ).toVar() );
|
|
593
|
+
|
|
594
|
+
Loop( { start: int( 0 ), end: int( directions ), type: 'int', condition: '<', name: 'dirIndex' }, ( { dirIndex: k } ) => {
|
|
595
|
+
|
|
596
|
+
// Cosine-weighted, so the estimate is π × the mean radiance.
|
|
597
|
+
const a = float( k.div( sqrtDirections ) ).add( 0.5 ).div( sqrtDirections );
|
|
598
|
+
const bb = float( k.mod( sqrtDirections ) ).add( 0.5 ).div( sqrtDirections );
|
|
599
|
+
const sinT = sqrt( a );
|
|
600
|
+
const phi = bb.mul( 2 * PI );
|
|
601
|
+
const dir = vec3( sinT.mul( cos( phi ) ), sqrt( max( float( 1 ).sub( a ), 0 ) ), sinT.mul( sin( phi ) ) ).toVar();
|
|
602
|
+
const { L } = inscatter( io, rayExtent( h0, dir.y ), muS, dot( dir, sun ), steps );
|
|
603
|
+
for ( const g of G4 ) E[ g ].addAssign( L[ g ] );
|
|
604
|
+
|
|
605
|
+
} );
|
|
606
|
+
|
|
607
|
+
for ( const g of G4 ) out.element( idx.mul( 4 ).add( g ) ).assign( toLog( E[ g ].mul( PI / directions ) ) );
|
|
608
|
+
|
|
609
|
+
} )().compute( count, [ 64 ] );
|
|
610
|
+
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// The sky without its sun disc, in the engine's equirect mapping (row 0 = nadir). Radiance depends
|
|
614
|
+
// only on elevation and azimuth from the sun, mirrored: rows are the equirect's own, so the horizon
|
|
615
|
+
// stays exact, and columns cover [0, π] as π·s², dense around the sun.
|
|
616
|
+
export const SKY_COLUMNS = 256;
|
|
617
|
+
const columnAzimuth = k => float( k ).div( SKY_COLUMNS - 1 ).pow( 2 ).mul( PI );
|
|
618
|
+
|
|
619
|
+
/** @returns {Array} kernels: the half-sky rows, then the equirect filled from them */
|
|
620
|
+
export function buildSkyViewKernels( io, { width, height, steps = 40, multipleScattering = true } ) {
|
|
621
|
+
|
|
622
|
+
const { sky, skyRows, irradiance, coefficients: c, rgb, sunDirection, altitude, sunSinRadius, transmittance } = io;
|
|
623
|
+
const scatter = multipleScattering ? io : { ...io, multipleScattering: null };
|
|
624
|
+
const rowCount = SKY_COLUMNS * height;
|
|
625
|
+
|
|
626
|
+
const rows = Fn( () => {
|
|
627
|
+
|
|
628
|
+
const idx = instanceIndex;
|
|
629
|
+
If( idx.greaterThanEqual( uint( rowCount ) ), () => {
|
|
630
|
+
|
|
631
|
+
Return();
|
|
632
|
+
|
|
633
|
+
} );
|
|
634
|
+
|
|
635
|
+
const phi = float( 1 ).sub( float( idx.div( SKY_COLUMNS ) ).add( 0.5 ).div( height ) ).mul( PI );
|
|
636
|
+
const dirY = cos( phi ).toVar();
|
|
637
|
+
const h0 = max( altitude, 1e-3 ).toVar();
|
|
638
|
+
const muS0 = sunDirection.y.toVar();
|
|
639
|
+
const nu = sin( phi ).mul( cos( columnAzimuth( idx.mod( SKY_COLUMNS ) ) ) ).mul( sqrt( max( float( 1 ).sub( muS0.mul( muS0 ) ), 0 ) ) ).add( dirY.mul( muS0 ) ).toVar();
|
|
640
|
+
const ray = rayExtent( h0, dirY );
|
|
641
|
+
|
|
642
|
+
const { L, throughput } = inscatter( scatter, ray, muS0, nu, steps );
|
|
643
|
+
|
|
644
|
+
If( ray.hitsGround, () => {
|
|
645
|
+
|
|
646
|
+
const muG = clamp( ray.r.mul( muS0 ).add( ray.tMax.mul( nu ) ).div( RG ), - 1, 1 ).toVar();
|
|
647
|
+
const tSun = sunTransmittance( transmittance, c, float( 0 ), muG, sunSinRadius );
|
|
648
|
+
const eSky = skyIrradiance( irradiance, muG );
|
|
649
|
+
for ( const g of G4 ) {
|
|
650
|
+
|
|
651
|
+
L[ g ].addAssign( throughput[ g ].mul( c.albedo[ g ] ).mul( 1 / PI ).mul( tSun[ g ].mul( max( muG, 0 ) ).add( eSky[ g ] ) ) );
|
|
652
|
+
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
} );
|
|
656
|
+
|
|
657
|
+
const radiance = spectrum( g => L[ g ].mul( c.solar[ g ] ) );
|
|
658
|
+
const channel = row => radiance.reduce( ( acc, s, g ) => acc.add( dot( s, rgb[ row * 4 + g ] ) ), float( 0 ) );
|
|
659
|
+
skyRows.element( idx ).assign( vec4( max( vec3( channel( 0 ), channel( 1 ), channel( 2 ) ), vec3( 0 ) ), 1 ) );
|
|
660
|
+
|
|
661
|
+
} )().compute( rowCount, [ 64 ] );
|
|
662
|
+
|
|
663
|
+
const fill = Fn( () => {
|
|
664
|
+
|
|
665
|
+
const idx = instanceIndex;
|
|
666
|
+
If( idx.greaterThanEqual( uint( width * height ) ), () => {
|
|
667
|
+
|
|
668
|
+
Return();
|
|
669
|
+
|
|
670
|
+
} );
|
|
671
|
+
|
|
672
|
+
const theta = float( idx.mod( width ) ).add( 0.5 ).div( width ).sub( 0.5 ).mul( 2 * PI );
|
|
673
|
+
const d = theta.sub( atan( sunDirection.z, sunDirection.x ) );
|
|
674
|
+
const azimuth = abs( atan( sin( d ), cos( d ) ) );
|
|
675
|
+
const x = sqrt( azimuth.div( PI ) ).mul( SKY_COLUMNS - 1 ).toVar();
|
|
676
|
+
const k = min( floor( x ), SKY_COLUMNS - 2 ).toVar();
|
|
677
|
+
const base = idx.div( width ).mul( SKY_COLUMNS ).add( uint( k ) ).toVar();
|
|
678
|
+
sky.element( idx ).assign( mix( skyRows.element( base ), skyRows.element( base.add( 1 ) ), x.sub( k ) ) );
|
|
679
|
+
|
|
680
|
+
} )().compute( width * height, [ 64 ] );
|
|
681
|
+
|
|
682
|
+
return [ rows, fill ];
|
|
683
|
+
|
|
684
|
+
}
|