@takram/three-clouds 0.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.
Files changed (103) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +1130 -0
  3. package/assets/local_weather.png +0 -0
  4. package/assets/shape.bin +1 -0
  5. package/assets/shape_detail.bin +1 -0
  6. package/assets/turbulence.png +0 -0
  7. package/build/index.cjs +583 -0
  8. package/build/index.cjs.map +1 -0
  9. package/build/index.js +728 -0
  10. package/build/index.js.map +1 -0
  11. package/build/r3f.cjs +2 -0
  12. package/build/r3f.cjs.map +1 -0
  13. package/build/r3f.js +205 -0
  14. package/build/r3f.js.map +1 -0
  15. package/build/shared.cjs +2189 -0
  16. package/build/shared.cjs.map +1 -0
  17. package/build/shared.js +3825 -0
  18. package/build/shared.js.map +1 -0
  19. package/package.json +77 -0
  20. package/src/CascadedShadowMaps.ts +288 -0
  21. package/src/CloudLayer.ts +85 -0
  22. package/src/CloudLayers.test.ts +61 -0
  23. package/src/CloudLayers.ts +181 -0
  24. package/src/CloudShape.ts +22 -0
  25. package/src/CloudShapeDetail.ts +22 -0
  26. package/src/CloudsEffect.ts +810 -0
  27. package/src/CloudsMaterial.ts +467 -0
  28. package/src/CloudsPass.ts +285 -0
  29. package/src/CloudsResolveMaterial.ts +108 -0
  30. package/src/DensityProfile.ts +38 -0
  31. package/src/LocalWeather.ts +21 -0
  32. package/src/PassBase.ts +28 -0
  33. package/src/Procedural3DTexture.ts +94 -0
  34. package/src/ProceduralTexture.ts +94 -0
  35. package/src/ShaderArrayPass.ts +32 -0
  36. package/src/ShadowMaterial.ts +141 -0
  37. package/src/ShadowPass.ts +185 -0
  38. package/src/ShadowResolveMaterial.ts +72 -0
  39. package/src/Turbulence.ts +21 -0
  40. package/src/bayer.ts +23 -0
  41. package/src/constants.ts +8 -0
  42. package/src/helpers/FrustumCorners.ts +138 -0
  43. package/src/helpers/setArrayRenderTargetLayers.ts +32 -0
  44. package/src/helpers/splitFrustum.ts +59 -0
  45. package/src/index.ts +14 -0
  46. package/src/qualityPresets.ts +117 -0
  47. package/src/r3f/CloudLayer.tsx +95 -0
  48. package/src/r3f/CloudLayers.tsx +54 -0
  49. package/src/r3f/Clouds.tsx +278 -0
  50. package/src/r3f/index.ts +2 -0
  51. package/src/shaders/catmullRomSampling.glsl +113 -0
  52. package/src/shaders/cloudShape.frag +78 -0
  53. package/src/shaders/cloudShapeDetail.frag +56 -0
  54. package/src/shaders/clouds.frag +996 -0
  55. package/src/shaders/clouds.glsl +190 -0
  56. package/src/shaders/clouds.vert +69 -0
  57. package/src/shaders/cloudsEffect.frag +11 -0
  58. package/src/shaders/cloudsResolve.frag +202 -0
  59. package/src/shaders/cloudsResolve.vert +10 -0
  60. package/src/shaders/localWeather.frag +83 -0
  61. package/src/shaders/parameters.glsl +64 -0
  62. package/src/shaders/perlin.glsl +211 -0
  63. package/src/shaders/shadow.frag +197 -0
  64. package/src/shaders/shadow.vert +16 -0
  65. package/src/shaders/shadowResolve.frag +76 -0
  66. package/src/shaders/shadowResolve.vert +10 -0
  67. package/src/shaders/structuredSampling.glsl +101 -0
  68. package/src/shaders/tileableNoise.glsl +88 -0
  69. package/src/shaders/turbulence.frag +51 -0
  70. package/src/shaders/types.glsl +18 -0
  71. package/src/shaders/varianceClipping.glsl +114 -0
  72. package/src/uniforms.ts +218 -0
  73. package/types/CascadedShadowMaps.d.ts +52 -0
  74. package/types/CloudLayer.d.ts +26 -0
  75. package/types/CloudLayers.d.ts +21 -0
  76. package/types/CloudShape.d.ts +5 -0
  77. package/types/CloudShapeDetail.d.ts +5 -0
  78. package/types/CloudsEffect.d.ts +170 -0
  79. package/types/CloudsMaterial.d.ts +86 -0
  80. package/types/CloudsPass.d.ts +44 -0
  81. package/types/CloudsResolveMaterial.d.ts +30 -0
  82. package/types/DensityProfile.d.ts +12 -0
  83. package/types/LocalWeather.d.ts +5 -0
  84. package/types/PassBase.d.ts +14 -0
  85. package/types/Procedural3DTexture.d.ts +20 -0
  86. package/types/ProceduralTexture.d.ts +24 -0
  87. package/types/ShaderArrayPass.d.ts +7 -0
  88. package/types/ShadowMaterial.d.ts +34 -0
  89. package/types/ShadowPass.d.ts +34 -0
  90. package/types/ShadowResolveMaterial.d.ts +20 -0
  91. package/types/Turbulence.d.ts +5 -0
  92. package/types/bayer.d.ts +4 -0
  93. package/types/constants.d.ts +6 -0
  94. package/types/helpers/FrustumCorners.d.ts +18 -0
  95. package/types/helpers/setArrayRenderTargetLayers.d.ts +3 -0
  96. package/types/helpers/splitFrustum.d.ts +9 -0
  97. package/types/index.d.ts +13 -0
  98. package/types/qualityPresets.d.ts +46 -0
  99. package/types/r3f/CloudLayer.d.ts +7 -0
  100. package/types/r3f/CloudLayers.d.ts +15 -0
  101. package/types/r3f/Clouds.d.ts +16 -0
  102. package/types/r3f/index.d.ts +2 -0
  103. package/types/uniforms.d.ts +66 -0
@@ -0,0 +1,810 @@
1
+ import { Effect, EffectAttribute, Resolution } from 'postprocessing'
2
+ import {
3
+ Camera,
4
+ Data3DTexture,
5
+ EventDispatcher,
6
+ Matrix4,
7
+ Texture,
8
+ Uniform,
9
+ Vector2,
10
+ Vector3,
11
+ type DataTexture,
12
+ type DepthPackingStrategies,
13
+ type PerspectiveCamera,
14
+ type TextureDataType,
15
+ type WebGLRenderer,
16
+ type WebGLRenderTarget
17
+ } from 'three'
18
+
19
+ import {
20
+ AtmosphereParameters,
21
+ getAltitudeCorrectionOffset,
22
+ type AtmosphereOverlay,
23
+ type AtmosphereShadow,
24
+ type AtmosphereShadowLength
25
+ } from '@takram/three-atmosphere'
26
+ import {
27
+ define,
28
+ definePropertyShorthand,
29
+ defineUniformShorthand,
30
+ lerp,
31
+ UniformMap,
32
+ type Ellipsoid,
33
+ type PropertyShorthand,
34
+ type UniformShorthand
35
+ } from '@takram/three-geospatial'
36
+
37
+ import { CascadedShadowMaps } from './CascadedShadowMaps'
38
+ import { CloudLayers } from './CloudLayers'
39
+ import {
40
+ type CloudsMaterial,
41
+ type CloudsMaterialUniforms
42
+ } from './CloudsMaterial'
43
+ import { CloudsPass } from './CloudsPass'
44
+ import { type Procedural3DTexture } from './Procedural3DTexture'
45
+ import { type ProceduralTexture } from './ProceduralTexture'
46
+ import { defaults, qualityPresets, type QualityPreset } from './qualityPresets'
47
+ import {
48
+ type ShadowMaterial,
49
+ type ShadowMaterialUniforms
50
+ } from './ShadowMaterial'
51
+ import { ShadowPass } from './ShadowPass'
52
+ import {
53
+ createAtmosphereUniforms,
54
+ createCloudLayerUniforms,
55
+ createCloudParameterUniforms,
56
+ updateCloudLayerUniforms,
57
+ type AtmosphereUniforms,
58
+ type CloudLayerUniforms,
59
+ type CloudParameterUniforms
60
+ } from './uniforms'
61
+
62
+ import fragmentShader from './shaders/cloudsEffect.frag?raw'
63
+
64
+ const vector3Scratch = /*#__PURE__*/ new Vector3()
65
+ const vector2Scratch = /*#__PURE__*/ new Vector2()
66
+
67
+ const cloudsUniformKeys = [
68
+ 'maxIterationCount',
69
+ 'minStepSize',
70
+ 'maxStepSize',
71
+ 'maxRayDistance',
72
+ 'perspectiveStepScale',
73
+ 'minDensity',
74
+ 'minExtinction',
75
+ 'minTransmittance',
76
+ 'maxIterationCountToSun',
77
+ 'maxIterationCountToGround',
78
+ 'minSecondaryStepSize',
79
+ 'secondaryStepScale',
80
+ 'maxShadowFilterRadius',
81
+ 'maxShadowLengthIterationCount',
82
+ 'minShadowLengthStepSize',
83
+ 'maxShadowLengthRayDistance',
84
+ 'hazeDensityScale',
85
+ 'hazeExponent'
86
+ ] as const satisfies Array<keyof CloudsMaterialUniforms>
87
+
88
+ // prettier-ignore
89
+ const cloudsMaterialParameterKeys = [
90
+ 'multiScatteringOctaves',
91
+ 'accurateSunSkyIrradiance',
92
+ 'accuratePhaseFunction',
93
+ ] as const satisfies Array<keyof CloudsMaterial>
94
+
95
+ const shadowUniformKeys = [
96
+ 'maxIterationCount',
97
+ 'minStepSize',
98
+ 'maxStepSize',
99
+ 'minDensity',
100
+ 'minExtinction',
101
+ 'minTransmittance',
102
+ 'opticalDepthTailScale'
103
+ ] as const satisfies Array<keyof ShadowMaterialUniforms>
104
+
105
+ // prettier-ignore
106
+ const shadowMaterialParameterKeys = [
107
+ 'temporalJitter'
108
+ ] as const satisfies Array<keyof ShadowMaterial>
109
+
110
+ // prettier-ignore
111
+ const shadowPassParameterKeys = [
112
+ 'temporalPass'
113
+ ] as const satisfies Array<keyof ShadowPass>
114
+
115
+ const shadowMapsParameterKeys = [
116
+ 'cascadeCount',
117
+ 'mapSize',
118
+ 'maxFar',
119
+ 'farScale',
120
+ 'splitMode',
121
+ 'splitLambda'
122
+ ] as const satisfies Array<keyof CascadedShadowMaps>
123
+
124
+ interface CloudsShorthand
125
+ extends UniformShorthand<CloudsMaterial, (typeof cloudsUniformKeys)[number]>,
126
+ PropertyShorthand<[CloudsMaterial, typeof cloudsMaterialParameterKeys]> {}
127
+
128
+ interface ShadowShorthand
129
+ extends UniformShorthand<ShadowMaterial, (typeof shadowUniformKeys)[number]>,
130
+ PropertyShorthand<
131
+ [
132
+ ShadowMaterial,
133
+ typeof shadowMaterialParameterKeys,
134
+ ShadowPass,
135
+ typeof shadowPassParameterKeys,
136
+ CascadedShadowMaps,
137
+ typeof shadowMapsParameterKeys
138
+ ]
139
+ > {}
140
+
141
+ export interface CloudsEffectChangeEvent {
142
+ type: 'change'
143
+ target?: CloudsEffect
144
+ property?: 'atmosphereOverlay' | 'atmosphereShadow' | 'atmosphereShadowLength'
145
+ }
146
+
147
+ const changeEvent: CloudsEffectChangeEvent = {
148
+ type: 'change'
149
+ }
150
+
151
+ export interface CloudsEffectUniforms {
152
+ cloudsBuffer: Uniform<Texture | null>
153
+ }
154
+
155
+ export interface CloudsEffectOptions {
156
+ resolutionScale?: number
157
+ width?: number
158
+ height?: number
159
+ resolutionX?: number
160
+ resolutionY?: number
161
+ }
162
+
163
+ export const cloudsPassOptionsDefaults = {
164
+ resolutionScale: defaults.resolutionScale,
165
+ width: Resolution.AUTO_SIZE,
166
+ height: Resolution.AUTO_SIZE
167
+ } satisfies CloudsEffectOptions
168
+
169
+ // We explicitly use Effect instead of Pass, even though this only renders to
170
+ // render buffers. Pass, at least when used with R3F, tends to be unstable
171
+ // during hot reloading. This should not impact performance since this effect
172
+ // can be merged.
173
+ export class CloudsEffect extends Effect {
174
+ declare uniforms: UniformMap<CloudsEffectUniforms>
175
+
176
+ readonly cloudLayers = CloudLayers.DEFAULT.clone()
177
+
178
+ correctAltitude = true
179
+
180
+ // Mutable instances of cloud parameter uniforms
181
+ readonly localWeatherRepeat = new Vector2().setScalar(100)
182
+ readonly localWeatherOffset = new Vector2()
183
+ readonly shapeRepeat = new Vector3().setScalar(0.0003)
184
+ readonly shapeOffset = new Vector3()
185
+ readonly shapeDetailRepeat = new Vector3().setScalar(0.006)
186
+ readonly shapeDetailOffset = new Vector3()
187
+ readonly turbulenceRepeat = new Vector2().setScalar(20)
188
+
189
+ // Mutable instances of atmosphere parameter uniforms
190
+ readonly ellipsoidCenter = new Vector3()
191
+ readonly ellipsoidMatrix = new Matrix4()
192
+ private readonly inverseEllipsoidMatrix = new Matrix4()
193
+ private readonly altitudeCorrection = new Vector3()
194
+ readonly sunDirection = new Vector3()
195
+
196
+ // Uniforms shared by both cloud and shadow materials
197
+ private readonly parameterUniforms: CloudParameterUniforms
198
+ private readonly layerUniforms: CloudLayerUniforms
199
+ private readonly atmosphereUniforms: AtmosphereUniforms
200
+
201
+ readonly localWeatherVelocity = new Vector2()
202
+ readonly shapeVelocity = new Vector3()
203
+ readonly shapeDetailVelocity = new Vector3()
204
+
205
+ // Weather and shape procedural textures
206
+ private proceduralLocalWeather?: ProceduralTexture
207
+ private proceduralShape?: Procedural3DTexture
208
+ private proceduralShapeDetail?: Procedural3DTexture
209
+ private proceduralTurbulence?: ProceduralTexture
210
+
211
+ readonly shadowMaps: CascadedShadowMaps
212
+ readonly shadowPass: ShadowPass
213
+ readonly cloudsPass: CloudsPass
214
+
215
+ readonly clouds: CloudsShorthand
216
+ readonly shadow: ShadowShorthand
217
+
218
+ private _atmosphereOverlay: AtmosphereOverlay | null = null
219
+ private _atmosphereShadow: AtmosphereShadow | null = null
220
+ private _atmosphereShadowLength: AtmosphereShadowLength | null = null
221
+
222
+ readonly resolution: Resolution
223
+ readonly events = new EventDispatcher<{
224
+ change: CloudsEffectChangeEvent
225
+ }>()
226
+
227
+ private frame = 0
228
+ private shadowCascadeCount = 0
229
+ private readonly shadowMapSize = new Vector2()
230
+
231
+ constructor(
232
+ private camera: Camera = new Camera(),
233
+ options?: CloudsEffectOptions,
234
+ private readonly atmosphere = AtmosphereParameters.DEFAULT
235
+ ) {
236
+ super('CloudsEffect', fragmentShader, {
237
+ attributes: EffectAttribute.DEPTH,
238
+ uniforms: new Map<string, Uniform>([['cloudsBuffer', new Uniform(null)]])
239
+ })
240
+
241
+ const {
242
+ resolutionScale,
243
+ width,
244
+ height,
245
+ resolutionX = width,
246
+ resolutionY = height
247
+ } = {
248
+ ...cloudsPassOptionsDefaults,
249
+ ...options
250
+ }
251
+
252
+ this.shadowMaps = new CascadedShadowMaps({
253
+ cascadeCount: defaults.shadow.cascadeCount,
254
+ mapSize: defaults.shadow.mapSize,
255
+ splitLambda: 0.6
256
+ })
257
+
258
+ this.parameterUniforms = createCloudParameterUniforms({
259
+ localWeatherTexture: this.proceduralLocalWeather?.texture ?? null,
260
+ localWeatherRepeat: this.localWeatherRepeat,
261
+ localWeatherOffset: this.localWeatherOffset,
262
+ shapeTexture: this.proceduralShape?.texture ?? null,
263
+ shapeRepeat: this.shapeRepeat,
264
+ shapeOffset: this.shapeOffset,
265
+ shapeDetailTexture: this.proceduralShapeDetail?.texture ?? null,
266
+ shapeDetailRepeat: this.shapeDetailRepeat,
267
+ shapeDetailOffset: this.shapeDetailOffset,
268
+ turbulenceTexture: this.proceduralTurbulence?.texture ?? null,
269
+ turbulenceRepeat: this.turbulenceRepeat
270
+ })
271
+
272
+ this.layerUniforms = createCloudLayerUniforms()
273
+
274
+ this.atmosphereUniforms = createAtmosphereUniforms(atmosphere, {
275
+ ellipsoidCenter: this.ellipsoidCenter,
276
+ ellipsoidMatrix: this.ellipsoidMatrix,
277
+ inverseEllipsoidMatrix: this.inverseEllipsoidMatrix,
278
+ altitudeCorrection: this.altitudeCorrection,
279
+ sunDirection: this.sunDirection
280
+ })
281
+
282
+ const passOptions = {
283
+ shadow: this.shadowMaps,
284
+ parameterUniforms: this.parameterUniforms,
285
+ layerUniforms: this.layerUniforms,
286
+ atmosphereUniforms: this.atmosphereUniforms
287
+ }
288
+ this.shadowPass = new ShadowPass(passOptions)
289
+ this.cloudsPass = new CloudsPass(passOptions, atmosphere)
290
+
291
+ this.clouds = definePropertyShorthand(
292
+ defineUniformShorthand(
293
+ {},
294
+ this.cloudsPass.currentMaterial,
295
+ cloudsUniformKeys
296
+ ),
297
+ this.cloudsPass.currentMaterial,
298
+ cloudsMaterialParameterKeys
299
+ )
300
+ this.shadow = definePropertyShorthand(
301
+ defineUniformShorthand(
302
+ {},
303
+ this.shadowPass.currentMaterial,
304
+ shadowUniformKeys
305
+ ),
306
+ this.shadowPass.currentMaterial,
307
+ shadowMaterialParameterKeys,
308
+ this.shadowPass,
309
+ shadowPassParameterKeys,
310
+ this.shadowMaps,
311
+ shadowMapsParameterKeys
312
+ )
313
+
314
+ this.resolution = new Resolution(
315
+ this,
316
+ resolutionX,
317
+ resolutionY,
318
+ resolutionScale
319
+ )
320
+ this.resolution.addEventListener('change', this.onResolutionChange)
321
+ }
322
+
323
+ private readonly onResolutionChange = (): void => {
324
+ this.setSize(this.resolution.baseWidth, this.resolution.baseHeight)
325
+ }
326
+
327
+ override get mainCamera(): Camera {
328
+ return this.camera
329
+ }
330
+
331
+ override set mainCamera(value: Camera) {
332
+ this.camera = value
333
+ this.shadowPass.mainCamera = value
334
+ this.cloudsPass.mainCamera = value
335
+ }
336
+
337
+ override initialize(
338
+ renderer: WebGLRenderer,
339
+ alpha: boolean,
340
+ frameBufferType: TextureDataType
341
+ ): void {
342
+ this.shadowPass.initialize(renderer, alpha, frameBufferType)
343
+ this.cloudsPass.initialize(renderer, alpha, frameBufferType)
344
+ }
345
+
346
+ private updateSharedUniforms(deltaTime: number): void {
347
+ updateCloudLayerUniforms(this.layerUniforms, this.cloudLayers)
348
+
349
+ // Apply velocity to offset uniforms.
350
+ const { parameterUniforms } = this
351
+ parameterUniforms.localWeatherOffset.value.add(
352
+ vector2Scratch.copy(this.localWeatherVelocity).multiplyScalar(deltaTime)
353
+ )
354
+ parameterUniforms.shapeOffset.value.add(
355
+ vector3Scratch.copy(this.shapeVelocity).multiplyScalar(deltaTime)
356
+ )
357
+ parameterUniforms.shapeDetailOffset.value.add(
358
+ vector3Scratch.copy(this.shapeDetailVelocity).multiplyScalar(deltaTime)
359
+ )
360
+
361
+ // Update atmosphere uniforms.
362
+ const inverseEllipsoidMatrix = this.inverseEllipsoidMatrix
363
+ .copy(this.ellipsoidMatrix)
364
+ .invert()
365
+ const cameraPositionECEF = this.camera
366
+ .getWorldPosition(vector3Scratch)
367
+ .applyMatrix4(inverseEllipsoidMatrix)
368
+ .sub(this.ellipsoidCenter)
369
+
370
+ const altitudeCorrection = this.altitudeCorrection
371
+ if (this.correctAltitude) {
372
+ getAltitudeCorrectionOffset(
373
+ cameraPositionECEF,
374
+ this.atmosphere.bottomRadius,
375
+ this.ellipsoid,
376
+ altitudeCorrection,
377
+ false
378
+ )
379
+ } else {
380
+ altitudeCorrection.setScalar(0)
381
+ }
382
+
383
+ // TODO: Position the sun on the top atmosphere sphere.
384
+ // Increase light's distance to the target when the sun is at the horizon.
385
+ const surfaceNormal = this.ellipsoid.getSurfaceNormal(
386
+ cameraPositionECEF,
387
+ vector3Scratch
388
+ )
389
+ const zenithAngle = this.sunDirection.dot(surfaceNormal)
390
+ const distance = lerp(1e6, 1e3, zenithAngle)
391
+
392
+ this.shadowMaps.update(
393
+ this.camera as PerspectiveCamera,
394
+ // The sun direction must be rotated with the ellipsoid to ensure the
395
+ // frusta are constructed correctly. Note this affects the transformation
396
+ // in the shadow shader.
397
+ vector3Scratch.copy(this.sunDirection).applyMatrix4(this.ellipsoidMatrix),
398
+ distance
399
+ )
400
+ }
401
+
402
+ private updateWeatherTextureChannels(): void {
403
+ const value = this.cloudLayers.localWeatherChannels
404
+ this.cloudsPass.currentMaterial.localWeatherChannels = value
405
+ this.shadowPass.currentMaterial.localWeatherChannels = value
406
+ }
407
+
408
+ private updateAtmosphereComposition(): void {
409
+ const { shadowMaps, shadowPass, cloudsPass } = this
410
+ const shadowUniforms = shadowPass.currentMaterial.uniforms
411
+ const cloudsUniforms = cloudsPass.currentMaterial.uniforms
412
+
413
+ const prevOverlay = this._atmosphereOverlay
414
+ const nextOverlay = Object.assign(this._atmosphereOverlay ?? {}, {
415
+ map: cloudsPass.outputBuffer
416
+ } satisfies AtmosphereOverlay)
417
+ if (prevOverlay !== nextOverlay) {
418
+ this._atmosphereOverlay = nextOverlay
419
+ changeEvent.target = this
420
+ changeEvent.property = 'atmosphereOverlay'
421
+ this.events.dispatchEvent(changeEvent)
422
+ }
423
+
424
+ const prevShadow = this._atmosphereShadow
425
+ const nextShadow = Object.assign(this._atmosphereShadow ?? {}, {
426
+ map: shadowPass.outputBuffer,
427
+ mapSize: shadowMaps.mapSize,
428
+ cascadeCount: shadowMaps.cascadeCount,
429
+ intervals: cloudsUniforms.shadowIntervals.value,
430
+ matrices: cloudsUniforms.shadowMatrices.value,
431
+ inverseMatrices: shadowUniforms.inverseShadowMatrices.value,
432
+ far: shadowMaps.far,
433
+ topHeight: cloudsUniforms.shadowTopHeight.value
434
+ } satisfies AtmosphereShadow)
435
+ if (prevShadow !== nextShadow) {
436
+ this._atmosphereShadow = nextShadow
437
+ changeEvent.target = this
438
+ changeEvent.property = 'atmosphereShadow'
439
+ this.events.dispatchEvent(changeEvent)
440
+ }
441
+
442
+ const prevShadowLength = this._atmosphereShadowLength
443
+ const nextShadowLength =
444
+ cloudsPass.shadowLengthBuffer != null
445
+ ? Object.assign(this._atmosphereShadowLength ?? {}, {
446
+ map: cloudsPass.shadowLengthBuffer
447
+ } satisfies AtmosphereShadowLength)
448
+ : null
449
+ if (prevShadowLength !== nextShadowLength) {
450
+ this._atmosphereShadowLength = nextShadowLength
451
+ changeEvent.target = this
452
+ changeEvent.property = 'atmosphereShadowLength'
453
+ this.events.dispatchEvent(changeEvent)
454
+ }
455
+ }
456
+
457
+ override update(
458
+ renderer: WebGLRenderer,
459
+ inputBuffer: WebGLRenderTarget,
460
+ deltaTime = 0
461
+ ): void {
462
+ const { shadowMaps, shadowPass, cloudsPass } = this
463
+ if (
464
+ shadowMaps.cascadeCount !== this.shadowCascadeCount ||
465
+ !shadowMaps.mapSize.equals(this.shadowMapSize)
466
+ ) {
467
+ const { width, height } = shadowMaps.mapSize
468
+ const depth = shadowMaps.cascadeCount
469
+ this.shadowMapSize.set(width, height)
470
+ this.shadowCascadeCount = depth
471
+
472
+ shadowPass.setSize(width, height, depth)
473
+ cloudsPass.setShadowSize(width, height, depth)
474
+ }
475
+
476
+ this.proceduralLocalWeather?.render(renderer, deltaTime)
477
+ this.proceduralShape?.render(renderer, deltaTime)
478
+ this.proceduralShapeDetail?.render(renderer, deltaTime)
479
+ this.proceduralTurbulence?.render(renderer, deltaTime)
480
+
481
+ ++this.frame
482
+ this.updateSharedUniforms(deltaTime)
483
+ this.updateWeatherTextureChannels()
484
+
485
+ shadowPass.update(renderer, this.frame, deltaTime)
486
+ cloudsPass.shadowBuffer = shadowPass.outputBuffer
487
+ cloudsPass.update(renderer, this.frame, deltaTime)
488
+
489
+ this.updateAtmosphereComposition()
490
+ this.uniforms.get('cloudsBuffer').value = this.cloudsPass.outputBuffer
491
+ }
492
+
493
+ override setSize(baseWidth: number, baseHeight: number): void {
494
+ const { resolution } = this
495
+ resolution.setBaseSize(baseWidth, baseHeight)
496
+ const { width, height } = resolution
497
+ this.cloudsPass.setSize(width, height)
498
+ }
499
+
500
+ override setDepthTexture(
501
+ depthTexture: Texture,
502
+ depthPacking?: DepthPackingStrategies
503
+ ): void {
504
+ this.shadowPass.setDepthTexture(depthTexture, depthPacking)
505
+ this.cloudsPass.setDepthTexture(depthTexture, depthPacking)
506
+ }
507
+
508
+ @define('SKIP_RENDERING')
509
+ skipRendering = true
510
+
511
+ // eslint-disable-next-line accessor-pairs
512
+ set qualityPreset(value: QualityPreset) {
513
+ const { clouds, shadow, ...props } = qualityPresets[value]
514
+ Object.assign(this, props)
515
+ Object.assign(this.clouds, clouds)
516
+ Object.assign(this.shadow, shadow)
517
+ }
518
+
519
+ // Textures
520
+
521
+ get localWeatherTexture(): Texture | ProceduralTexture | null {
522
+ return (
523
+ this.proceduralLocalWeather ??
524
+ this.parameterUniforms.localWeatherTexture.value
525
+ )
526
+ }
527
+
528
+ set localWeatherTexture(value: Texture | ProceduralTexture | null) {
529
+ if (value instanceof Texture || value == null) {
530
+ this.proceduralLocalWeather = undefined
531
+ this.parameterUniforms.localWeatherTexture.value = value
532
+ } else {
533
+ this.proceduralLocalWeather = value
534
+ this.parameterUniforms.localWeatherTexture.value = value.texture
535
+ }
536
+ }
537
+
538
+ get shapeTexture(): Data3DTexture | Procedural3DTexture | null {
539
+ return this.proceduralShape ?? this.parameterUniforms.shapeTexture.value
540
+ }
541
+
542
+ set shapeTexture(value: Data3DTexture | Procedural3DTexture | null) {
543
+ if (value instanceof Data3DTexture || value == null) {
544
+ this.proceduralShape = undefined
545
+ this.parameterUniforms.shapeTexture.value = value
546
+ } else {
547
+ this.proceduralShape = value
548
+ this.parameterUniforms.shapeTexture.value = value.texture
549
+ }
550
+ }
551
+
552
+ get shapeDetailTexture(): Data3DTexture | Procedural3DTexture | null {
553
+ return (
554
+ this.proceduralShapeDetail ??
555
+ this.parameterUniforms.shapeDetailTexture.value
556
+ )
557
+ }
558
+
559
+ set shapeDetailTexture(value: Data3DTexture | Procedural3DTexture | null) {
560
+ if (value instanceof Data3DTexture || value == null) {
561
+ this.proceduralShapeDetail = undefined
562
+ this.parameterUniforms.shapeDetailTexture.value = value
563
+ } else {
564
+ this.proceduralShapeDetail = value
565
+ this.parameterUniforms.shapeDetailTexture.value = value.texture
566
+ }
567
+ }
568
+
569
+ get turbulenceTexture(): Texture | ProceduralTexture | null {
570
+ return (
571
+ this.proceduralTurbulence ??
572
+ this.parameterUniforms.turbulenceTexture.value
573
+ )
574
+ }
575
+
576
+ set turbulenceTexture(value: Texture | ProceduralTexture | null) {
577
+ if (value instanceof Texture || value == null) {
578
+ this.proceduralTurbulence = undefined
579
+ this.parameterUniforms.turbulenceTexture.value = value
580
+ } else {
581
+ this.proceduralTurbulence = value
582
+ this.parameterUniforms.turbulenceTexture.value = value.texture
583
+ }
584
+ }
585
+
586
+ get stbnTexture(): Data3DTexture | null {
587
+ return this.cloudsPass.currentMaterial.uniforms.stbnTexture.value
588
+ }
589
+
590
+ set stbnTexture(value: Data3DTexture | null) {
591
+ this.cloudsPass.currentMaterial.uniforms.stbnTexture.value = value
592
+ this.shadowPass.currentMaterial.uniforms.stbnTexture.value = value
593
+ }
594
+
595
+ // Rendering controls
596
+
597
+ get resolutionScale(): number {
598
+ return this.resolution.scale
599
+ }
600
+
601
+ set resolutionScale(value: number) {
602
+ this.resolution.scale = value
603
+ }
604
+
605
+ get temporalUpscale(): boolean {
606
+ return this.cloudsPass.temporalUpscale
607
+ }
608
+
609
+ set temporalUpscale(value: boolean) {
610
+ this.cloudsPass.temporalUpscale = value
611
+ }
612
+
613
+ get lightShafts(): boolean {
614
+ return this.cloudsPass.lightShafts
615
+ }
616
+
617
+ set lightShafts(value: boolean) {
618
+ this.cloudsPass.lightShafts = value
619
+ }
620
+
621
+ get shapeDetail(): boolean {
622
+ return this.cloudsPass.currentMaterial.shapeDetail
623
+ }
624
+
625
+ set shapeDetail(value: boolean) {
626
+ this.cloudsPass.currentMaterial.shapeDetail = value
627
+ this.shadowPass.currentMaterial.shapeDetail = value
628
+ }
629
+
630
+ get turbulence(): boolean {
631
+ return this.cloudsPass.currentMaterial.turbulence
632
+ }
633
+
634
+ set turbulence(value: boolean) {
635
+ this.cloudsPass.currentMaterial.turbulence = value
636
+ this.shadowPass.currentMaterial.turbulence = value
637
+ }
638
+
639
+ get haze(): boolean {
640
+ return this.cloudsPass.currentMaterial.haze
641
+ }
642
+
643
+ set haze(value: boolean) {
644
+ this.cloudsPass.currentMaterial.haze = value
645
+ }
646
+
647
+ // Cloud parameter primitives
648
+
649
+ get scatteringCoefficient(): number {
650
+ return this.parameterUniforms.scatteringCoefficient.value
651
+ }
652
+
653
+ set scatteringCoefficient(value: number) {
654
+ this.parameterUniforms.scatteringCoefficient.value = value
655
+ }
656
+
657
+ get absorptionCoefficient(): number {
658
+ return this.parameterUniforms.absorptionCoefficient.value
659
+ }
660
+
661
+ set absorptionCoefficient(value: number) {
662
+ this.parameterUniforms.absorptionCoefficient.value = value
663
+ }
664
+
665
+ get coverage(): number {
666
+ return this.parameterUniforms.coverage.value
667
+ }
668
+
669
+ set coverage(value: number) {
670
+ this.parameterUniforms.coverage.value = value
671
+ }
672
+
673
+ get turbulenceDisplacement(): number {
674
+ return this.parameterUniforms.turbulenceDisplacement.value
675
+ }
676
+
677
+ set turbulenceDisplacement(value: number) {
678
+ this.parameterUniforms.turbulenceDisplacement.value = value
679
+ }
680
+
681
+ // Scattering parameters
682
+
683
+ get scatterAnisotropy1(): number {
684
+ return this.cloudsPass.currentMaterial.scatterAnisotropy1
685
+ }
686
+
687
+ set scatterAnisotropy1(value: number) {
688
+ this.cloudsPass.currentMaterial.scatterAnisotropy1 = value
689
+ }
690
+
691
+ get scatterAnisotropy2(): number {
692
+ return this.cloudsPass.currentMaterial.scatterAnisotropy2
693
+ }
694
+
695
+ set scatterAnisotropy2(value: number) {
696
+ this.cloudsPass.currentMaterial.scatterAnisotropy2 = value
697
+ }
698
+
699
+ get scatterAnisotropyMix(): number {
700
+ return this.cloudsPass.currentMaterial.scatterAnisotropyMix
701
+ }
702
+
703
+ set scatterAnisotropyMix(value: number) {
704
+ this.cloudsPass.currentMaterial.scatterAnisotropyMix = value
705
+ }
706
+
707
+ get skyIrradianceScale(): number {
708
+ return this.cloudsPass.currentMaterial.uniforms.skyIrradianceScale.value
709
+ }
710
+
711
+ set skyIrradianceScale(value: number) {
712
+ this.cloudsPass.currentMaterial.uniforms.skyIrradianceScale.value = value
713
+ }
714
+
715
+ get groundIrradianceScale(): number {
716
+ return this.cloudsPass.currentMaterial.uniforms.groundIrradianceScale.value
717
+ }
718
+
719
+ set groundIrradianceScale(value: number) {
720
+ this.cloudsPass.currentMaterial.uniforms.groundIrradianceScale.value = value
721
+ }
722
+
723
+ get powderScale(): number {
724
+ return this.cloudsPass.currentMaterial.uniforms.powderScale.value
725
+ }
726
+
727
+ set powderScale(value: number) {
728
+ this.cloudsPass.currentMaterial.uniforms.powderScale.value = value
729
+ }
730
+
731
+ get powderExponent(): number {
732
+ return this.cloudsPass.currentMaterial.uniforms.powderExponent.value
733
+ }
734
+
735
+ set powderExponent(value: number) {
736
+ this.cloudsPass.currentMaterial.uniforms.powderExponent.value = value
737
+ }
738
+
739
+ // Atmosphere composition
740
+
741
+ get atmosphereOverlay(): AtmosphereOverlay | null {
742
+ return this._atmosphereOverlay
743
+ }
744
+
745
+ get atmosphereShadow(): AtmosphereShadow | null {
746
+ return this._atmosphereShadow
747
+ }
748
+
749
+ get atmosphereShadowLength(): AtmosphereShadowLength | null {
750
+ return this._atmosphereShadowLength
751
+ }
752
+
753
+ // Atmosphere parameters
754
+
755
+ get irradianceTexture(): DataTexture | null {
756
+ return this.cloudsPass.currentMaterial.irradianceTexture
757
+ }
758
+
759
+ set irradianceTexture(value: DataTexture | null) {
760
+ this.cloudsPass.currentMaterial.irradianceTexture = value
761
+ }
762
+
763
+ get scatteringTexture(): Data3DTexture | null {
764
+ return this.cloudsPass.currentMaterial.scatteringTexture
765
+ }
766
+
767
+ set scatteringTexture(value: Data3DTexture | null) {
768
+ this.cloudsPass.currentMaterial.scatteringTexture = value
769
+ }
770
+
771
+ get transmittanceTexture(): DataTexture | null {
772
+ return this.cloudsPass.currentMaterial.transmittanceTexture
773
+ }
774
+
775
+ set transmittanceTexture(value: DataTexture | null) {
776
+ this.cloudsPass.currentMaterial.transmittanceTexture = value
777
+ }
778
+
779
+ get useHalfFloat(): boolean {
780
+ return this.cloudsPass.currentMaterial.useHalfFloat
781
+ }
782
+
783
+ set useHalfFloat(value: boolean) {
784
+ this.cloudsPass.currentMaterial.useHalfFloat = value
785
+ }
786
+
787
+ get ellipsoid(): Ellipsoid {
788
+ return this.cloudsPass.currentMaterial.ellipsoid
789
+ }
790
+
791
+ set ellipsoid(value: Ellipsoid) {
792
+ this.cloudsPass.currentMaterial.ellipsoid = value
793
+ }
794
+
795
+ get photometric(): boolean {
796
+ return this.cloudsPass.currentMaterial.photometric
797
+ }
798
+
799
+ set photometric(value: boolean) {
800
+ this.cloudsPass.currentMaterial.photometric = value
801
+ }
802
+
803
+ get sunAngularRadius(): number {
804
+ return this.cloudsPass.currentMaterial.sunAngularRadius
805
+ }
806
+
807
+ set sunAngularRadius(value: number) {
808
+ this.cloudsPass.currentMaterial.sunAngularRadius = value
809
+ }
810
+ }