@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.
- package/CHANGELOG.md +5 -0
- package/README.md +1130 -0
- package/assets/local_weather.png +0 -0
- package/assets/shape.bin +1 -0
- package/assets/shape_detail.bin +1 -0
- package/assets/turbulence.png +0 -0
- package/build/index.cjs +583 -0
- package/build/index.cjs.map +1 -0
- package/build/index.js +728 -0
- package/build/index.js.map +1 -0
- package/build/r3f.cjs +2 -0
- package/build/r3f.cjs.map +1 -0
- package/build/r3f.js +205 -0
- package/build/r3f.js.map +1 -0
- package/build/shared.cjs +2189 -0
- package/build/shared.cjs.map +1 -0
- package/build/shared.js +3825 -0
- package/build/shared.js.map +1 -0
- package/package.json +77 -0
- package/src/CascadedShadowMaps.ts +288 -0
- package/src/CloudLayer.ts +85 -0
- package/src/CloudLayers.test.ts +61 -0
- package/src/CloudLayers.ts +181 -0
- package/src/CloudShape.ts +22 -0
- package/src/CloudShapeDetail.ts +22 -0
- package/src/CloudsEffect.ts +810 -0
- package/src/CloudsMaterial.ts +467 -0
- package/src/CloudsPass.ts +285 -0
- package/src/CloudsResolveMaterial.ts +108 -0
- package/src/DensityProfile.ts +38 -0
- package/src/LocalWeather.ts +21 -0
- package/src/PassBase.ts +28 -0
- package/src/Procedural3DTexture.ts +94 -0
- package/src/ProceduralTexture.ts +94 -0
- package/src/ShaderArrayPass.ts +32 -0
- package/src/ShadowMaterial.ts +141 -0
- package/src/ShadowPass.ts +185 -0
- package/src/ShadowResolveMaterial.ts +72 -0
- package/src/Turbulence.ts +21 -0
- package/src/bayer.ts +23 -0
- package/src/constants.ts +8 -0
- package/src/helpers/FrustumCorners.ts +138 -0
- package/src/helpers/setArrayRenderTargetLayers.ts +32 -0
- package/src/helpers/splitFrustum.ts +59 -0
- package/src/index.ts +14 -0
- package/src/qualityPresets.ts +117 -0
- package/src/r3f/CloudLayer.tsx +95 -0
- package/src/r3f/CloudLayers.tsx +54 -0
- package/src/r3f/Clouds.tsx +278 -0
- package/src/r3f/index.ts +2 -0
- package/src/shaders/catmullRomSampling.glsl +113 -0
- package/src/shaders/cloudShape.frag +78 -0
- package/src/shaders/cloudShapeDetail.frag +56 -0
- package/src/shaders/clouds.frag +996 -0
- package/src/shaders/clouds.glsl +190 -0
- package/src/shaders/clouds.vert +69 -0
- package/src/shaders/cloudsEffect.frag +11 -0
- package/src/shaders/cloudsResolve.frag +202 -0
- package/src/shaders/cloudsResolve.vert +10 -0
- package/src/shaders/localWeather.frag +83 -0
- package/src/shaders/parameters.glsl +64 -0
- package/src/shaders/perlin.glsl +211 -0
- package/src/shaders/shadow.frag +197 -0
- package/src/shaders/shadow.vert +16 -0
- package/src/shaders/shadowResolve.frag +76 -0
- package/src/shaders/shadowResolve.vert +10 -0
- package/src/shaders/structuredSampling.glsl +101 -0
- package/src/shaders/tileableNoise.glsl +88 -0
- package/src/shaders/turbulence.frag +51 -0
- package/src/shaders/types.glsl +18 -0
- package/src/shaders/varianceClipping.glsl +114 -0
- package/src/uniforms.ts +218 -0
- package/types/CascadedShadowMaps.d.ts +52 -0
- package/types/CloudLayer.d.ts +26 -0
- package/types/CloudLayers.d.ts +21 -0
- package/types/CloudShape.d.ts +5 -0
- package/types/CloudShapeDetail.d.ts +5 -0
- package/types/CloudsEffect.d.ts +170 -0
- package/types/CloudsMaterial.d.ts +86 -0
- package/types/CloudsPass.d.ts +44 -0
- package/types/CloudsResolveMaterial.d.ts +30 -0
- package/types/DensityProfile.d.ts +12 -0
- package/types/LocalWeather.d.ts +5 -0
- package/types/PassBase.d.ts +14 -0
- package/types/Procedural3DTexture.d.ts +20 -0
- package/types/ProceduralTexture.d.ts +24 -0
- package/types/ShaderArrayPass.d.ts +7 -0
- package/types/ShadowMaterial.d.ts +34 -0
- package/types/ShadowPass.d.ts +34 -0
- package/types/ShadowResolveMaterial.d.ts +20 -0
- package/types/Turbulence.d.ts +5 -0
- package/types/bayer.d.ts +4 -0
- package/types/constants.d.ts +6 -0
- package/types/helpers/FrustumCorners.d.ts +18 -0
- package/types/helpers/setArrayRenderTargetLayers.d.ts +3 -0
- package/types/helpers/splitFrustum.d.ts +9 -0
- package/types/index.d.ts +13 -0
- package/types/qualityPresets.d.ts +46 -0
- package/types/r3f/CloudLayer.d.ts +7 -0
- package/types/r3f/CloudLayers.d.ts +15 -0
- package/types/r3f/Clouds.d.ts +16 -0
- package/types/r3f/index.d.ts +2 -0
- 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
|
+
}
|