@takram/three-clouds 0.2.1 → 0.3.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 +24 -0
- package/README.md +12 -3
- package/build/r3f.cjs +1 -1
- package/build/r3f.cjs.map +1 -1
- package/build/r3f.js +169 -170
- package/build/r3f.js.map +1 -1
- package/build/shared.cjs +50 -37
- package/build/shared.cjs.map +1 -1
- package/build/shared.js +130 -116
- package/build/shared.js.map +1 -1
- package/package.json +3 -3
- package/src/CloudsEffect.ts +3 -1
- package/src/CloudsMaterial.ts +17 -11
- package/src/r3f/CloudLayer.tsx +64 -62
- package/src/r3f/Clouds.tsx +105 -109
- package/src/shaders/clouds.frag +37 -25
- package/src/shaders/clouds.glsl +1 -1
- package/src/shaders/clouds.vert +3 -3
- package/src/shaders/parameters.glsl +2 -1
- package/types/CloudsEffect.d.ts +1 -1
- package/types/CloudsMaterial.d.ts +3 -2
- package/types/r3f/CloudLayer.d.ts +3 -1
- package/types/r3f/Clouds.d.ts +2 -1
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@takram/three-clouds",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.3.0",
|
4
4
|
"description": "A Three.js and R3F implementation of geospatial volumetric clouds",
|
5
5
|
"keywords": [
|
6
6
|
"three",
|
@@ -45,8 +45,8 @@
|
|
45
45
|
"README.md"
|
46
46
|
],
|
47
47
|
"dependencies": {
|
48
|
-
"@takram/three-atmosphere": "0.
|
49
|
-
"@takram/three-geospatial": "0.2.
|
48
|
+
"@takram/three-atmosphere": "0.12.0",
|
49
|
+
"@takram/three-geospatial": "0.2.2",
|
50
50
|
"tiny-invariant": "^1.3.3",
|
51
51
|
"type-fest": "^4.34.1"
|
52
52
|
},
|
package/src/CloudsEffect.ts
CHANGED
@@ -82,7 +82,9 @@ const cloudsUniformKeys = [
|
|
82
82
|
'minShadowLengthStepSize',
|
83
83
|
'maxShadowLengthRayDistance',
|
84
84
|
'hazeDensityScale',
|
85
|
-
'hazeExponent'
|
85
|
+
'hazeExponent',
|
86
|
+
'hazeScatteringCoefficient',
|
87
|
+
'hazeAbsorptionCoefficient'
|
86
88
|
] as const satisfies Array<keyof CloudsMaterialUniforms>
|
87
89
|
|
88
90
|
// prettier-ignore
|
package/src/CloudsMaterial.ts
CHANGED
@@ -97,7 +97,6 @@ export interface CloudsMaterialUniforms
|
|
97
97
|
stbnTexture: Uniform<Data3DTexture | null>
|
98
98
|
|
99
99
|
// Scattering
|
100
|
-
albedo: Uniform<Vector3>
|
101
100
|
skyIrradianceScale: Uniform<number>
|
102
101
|
groundIrradianceScale: Uniform<number>
|
103
102
|
powderScale: Uniform<number>
|
@@ -135,6 +134,8 @@ export interface CloudsMaterialUniforms
|
|
135
134
|
// Haze
|
136
135
|
hazeDensityScale: Uniform<number>
|
137
136
|
hazeExponent: Uniform<number>
|
137
|
+
hazeScatteringCoefficient: Uniform<number>
|
138
|
+
hazeAbsorptionCoefficient: Uniform<number>
|
138
139
|
}
|
139
140
|
|
140
141
|
export class CloudsMaterial extends AtmosphereMaterialBase {
|
@@ -207,9 +208,8 @@ export class CloudsMaterial extends AtmosphereMaterialBase {
|
|
207
208
|
stbnTexture: new Uniform(null),
|
208
209
|
|
209
210
|
// Scattering
|
210
|
-
|
211
|
-
|
212
|
-
groundIrradianceScale: new Uniform(3),
|
211
|
+
skyIrradianceScale: new Uniform(1),
|
212
|
+
groundIrradianceScale: new Uniform(1),
|
213
213
|
powderScale: new Uniform(0.8),
|
214
214
|
powderExponent: new Uniform(150),
|
215
215
|
|
@@ -249,7 +249,9 @@ export class CloudsMaterial extends AtmosphereMaterialBase {
|
|
249
249
|
|
250
250
|
// Haze
|
251
251
|
hazeDensityScale: new Uniform(3e-5),
|
252
|
-
hazeExponent: new Uniform(1e-3)
|
252
|
+
hazeExponent: new Uniform(1e-3),
|
253
|
+
hazeScatteringCoefficient: new Uniform(0.9),
|
254
|
+
hazeAbsorptionCoefficient: new Uniform(0.5),
|
253
255
|
} satisfies Partial<AtmosphereMaterialBaseUniforms> &
|
254
256
|
CloudsMaterialUniforms
|
255
257
|
},
|
@@ -268,12 +270,16 @@ export class CloudsMaterial extends AtmosphereMaterialBase {
|
|
268
270
|
// Disable onBeforeRender in AtmosphereMaterialBase because we're rendering
|
269
271
|
// into fullscreen quad with another camera for the scene projection.
|
270
272
|
|
271
|
-
const
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
273
|
+
const prevLogarithmicDepthBuffer = this.defines.USE_LOGDEPTHBUF != null
|
274
|
+
const nextLogarithmicDepthBuffer =
|
275
|
+
renderer.capabilities.logarithmicDepthBuffer
|
276
|
+
if (nextLogarithmicDepthBuffer !== prevLogarithmicDepthBuffer) {
|
277
|
+
if (nextLogarithmicDepthBuffer) {
|
278
|
+
this.defines.USE_LOGDEPTHBUF = '1'
|
279
|
+
} else {
|
280
|
+
delete this.defines.USE_LOGDEPTHBUF
|
281
|
+
}
|
282
|
+
}
|
277
283
|
|
278
284
|
const prevPowder = this.defines.POWDER != null
|
279
285
|
const nextPowder = this.uniforms.powderScale.value > 0
|
package/src/r3f/CloudLayer.tsx
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
import {
|
2
|
-
forwardRef,
|
3
2
|
useContext,
|
4
3
|
useEffect,
|
5
4
|
useLayoutEffect,
|
6
|
-
useState
|
5
|
+
useState,
|
6
|
+
type FC,
|
7
|
+
type Ref
|
7
8
|
} from 'react'
|
8
9
|
|
9
10
|
import { type ExpandNestedProps } from '@takram/three-geospatial/r3f'
|
@@ -18,78 +19,79 @@ import { CloudLayersContext } from './CloudLayers'
|
|
18
19
|
export interface CloudLayerProps
|
19
20
|
extends CloudLayerLike,
|
20
21
|
ExpandNestedProps<CloudLayerLike, 'densityProfile'> {
|
22
|
+
ref?: Ref<CloudLayerImpl>
|
21
23
|
index?: number
|
22
24
|
}
|
23
25
|
|
24
|
-
export const CloudLayer
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
export const CloudLayer: FC<CloudLayerProps> = ({
|
27
|
+
ref: forwardedRef,
|
28
|
+
index: indexProp,
|
29
|
+
...props
|
30
|
+
}) => {
|
31
|
+
const context = useContext(CloudLayersContext)
|
32
|
+
if (context == null) {
|
33
|
+
throw new Error('CloudLayer can only be used within the Clouds component!')
|
34
|
+
}
|
32
35
|
|
33
|
-
|
34
|
-
|
36
|
+
const { layers, indexPool, disableDefault } = context
|
37
|
+
const [index, setIndex] = useState<number>()
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
}
|
39
|
+
useLayoutEffect(() => {
|
40
|
+
if (indexProp != null) {
|
41
|
+
const poolIndex = indexPool.indexOf(indexProp)
|
42
|
+
if (poolIndex !== -1) {
|
43
|
+
indexPool.splice(poolIndex, 1)
|
44
|
+
setIndex(indexProp)
|
45
|
+
return () => {
|
46
|
+
indexPool.push(indexProp)
|
47
|
+
setIndex(undefined)
|
46
48
|
}
|
47
|
-
} else {
|
48
|
-
// Sorting is just for predictability. Layer order is still not defined,
|
49
|
-
// but it doesn't matter.
|
50
|
-
const index = indexPool.sort((a, b) => a - b).shift()
|
51
|
-
if (index != null) {
|
52
|
-
setIndex(index)
|
53
|
-
return () => {
|
54
|
-
indexPool.push(index)
|
55
|
-
setIndex(undefined)
|
56
|
-
}
|
57
|
-
}
|
58
|
-
}
|
59
|
-
}, [indexProp, layers, indexPool])
|
60
|
-
|
61
|
-
useLayoutEffect(() => {
|
62
|
-
if (index == null) {
|
63
|
-
return
|
64
|
-
}
|
65
|
-
const layer = layers[index]
|
66
|
-
return () => {
|
67
|
-
layer.copy(
|
68
|
-
disableDefault ? CloudLayerImpl.DEFAULT : CloudLayers.DEFAULT[index]
|
69
|
-
)
|
70
|
-
}
|
71
|
-
}, [layers, index, disableDefault])
|
72
|
-
|
73
|
-
useEffect(() => {
|
74
|
-
if (index == null) {
|
75
|
-
return
|
76
49
|
}
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
50
|
+
} else {
|
51
|
+
// Sorting is just for predictability. Layer order is still not defined,
|
52
|
+
// but it doesn't matter.
|
53
|
+
const index = indexPool.sort((a, b) => a - b).shift()
|
54
|
+
if (index != null) {
|
55
|
+
setIndex(index)
|
56
|
+
return () => {
|
57
|
+
indexPool.push(index)
|
58
|
+
setIndex(undefined)
|
59
|
+
}
|
81
60
|
}
|
82
|
-
}
|
61
|
+
}
|
62
|
+
}, [indexProp, layers, indexPool])
|
83
63
|
|
84
|
-
|
85
|
-
if (index
|
86
|
-
|
64
|
+
useLayoutEffect(() => {
|
65
|
+
if (index == null) {
|
66
|
+
return
|
67
|
+
}
|
68
|
+
const layer = layers[index]
|
69
|
+
return () => {
|
87
70
|
layer.copy(
|
88
71
|
disableDefault ? CloudLayerImpl.DEFAULT : CloudLayers.DEFAULT[index]
|
89
72
|
)
|
90
|
-
layer.set(props)
|
91
73
|
}
|
74
|
+
}, [layers, index, disableDefault])
|
75
|
+
|
76
|
+
useEffect(() => {
|
77
|
+
if (index == null) {
|
78
|
+
return
|
79
|
+
}
|
80
|
+
if (typeof forwardedRef === 'function') {
|
81
|
+
forwardedRef(layers[index])
|
82
|
+
} else if (forwardedRef != null) {
|
83
|
+
forwardedRef.current = layers[index]
|
84
|
+
}
|
85
|
+
}, [forwardedRef, layers, index])
|
92
86
|
|
93
|
-
|
87
|
+
// Surely this resets any modifications made via forwarded ref.
|
88
|
+
if (index != null) {
|
89
|
+
const layer = layers[index]
|
90
|
+
layer.copy(
|
91
|
+
disableDefault ? CloudLayerImpl.DEFAULT : CloudLayers.DEFAULT[index]
|
92
|
+
)
|
93
|
+
layer.set(props)
|
94
94
|
}
|
95
|
-
|
95
|
+
|
96
|
+
return null
|
97
|
+
}
|
package/src/r3f/Clouds.tsx
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
import { useFrame, useThree, type ElementProps } from '@react-three/fiber'
|
2
2
|
import { EffectComposerContext } from '@react-three/postprocessing'
|
3
3
|
import {
|
4
|
-
forwardRef,
|
5
4
|
useCallback,
|
6
5
|
useContext,
|
7
6
|
useEffect,
|
8
7
|
useMemo,
|
9
|
-
useState
|
8
|
+
useState,
|
9
|
+
type FC
|
10
10
|
} from 'react'
|
11
11
|
import {
|
12
12
|
LinearFilter,
|
@@ -154,119 +154,115 @@ export interface CloudsProps
|
|
154
154
|
stbnTexture?: Data3DTexture | string
|
155
155
|
}
|
156
156
|
|
157
|
-
export const Clouds
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
) {
|
171
|
-
const { textures, transientStates, ...contextProps } =
|
172
|
-
useContext(AtmosphereContext)
|
157
|
+
export const Clouds: FC<CloudsProps> = ({
|
158
|
+
ref: forwardedRef,
|
159
|
+
disableDefaultLayers = false,
|
160
|
+
localWeatherTexture: localWeatherTextureProp = DEFAULT_LOCAL_WEATHER_URL,
|
161
|
+
shapeTexture: shapeTextureProp = DEFAULT_SHAPE_URL,
|
162
|
+
shapeDetailTexture: shapeDetailTextureProp = DEFAULT_SHAPE_DETAIL_URL,
|
163
|
+
turbulenceTexture: turbulenceTextureProp = DEFAULT_TURBULENCE_URL,
|
164
|
+
stbnTexture: stbnTextureProp = DEFAULT_STBN_URL,
|
165
|
+
children,
|
166
|
+
...props
|
167
|
+
}) => {
|
168
|
+
const { textures, transientStates, ...contextProps } =
|
169
|
+
useContext(AtmosphereContext)
|
173
170
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
171
|
+
const [atmosphereParameters, others] = separateProps({
|
172
|
+
...cloudsPassOptionsDefaults,
|
173
|
+
...contextProps,
|
174
|
+
...textures,
|
175
|
+
...props
|
176
|
+
})
|
180
177
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
178
|
+
const effect = useMemo(() => new CloudsEffect(), [])
|
179
|
+
useEffect(() => {
|
180
|
+
return () => {
|
181
|
+
effect.dispose()
|
182
|
+
}
|
183
|
+
}, [effect])
|
187
184
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
185
|
+
useFrame(() => {
|
186
|
+
if (transientStates != null) {
|
187
|
+
effect.sunDirection.copy(transientStates.sunDirection)
|
188
|
+
effect.ellipsoidCenter.copy(transientStates.ellipsoidCenter)
|
189
|
+
effect.ellipsoidMatrix.copy(transientStates.ellipsoidMatrix)
|
190
|
+
}
|
191
|
+
})
|
195
192
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
}
|
193
|
+
useEffect(() => {
|
194
|
+
if (transientStates != null) {
|
195
|
+
transientStates.overlay = effect.atmosphereOverlay
|
196
|
+
transientStates.shadow = effect.atmosphereShadow
|
197
|
+
transientStates.shadowLength = effect.atmosphereShadowLength
|
198
|
+
return () => {
|
199
|
+
transientStates.overlay = null
|
200
|
+
transientStates.shadow = null
|
201
|
+
transientStates.shadowLength = null
|
206
202
|
}
|
207
|
-
}
|
203
|
+
}
|
204
|
+
}, [effect, transientStates])
|
208
205
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
}
|
225
|
-
},
|
226
|
-
[effect, transientStates]
|
227
|
-
)
|
228
|
-
useEffect(() => {
|
229
|
-
effect.events.addEventListener('change', handleChange)
|
230
|
-
return () => {
|
231
|
-
effect.events.removeEventListener('change', handleChange)
|
206
|
+
const handleChange = useCallback(
|
207
|
+
(event: CloudsEffectChangeEvent) => {
|
208
|
+
if (transientStates == null) {
|
209
|
+
return
|
210
|
+
}
|
211
|
+
switch (event.property) {
|
212
|
+
case 'atmosphereOverlay':
|
213
|
+
transientStates.overlay = effect.atmosphereOverlay
|
214
|
+
break
|
215
|
+
case 'atmosphereShadow':
|
216
|
+
transientStates.shadow = effect.atmosphereShadow
|
217
|
+
break
|
218
|
+
case 'atmosphereShadowLength':
|
219
|
+
transientStates.shadowLength = effect.atmosphereShadowLength
|
220
|
+
break
|
232
221
|
}
|
233
|
-
},
|
222
|
+
},
|
223
|
+
[effect, transientStates]
|
224
|
+
)
|
225
|
+
useEffect(() => {
|
226
|
+
effect.events.addEventListener('change', handleChange)
|
227
|
+
return () => {
|
228
|
+
effect.events.removeEventListener('change', handleChange)
|
229
|
+
}
|
230
|
+
}, [effect, handleChange])
|
234
231
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
232
|
+
const gl = useThree(({ gl }) => gl)
|
233
|
+
const localWeatherTexture = useTextureState(localWeatherTextureProp, gl)
|
234
|
+
const shapeTexture = use3DTextureState(
|
235
|
+
shapeTextureProp,
|
236
|
+
CLOUD_SHAPE_TEXTURE_SIZE
|
237
|
+
)
|
238
|
+
const shapeDetailTexture = use3DTextureState(
|
239
|
+
shapeDetailTextureProp,
|
240
|
+
CLOUD_SHAPE_DETAIL_TEXTURE_SIZE
|
241
|
+
)
|
242
|
+
const turbulenceTexture = useTextureState(turbulenceTextureProp, gl)
|
243
|
+
const stbnTexture = useSTBNTextureState(stbnTextureProp)
|
247
244
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
)
|
245
|
+
const { camera } = useContext(EffectComposerContext)
|
246
|
+
return (
|
247
|
+
<>
|
248
|
+
<primitive
|
249
|
+
ref={forwardedRef}
|
250
|
+
object={effect}
|
251
|
+
mainCamera={camera}
|
252
|
+
{...atmosphereParameters}
|
253
|
+
localWeatherTexture={localWeatherTexture}
|
254
|
+
shapeTexture={shapeTexture}
|
255
|
+
shapeDetailTexture={shapeDetailTexture}
|
256
|
+
turbulenceTexture={turbulenceTexture}
|
257
|
+
stbnTexture={stbnTexture}
|
258
|
+
{...others}
|
259
|
+
/>
|
260
|
+
<CloudLayers
|
261
|
+
layers={effect.cloudLayers}
|
262
|
+
disableDefault={disableDefaultLayers}
|
263
|
+
>
|
264
|
+
{children}
|
265
|
+
</CloudLayers>
|
266
|
+
</>
|
267
|
+
)
|
268
|
+
}
|
package/src/shaders/clouds.frag
CHANGED
@@ -418,6 +418,7 @@ vec3 getGroundSunSkyIrradiance(
|
|
418
418
|
#ifdef ACCURATE_SUN_SKY_IRRADIANCE
|
419
419
|
return GetSunAndSkyIrradiance(
|
420
420
|
(position - surfaceNormal * height) * METER_TO_LENGTH_UNIT,
|
421
|
+
surfaceNormal,
|
421
422
|
sunDirection,
|
422
423
|
skyIrradiance
|
423
424
|
);
|
@@ -429,7 +430,11 @@ vec3 getGroundSunSkyIrradiance(
|
|
429
430
|
|
430
431
|
vec3 getCloudsSunSkyIrradiance(const vec3 position, const float height, out vec3 skyIrradiance) {
|
431
432
|
#ifdef ACCURATE_SUN_SKY_IRRADIANCE
|
432
|
-
return
|
433
|
+
return GetSunAndSkyIrradianceForParticle(
|
434
|
+
position * METER_TO_LENGTH_UNIT,
|
435
|
+
sunDirection,
|
436
|
+
skyIrradiance
|
437
|
+
);
|
433
438
|
#else // ACCURATE_SUN_SKY_IRRADIANCE
|
434
439
|
float alpha = remapClamped(height, minHeight, maxHeight);
|
435
440
|
skyIrradiance = mix(vCloudsIrradiance.minSky, vCloudsIrradiance.maxSky, alpha);
|
@@ -455,10 +460,9 @@ vec3 approximateIrradianceFromGround(
|
|
455
460
|
vec3 skyIrradiance;
|
456
461
|
vec3 sunIrradiance = getGroundSunSkyIrradiance(position, surfaceNormal, height, skyIrradiance);
|
457
462
|
const float groundAlbedo = 0.3;
|
458
|
-
vec3 groundIrradiance = skyIrradiance + (1.0 - coverage) * sunIrradiance
|
459
|
-
vec3
|
460
|
-
|
461
|
-
return albedo * bouncedIrradiance * RECIPROCAL_PI4 * groundIrradianceScale;
|
463
|
+
vec3 groundIrradiance = skyIrradiance + (1.0 - coverage) * sunIrradiance;
|
464
|
+
vec3 bouncedRadiance = groundAlbedo * RECIPROCAL_PI * groundIrradiance;
|
465
|
+
return bouncedRadiance * exp(-opticalDepthToGround);
|
462
466
|
}
|
463
467
|
#endif // GROUND_IRRADIANCE
|
464
468
|
|
@@ -548,28 +552,28 @@ vec4 marchClouds(
|
|
548
552
|
);
|
549
553
|
}
|
550
554
|
|
551
|
-
|
552
|
-
vec3 radiance = albedo * sunIrradiance * scattering;
|
555
|
+
vec3 radiance = sunIrradiance * approximateMultipleScattering(opticalDepth, cosTheta);
|
553
556
|
|
554
557
|
#ifdef GROUND_IRRADIANCE
|
555
558
|
// Fudge factor for the irradiance from ground.
|
556
559
|
if (height < shadowTopHeight && mipLevel < 0.5) {
|
557
|
-
|
560
|
+
vec3 groundIrradiance = approximateIrradianceFromGround(
|
558
561
|
position,
|
559
562
|
surfaceNormal,
|
560
563
|
height,
|
561
564
|
mipLevel,
|
562
565
|
jitter
|
563
566
|
);
|
567
|
+
radiance += groundIrradiance * RECIPROCAL_PI4 * groundIrradianceScale;
|
564
568
|
}
|
565
569
|
#endif // GROUND_IRRADIANCE
|
566
570
|
|
567
571
|
// Crude approximation of sky gradient. Better than none in the shadows.
|
568
|
-
float skyGradient = dot(0.5 +
|
569
|
-
radiance +=
|
572
|
+
float skyGradient = dot(weather.heightFraction * 0.5 + 0.5, media.weight);
|
573
|
+
radiance += skyIrradiance * RECIPROCAL_PI4 * skyGradient * skyIrradianceScale;
|
570
574
|
|
571
|
-
// Finally multiply by
|
572
|
-
radiance *= media.
|
575
|
+
// Finally multiply by scattering.
|
576
|
+
radiance *= media.scattering;
|
573
577
|
|
574
578
|
#ifdef POWDER
|
575
579
|
radiance *= 1.0 - powderScale * exp(-media.extinction * powderExponent);
|
@@ -622,7 +626,7 @@ float marchShadowLength(
|
|
622
626
|
float maxRayDistance = rayNearFar.y - rayNearFar.x;
|
623
627
|
float stepSize = minShadowLengthStepSize;
|
624
628
|
float rayDistance = stepSize * jitter;
|
625
|
-
const float attenuationFactor = 1.0 -
|
629
|
+
const float attenuationFactor = 1.0 - 5e-4;
|
626
630
|
float attenuation = 1.0;
|
627
631
|
|
628
632
|
// TODO: This march is closed, and sample resolution can be much lower.
|
@@ -668,25 +672,33 @@ vec4 approximateHaze(
|
|
668
672
|
return vec4(0.0); // Prevent artifact in views from space
|
669
673
|
}
|
670
674
|
|
675
|
+
// Blend two normals by the difference in angle so that normal near the
|
676
|
+
// ground becomes that of the origin, and in the sky that of the horizon.
|
677
|
+
vec3 normalAtOrigin = normalize(rayOrigin);
|
678
|
+
vec3 normalAtHorizon = (rayOrigin - dot(rayOrigin, rayDirection) * rayDirection) / bottomRadius;
|
679
|
+
float alpha = remapClamped(dot(normalAtOrigin, normalAtHorizon), 0.9, 1.0);
|
680
|
+
vec3 normal = mix(normalAtOrigin, normalAtHorizon, alpha);
|
681
|
+
|
671
682
|
// Analytical optical depth where density exponentially decreases with height.
|
672
683
|
// Based on: https://iquilezles.org/articles/fog/
|
673
|
-
float angle = max(dot(
|
684
|
+
float angle = max(dot(normal, rayDirection), 1e-5);
|
674
685
|
float exponent = angle * hazeExponent;
|
686
|
+
float linearTerm = density / hazeExponent / angle;
|
687
|
+
|
675
688
|
// Derive the optical depths separately for with and without shadow length.
|
676
689
|
float expTerm = 1.0 - exp(-maxRayDistance * exponent);
|
677
690
|
float shadowExpTerm = 1.0 - exp(-min(maxRayDistance, shadowLength) * exponent);
|
678
|
-
float linearTerm = density / hazeExponent / angle;
|
679
691
|
float opticalDepth = expTerm * linearTerm;
|
680
|
-
float
|
692
|
+
float shadowOpticalDepth = max((expTerm - shadowExpTerm) * linearTerm, 0.0);
|
693
|
+
float transmittance = saturate(1.0 - exp(-opticalDepth));
|
694
|
+
float shadowTransmittance = saturate(1.0 - exp(-shadowOpticalDepth));
|
681
695
|
|
682
696
|
vec3 skyIrradiance = vGroundIrradiance.sky;
|
683
697
|
vec3 sunIrradiance = vGroundIrradiance.sun;
|
684
|
-
vec3
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
// Inscatter is attenuated by shadow length, but transmittance is not.
|
689
|
-
return vec4(inscatter, saturate(1.0 - exp(-opticalDepth)));
|
698
|
+
vec3 inscatter = sunIrradiance * phaseFunction(cosTheta) * shadowTransmittance;
|
699
|
+
inscatter += skyIrradiance * RECIPROCAL_PI4 * skyIrradianceScale * transmittance;
|
700
|
+
inscatter *= hazeScatteringCoefficient / (hazeAbsorptionCoefficient + hazeScatteringCoefficient);
|
701
|
+
return vec4(inscatter, transmittance);
|
690
702
|
}
|
691
703
|
|
692
704
|
#endif // HAZE
|
@@ -705,8 +717,7 @@ void applyAerialPerspective(
|
|
705
717
|
sunDirection,
|
706
718
|
transmittance
|
707
719
|
);
|
708
|
-
|
709
|
-
color.rgb = mix(vec3(0.0), color.rgb * transmittance / clampedAlpha + inscatter, color.a);
|
720
|
+
color.rgb = color.rgb * transmittance + inscatter * color.a;
|
710
721
|
}
|
711
722
|
|
712
723
|
bool rayIntersectsGround(const vec3 cameraPosition, const vec3 rayDirection) {
|
@@ -985,7 +996,8 @@ void main() {
|
|
985
996
|
cosTheta,
|
986
997
|
shadowLength
|
987
998
|
);
|
988
|
-
color = color
|
999
|
+
color.rgb = mix(color.rgb, haze.rgb, haze.a);
|
1000
|
+
color.a = color.a * (1.0 - haze.a) + haze.a;
|
989
1001
|
#endif // HAZE
|
990
1002
|
|
991
1003
|
outputColor = color;
|
package/src/shaders/clouds.glsl
CHANGED
@@ -167,7 +167,7 @@ MediaSample sampleMedia(
|
|
167
167
|
}
|
168
168
|
#endif // SHAPE_DETAIL
|
169
169
|
|
170
|
-
//
|
170
|
+
// Apply the density profiles.
|
171
171
|
density = saturate(density * densityScales * getLayerDensity(weather.heightFraction));
|
172
172
|
|
173
173
|
MediaSample media;
|
package/src/shaders/clouds.vert
CHANGED
@@ -32,7 +32,7 @@ out GroundIrradiance vGroundIrradiance;
|
|
32
32
|
out CloudsIrradiance vCloudsIrradiance;
|
33
33
|
|
34
34
|
void sampleSunSkyIrradiance(const vec3 positionECEF) {
|
35
|
-
vGroundIrradiance.sun =
|
35
|
+
vGroundIrradiance.sun = GetSunAndSkyIrradianceForParticle(
|
36
36
|
positionECEF * METER_TO_LENGTH_UNIT,
|
37
37
|
sunDirection,
|
38
38
|
vGroundIrradiance.sky
|
@@ -40,12 +40,12 @@ void sampleSunSkyIrradiance(const vec3 positionECEF) {
|
|
40
40
|
|
41
41
|
vec3 surfaceNormal = normalize(positionECEF);
|
42
42
|
vec2 radii = (bottomRadius + vec2(minHeight, maxHeight)) * METER_TO_LENGTH_UNIT;
|
43
|
-
vCloudsIrradiance.minSun =
|
43
|
+
vCloudsIrradiance.minSun = GetSunAndSkyIrradianceForParticle(
|
44
44
|
surfaceNormal * radii.x,
|
45
45
|
sunDirection,
|
46
46
|
vCloudsIrradiance.minSky
|
47
47
|
);
|
48
|
-
vCloudsIrradiance.maxSun =
|
48
|
+
vCloudsIrradiance.maxSun = GetSunAndSkyIrradianceForParticle(
|
49
49
|
surfaceNormal * radii.y,
|
50
50
|
sunDirection,
|
51
51
|
vCloudsIrradiance.maxSky
|