@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,117 @@
|
|
1
|
+
import { Vector2 } from 'three'
|
2
|
+
import { type PartialDeep, type SharedUnionFieldsDeep } from 'type-fest'
|
3
|
+
|
4
|
+
import { type CloudsEffect } from './CloudsEffect'
|
5
|
+
|
6
|
+
export type QualityPreset = 'low' | 'medium' | 'high' | 'ultra'
|
7
|
+
|
8
|
+
const values = {
|
9
|
+
resolutionScale: 1,
|
10
|
+
lightShafts: true,
|
11
|
+
shapeDetail: true,
|
12
|
+
turbulence: true,
|
13
|
+
haze: true,
|
14
|
+
clouds: {
|
15
|
+
multiScatteringOctaves: 8,
|
16
|
+
accurateSunSkyIrradiance: true,
|
17
|
+
accuratePhaseFunction: false,
|
18
|
+
|
19
|
+
// Primary raymarch
|
20
|
+
maxIterationCount: 500,
|
21
|
+
minStepSize: 50,
|
22
|
+
maxStepSize: 1000,
|
23
|
+
maxRayDistance: 2e5,
|
24
|
+
perspectiveStepScale: 1.01,
|
25
|
+
minDensity: 1e-5,
|
26
|
+
minExtinction: 1e-5,
|
27
|
+
minTransmittance: 1e-2,
|
28
|
+
|
29
|
+
// Secondary raymarch
|
30
|
+
maxIterationCountToGround: 3,
|
31
|
+
maxIterationCountToSun: 2,
|
32
|
+
minSecondaryStepSize: 100,
|
33
|
+
secondaryStepScale: 2,
|
34
|
+
|
35
|
+
// Shadow length
|
36
|
+
maxShadowLengthIterationCount: 500,
|
37
|
+
minShadowLengthStepSize: 50,
|
38
|
+
maxShadowLengthRayDistance: 2e5
|
39
|
+
},
|
40
|
+
shadow: {
|
41
|
+
cascadeCount: 3,
|
42
|
+
mapSize: /*#__PURE__*/ new Vector2(512, 512),
|
43
|
+
|
44
|
+
// Primary raymarch
|
45
|
+
maxIterationCount: 50,
|
46
|
+
minStepSize: 100,
|
47
|
+
maxStepSize: 1000,
|
48
|
+
minDensity: 1e-5,
|
49
|
+
minExtinction: 1e-5,
|
50
|
+
minTransmittance: 1e-4
|
51
|
+
}
|
52
|
+
} satisfies PartialDeep<CloudsEffect>
|
53
|
+
|
54
|
+
// Relax types narrowed down by satisfies operator.
|
55
|
+
type Schema = SharedUnionFieldsDeep<typeof values | CloudsEffect>
|
56
|
+
|
57
|
+
export const defaults: Schema = values
|
58
|
+
|
59
|
+
export const qualityPresets: Record<QualityPreset, Schema> = {
|
60
|
+
// TODO: We cloud decrease multi-scattering octaves for lower quality presets,
|
61
|
+
// but it leads to a loss of higher frequency scattering, making it darker
|
62
|
+
// overall, which suggests the need for a fudge factor to scale the radiance.
|
63
|
+
low: {
|
64
|
+
...defaults,
|
65
|
+
lightShafts: false, // Expensive
|
66
|
+
shapeDetail: false, // Expensive
|
67
|
+
turbulence: false, // Expensive
|
68
|
+
clouds: {
|
69
|
+
...defaults.clouds,
|
70
|
+
accurateSunSkyIrradiance: false, // Greatly reduces texel reads.
|
71
|
+
maxIterationCount: 200,
|
72
|
+
minStepSize: 100,
|
73
|
+
maxRayDistance: 1e5,
|
74
|
+
minDensity: 1e-4,
|
75
|
+
minExtinction: 1e-4,
|
76
|
+
minTransmittance: 1e-1, // Makes the primary march terminate earlier.
|
77
|
+
maxIterationCountToGround: 0, // Expensive
|
78
|
+
maxIterationCountToSun: 1 // Only 1 march makes big difference
|
79
|
+
},
|
80
|
+
shadow: {
|
81
|
+
...defaults.shadow,
|
82
|
+
maxIterationCount: 25,
|
83
|
+
minDensity: 1e-4,
|
84
|
+
minExtinction: 1e-4,
|
85
|
+
minTransmittance: 1e-2, // Makes the primary march terminate earlier.
|
86
|
+
cascadeCount: 2, // Obvious
|
87
|
+
mapSize: /*#__PURE__*/ new Vector2(256, 256) // Obvious
|
88
|
+
}
|
89
|
+
},
|
90
|
+
medium: {
|
91
|
+
...defaults,
|
92
|
+
lightShafts: false, // Expensive
|
93
|
+
turbulence: false, // Expensive
|
94
|
+
clouds: {
|
95
|
+
...defaults.clouds,
|
96
|
+
minDensity: 1e-4,
|
97
|
+
minExtinction: 1e-4,
|
98
|
+
accurateSunSkyIrradiance: false,
|
99
|
+
maxIterationCountToSun: 2,
|
100
|
+
maxIterationCountToGround: 1
|
101
|
+
},
|
102
|
+
shadow: {
|
103
|
+
...defaults.shadow,
|
104
|
+
minDensity: 1e-4,
|
105
|
+
minExtinction: 1e-4,
|
106
|
+
mapSize: /*#__PURE__*/ new Vector2(256, 256)
|
107
|
+
}
|
108
|
+
},
|
109
|
+
high: defaults, // Consider high quality preset as default.
|
110
|
+
ultra: {
|
111
|
+
...defaults,
|
112
|
+
shadow: {
|
113
|
+
...defaults.shadow,
|
114
|
+
mapSize: /*#__PURE__*/ new Vector2(1024, 1024)
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import {
|
2
|
+
forwardRef,
|
3
|
+
useContext,
|
4
|
+
useEffect,
|
5
|
+
useLayoutEffect,
|
6
|
+
useState
|
7
|
+
} from 'react'
|
8
|
+
|
9
|
+
import { type ExpandNestedProps } from '@takram/three-geospatial/r3f'
|
10
|
+
|
11
|
+
import {
|
12
|
+
CloudLayer as CloudLayerImpl,
|
13
|
+
type CloudLayerLike
|
14
|
+
} from '../CloudLayer'
|
15
|
+
import { CloudLayers } from '../CloudLayers'
|
16
|
+
import { CloudLayersContext } from './CloudLayers'
|
17
|
+
|
18
|
+
export interface CloudLayerProps
|
19
|
+
extends CloudLayerLike,
|
20
|
+
ExpandNestedProps<CloudLayerLike, 'densityProfile'> {
|
21
|
+
index?: number
|
22
|
+
}
|
23
|
+
|
24
|
+
export const CloudLayer = forwardRef<CloudLayerImpl, CloudLayerProps>(
|
25
|
+
function CloudLayer({ index: indexProp, ...props }, forwardedRef) {
|
26
|
+
const context = useContext(CloudLayersContext)
|
27
|
+
if (context == null) {
|
28
|
+
throw new Error(
|
29
|
+
'CloudLayer can only be used within the Clouds component!'
|
30
|
+
)
|
31
|
+
}
|
32
|
+
|
33
|
+
const { layers, indexPool, disableDefault } = context
|
34
|
+
const [index, setIndex] = useState<number>()
|
35
|
+
|
36
|
+
useLayoutEffect(() => {
|
37
|
+
if (indexProp != null) {
|
38
|
+
const poolIndex = indexPool.indexOf(indexProp)
|
39
|
+
if (poolIndex !== -1) {
|
40
|
+
indexPool.splice(poolIndex, 1)
|
41
|
+
setIndex(indexProp)
|
42
|
+
return () => {
|
43
|
+
indexPool.push(indexProp)
|
44
|
+
setIndex(undefined)
|
45
|
+
}
|
46
|
+
}
|
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
|
+
}
|
77
|
+
if (typeof forwardedRef === 'function') {
|
78
|
+
forwardedRef(layers[index])
|
79
|
+
} else if (forwardedRef != null) {
|
80
|
+
forwardedRef.current = layers[index]
|
81
|
+
}
|
82
|
+
}, [forwardedRef, layers, index])
|
83
|
+
|
84
|
+
// Surely this resets any modifications made via forwarded ref.
|
85
|
+
if (index != null) {
|
86
|
+
const layer = layers[index]
|
87
|
+
layer.copy(
|
88
|
+
disableDefault ? CloudLayerImpl.DEFAULT : CloudLayers.DEFAULT[index]
|
89
|
+
)
|
90
|
+
layer.set(props)
|
91
|
+
}
|
92
|
+
|
93
|
+
return null
|
94
|
+
}
|
95
|
+
)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import {
|
2
|
+
createContext,
|
3
|
+
useLayoutEffect,
|
4
|
+
useState,
|
5
|
+
type FC,
|
6
|
+
type ReactNode
|
7
|
+
} from 'react'
|
8
|
+
|
9
|
+
import { CloudLayer } from '../CloudLayer'
|
10
|
+
import { CloudLayers as CloudLayersImpl } from '../CloudLayers'
|
11
|
+
|
12
|
+
export interface CloudLayersContextValue {
|
13
|
+
layers: CloudLayersImpl
|
14
|
+
indexPool: number[]
|
15
|
+
disableDefault: boolean
|
16
|
+
}
|
17
|
+
|
18
|
+
export const CloudLayersContext =
|
19
|
+
/*#__PURE__*/ createContext<CloudLayersContextValue | null>(null)
|
20
|
+
|
21
|
+
export interface CloudLayersProps {
|
22
|
+
layers: CloudLayersImpl
|
23
|
+
disableDefault?: boolean
|
24
|
+
children?: ReactNode
|
25
|
+
}
|
26
|
+
|
27
|
+
export const CloudLayers: FC<CloudLayersProps> = ({
|
28
|
+
layers,
|
29
|
+
disableDefault = false,
|
30
|
+
children
|
31
|
+
}) => {
|
32
|
+
const [context, setContext] = useState<CloudLayersContextValue>()
|
33
|
+
|
34
|
+
useLayoutEffect(() => {
|
35
|
+
layers.set(
|
36
|
+
disableDefault
|
37
|
+
? Array(4).fill(CloudLayer.DEFAULT)
|
38
|
+
: CloudLayersImpl.DEFAULT
|
39
|
+
)
|
40
|
+
setContext({
|
41
|
+
layers,
|
42
|
+
indexPool: [0, 1, 2, 3],
|
43
|
+
disableDefault
|
44
|
+
})
|
45
|
+
}, [layers, disableDefault])
|
46
|
+
|
47
|
+
return (
|
48
|
+
context != null && (
|
49
|
+
<CloudLayersContext.Provider value={context}>
|
50
|
+
{children}
|
51
|
+
</CloudLayersContext.Provider>
|
52
|
+
)
|
53
|
+
)
|
54
|
+
}
|
@@ -0,0 +1,278 @@
|
|
1
|
+
import { useFrame, useThree } from '@react-three/fiber'
|
2
|
+
import { EffectComposerContext } from '@react-three/postprocessing'
|
3
|
+
import {
|
4
|
+
forwardRef,
|
5
|
+
useCallback,
|
6
|
+
useContext,
|
7
|
+
useEffect,
|
8
|
+
useMemo,
|
9
|
+
useState
|
10
|
+
} from 'react'
|
11
|
+
import {
|
12
|
+
LinearFilter,
|
13
|
+
LinearMipMapLinearFilter,
|
14
|
+
NoColorSpace,
|
15
|
+
RedFormat,
|
16
|
+
RepeatWrapping,
|
17
|
+
TextureLoader,
|
18
|
+
type Data3DTexture,
|
19
|
+
type Texture,
|
20
|
+
type WebGLRenderer
|
21
|
+
} from 'three'
|
22
|
+
|
23
|
+
import { AtmosphereContext, separateProps } from '@takram/three-atmosphere/r3f'
|
24
|
+
import {
|
25
|
+
createData3DTextureLoaderClass,
|
26
|
+
DEFAULT_STBN_URL,
|
27
|
+
parseUint8Array,
|
28
|
+
STBNLoader
|
29
|
+
} from '@takram/three-geospatial'
|
30
|
+
import {
|
31
|
+
type ExpandNestedProps,
|
32
|
+
type PassThoughInstanceProps
|
33
|
+
} from '@takram/three-geospatial/r3f'
|
34
|
+
|
35
|
+
import {
|
36
|
+
CloudsEffect,
|
37
|
+
cloudsPassOptionsDefaults,
|
38
|
+
type CloudsEffectChangeEvent
|
39
|
+
} from '../CloudsEffect'
|
40
|
+
import {
|
41
|
+
CLOUD_SHAPE_DETAIL_TEXTURE_SIZE,
|
42
|
+
CLOUD_SHAPE_TEXTURE_SIZE,
|
43
|
+
DEFAULT_LOCAL_WEATHER_URL,
|
44
|
+
DEFAULT_SHAPE_DETAIL_URL,
|
45
|
+
DEFAULT_SHAPE_URL,
|
46
|
+
DEFAULT_TURBULENCE_URL
|
47
|
+
} from '../constants'
|
48
|
+
import { type Procedural3DTexture } from '../Procedural3DTexture'
|
49
|
+
import { type ProceduralTexture } from '../ProceduralTexture'
|
50
|
+
import { CloudLayers } from './CloudLayers'
|
51
|
+
|
52
|
+
function useTextureState(
|
53
|
+
input: string | Texture | ProceduralTexture,
|
54
|
+
gl: WebGLRenderer
|
55
|
+
): Texture | ProceduralTexture | null {
|
56
|
+
const [data, setData] = useState(typeof input !== 'string' ? input : null)
|
57
|
+
useEffect(() => {
|
58
|
+
if (typeof input === 'string') {
|
59
|
+
const loader = new TextureLoader()
|
60
|
+
;(async () => {
|
61
|
+
const texture = await loader.loadAsync(input)
|
62
|
+
texture.minFilter = LinearMipMapLinearFilter
|
63
|
+
texture.magFilter = LinearFilter
|
64
|
+
texture.wrapS = RepeatWrapping
|
65
|
+
texture.wrapT = RepeatWrapping
|
66
|
+
texture.colorSpace = NoColorSpace
|
67
|
+
texture.needsUpdate = true
|
68
|
+
|
69
|
+
// WORKAROUND: The color space resets to sRGB for unknown reason, unless
|
70
|
+
// the texture is initialized here.
|
71
|
+
gl.initTexture(texture)
|
72
|
+
|
73
|
+
setData(texture)
|
74
|
+
})().catch(error => {
|
75
|
+
console.error(error)
|
76
|
+
})
|
77
|
+
} else {
|
78
|
+
setData(input)
|
79
|
+
}
|
80
|
+
}, [input, gl])
|
81
|
+
|
82
|
+
return data
|
83
|
+
}
|
84
|
+
|
85
|
+
function use3DTextureState(
|
86
|
+
input: string | Data3DTexture | Procedural3DTexture,
|
87
|
+
size: number
|
88
|
+
): Data3DTexture | Procedural3DTexture | null {
|
89
|
+
const [data, setData] = useState(typeof input !== 'string' ? input : null)
|
90
|
+
useEffect(() => {
|
91
|
+
if (typeof input === 'string') {
|
92
|
+
const Loader = createData3DTextureLoaderClass(parseUint8Array, {
|
93
|
+
width: size,
|
94
|
+
height: size,
|
95
|
+
depth: size,
|
96
|
+
format: RedFormat,
|
97
|
+
minFilter: LinearFilter,
|
98
|
+
magFilter: LinearFilter,
|
99
|
+
wrapS: RepeatWrapping,
|
100
|
+
wrapT: RepeatWrapping,
|
101
|
+
wrapR: RepeatWrapping,
|
102
|
+
colorSpace: NoColorSpace
|
103
|
+
})
|
104
|
+
const loader = new Loader()
|
105
|
+
;(async () => {
|
106
|
+
setData(await loader.loadAsync(input))
|
107
|
+
})().catch(error => {
|
108
|
+
console.error(error)
|
109
|
+
})
|
110
|
+
} else {
|
111
|
+
setData(input)
|
112
|
+
}
|
113
|
+
}, [input, size])
|
114
|
+
|
115
|
+
return data
|
116
|
+
}
|
117
|
+
|
118
|
+
function useSTBNTextureState(
|
119
|
+
input: string | Data3DTexture
|
120
|
+
): Data3DTexture | null {
|
121
|
+
const [data, setData] = useState(typeof input !== 'string' ? input : null)
|
122
|
+
useEffect(() => {
|
123
|
+
if (typeof input === 'string') {
|
124
|
+
const loader = new STBNLoader()
|
125
|
+
;(async () => {
|
126
|
+
setData(await loader.loadAsync(input))
|
127
|
+
})().catch(error => {
|
128
|
+
console.error(error)
|
129
|
+
})
|
130
|
+
} else {
|
131
|
+
setData(input)
|
132
|
+
}
|
133
|
+
}, [input])
|
134
|
+
|
135
|
+
return data
|
136
|
+
}
|
137
|
+
|
138
|
+
export interface CloudsProps
|
139
|
+
extends Omit<
|
140
|
+
PassThoughInstanceProps<
|
141
|
+
CloudsEffect,
|
142
|
+
[],
|
143
|
+
Partial<
|
144
|
+
CloudsEffect &
|
145
|
+
ExpandNestedProps<CloudsEffect, 'clouds'> &
|
146
|
+
ExpandNestedProps<CloudsEffect, 'shadow'>
|
147
|
+
>
|
148
|
+
>,
|
149
|
+
| 'localWeatherTexture'
|
150
|
+
| 'shapeTexture'
|
151
|
+
| 'shapeDetailTexture'
|
152
|
+
| 'turbulenceTexture'
|
153
|
+
| 'stbnTexture'
|
154
|
+
> {
|
155
|
+
disableDefaultLayers?: boolean
|
156
|
+
localWeatherTexture?: Texture | ProceduralTexture | string
|
157
|
+
shapeTexture?: Data3DTexture | Procedural3DTexture | string
|
158
|
+
shapeDetailTexture?: Data3DTexture | Procedural3DTexture | string
|
159
|
+
turbulenceTexture?: Texture | ProceduralTexture | string
|
160
|
+
stbnTexture?: Data3DTexture | string
|
161
|
+
}
|
162
|
+
|
163
|
+
export const Clouds = /*#__PURE__*/ forwardRef<CloudsEffect, CloudsProps>(
|
164
|
+
function Clouds(
|
165
|
+
{
|
166
|
+
disableDefaultLayers = false,
|
167
|
+
localWeatherTexture: localWeatherTextureProp = DEFAULT_LOCAL_WEATHER_URL,
|
168
|
+
shapeTexture: shapeTextureProp = DEFAULT_SHAPE_URL,
|
169
|
+
shapeDetailTexture: shapeDetailTextureProp = DEFAULT_SHAPE_DETAIL_URL,
|
170
|
+
turbulenceTexture: turbulenceTextureProp = DEFAULT_TURBULENCE_URL,
|
171
|
+
stbnTexture: stbnTextureProp = DEFAULT_STBN_URL,
|
172
|
+
children,
|
173
|
+
...props
|
174
|
+
},
|
175
|
+
forwardedRef
|
176
|
+
) {
|
177
|
+
const { textures, transientStates, ...contextProps } =
|
178
|
+
useContext(AtmosphereContext)
|
179
|
+
|
180
|
+
const [atmosphereParameters, others] = separateProps({
|
181
|
+
...cloudsPassOptionsDefaults,
|
182
|
+
...contextProps,
|
183
|
+
...textures,
|
184
|
+
...props
|
185
|
+
})
|
186
|
+
|
187
|
+
const effect = useMemo(() => new CloudsEffect(), [])
|
188
|
+
useEffect(() => {
|
189
|
+
return () => {
|
190
|
+
effect.dispose()
|
191
|
+
}
|
192
|
+
}, [effect])
|
193
|
+
|
194
|
+
useFrame(() => {
|
195
|
+
if (transientStates != null) {
|
196
|
+
effect.sunDirection.copy(transientStates.sunDirection)
|
197
|
+
effect.ellipsoidCenter.copy(transientStates.ellipsoidCenter)
|
198
|
+
effect.ellipsoidMatrix.copy(transientStates.ellipsoidMatrix)
|
199
|
+
}
|
200
|
+
})
|
201
|
+
|
202
|
+
useEffect(() => {
|
203
|
+
if (transientStates != null) {
|
204
|
+
transientStates.overlay = effect.atmosphereOverlay
|
205
|
+
transientStates.shadow = effect.atmosphereShadow
|
206
|
+
transientStates.shadowLength = effect.atmosphereShadowLength
|
207
|
+
return () => {
|
208
|
+
transientStates.overlay = null
|
209
|
+
transientStates.shadow = null
|
210
|
+
transientStates.shadowLength = null
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}, [effect, transientStates])
|
214
|
+
|
215
|
+
const handleChange = useCallback(
|
216
|
+
(event: CloudsEffectChangeEvent) => {
|
217
|
+
if (transientStates == null) {
|
218
|
+
return
|
219
|
+
}
|
220
|
+
switch (event.property) {
|
221
|
+
case 'atmosphereOverlay':
|
222
|
+
transientStates.overlay = effect.atmosphereOverlay
|
223
|
+
break
|
224
|
+
case 'atmosphereShadow':
|
225
|
+
transientStates.shadow = effect.atmosphereShadow
|
226
|
+
break
|
227
|
+
case 'atmosphereShadowLength':
|
228
|
+
transientStates.shadowLength = effect.atmosphereShadowLength
|
229
|
+
break
|
230
|
+
}
|
231
|
+
},
|
232
|
+
[effect, transientStates]
|
233
|
+
)
|
234
|
+
useEffect(() => {
|
235
|
+
effect.events.addEventListener('change', handleChange)
|
236
|
+
return () => {
|
237
|
+
effect.events.removeEventListener('change', handleChange)
|
238
|
+
}
|
239
|
+
}, [effect, handleChange])
|
240
|
+
|
241
|
+
const gl = useThree(({ gl }) => gl)
|
242
|
+
const localWeatherTexture = useTextureState(localWeatherTextureProp, gl)
|
243
|
+
const shapeTexture = use3DTextureState(
|
244
|
+
shapeTextureProp,
|
245
|
+
CLOUD_SHAPE_TEXTURE_SIZE
|
246
|
+
)
|
247
|
+
const shapeDetailTexture = use3DTextureState(
|
248
|
+
shapeDetailTextureProp,
|
249
|
+
CLOUD_SHAPE_DETAIL_TEXTURE_SIZE
|
250
|
+
)
|
251
|
+
const turbulenceTexture = useTextureState(turbulenceTextureProp, gl)
|
252
|
+
const stbnTexture = useSTBNTextureState(stbnTextureProp)
|
253
|
+
|
254
|
+
const { camera } = useContext(EffectComposerContext)
|
255
|
+
return (
|
256
|
+
<>
|
257
|
+
<primitive
|
258
|
+
ref={forwardedRef}
|
259
|
+
object={effect}
|
260
|
+
mainCamera={camera}
|
261
|
+
{...atmosphereParameters}
|
262
|
+
localWeatherTexture={localWeatherTexture}
|
263
|
+
shapeTexture={shapeTexture}
|
264
|
+
shapeDetailTexture={shapeDetailTexture}
|
265
|
+
turbulenceTexture={turbulenceTexture}
|
266
|
+
stbnTexture={stbnTexture}
|
267
|
+
{...others}
|
268
|
+
/>
|
269
|
+
<CloudLayers
|
270
|
+
layers={effect.cloudLayers}
|
271
|
+
disableDefault={disableDefaultLayers}
|
272
|
+
>
|
273
|
+
{children}
|
274
|
+
</CloudLayers>
|
275
|
+
</>
|
276
|
+
)
|
277
|
+
}
|
278
|
+
)
|
package/src/r3f/index.ts
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
// Taken from https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1
|
2
|
+
// TODO: Use 5-taps version: https://www.shadertoy.com/view/MtVGWz
|
3
|
+
// Or even 4 taps (requires preprocessing in the input buffer):
|
4
|
+
// https://www.shadertoy.com/view/4tyGDD
|
5
|
+
|
6
|
+
/**
|
7
|
+
* MIT License
|
8
|
+
*
|
9
|
+
* Copyright (c) 2019 MJP
|
10
|
+
*
|
11
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
12
|
+
* of this software and associated documentation files (the "Software"), to deal
|
13
|
+
* in the Software without restriction, including without limitation the rights
|
14
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
15
|
+
* copies of the Software, and to permit persons to whom the Software is
|
16
|
+
* furnished to do so, subject to the following conditions:
|
17
|
+
*
|
18
|
+
* The above copyright notice and this permission notice shall be included in all
|
19
|
+
* copies or substantial portions of the Software.
|
20
|
+
*
|
21
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
22
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
23
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
24
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
25
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
26
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
27
|
+
* SOFTWARE.
|
28
|
+
*/
|
29
|
+
|
30
|
+
vec4 textureCatmullRom(sampler2D tex, vec2 uv) {
|
31
|
+
vec2 texSize = vec2(textureSize(tex, 0));
|
32
|
+
|
33
|
+
// We're going to sample a a 4x4 grid of texels surrounding the target UV
|
34
|
+
// coordinate. We'll do this by rounding down the sample location to get the
|
35
|
+
// exact center of our "starting" texel. The starting texel will be at
|
36
|
+
// location [1, 1] in the grid, where [0, 0] is the top left corner.
|
37
|
+
vec2 samplePos = uv * texSize;
|
38
|
+
vec2 texPos1 = floor(samplePos - 0.5) + 0.5;
|
39
|
+
|
40
|
+
// Compute the fractional offset from our starting texel to our original
|
41
|
+
// sample location, which we'll feed into the Catmull-Rom spline function to
|
42
|
+
// get our filter weights.
|
43
|
+
vec2 f = samplePos - texPos1;
|
44
|
+
|
45
|
+
// Compute the Catmull-Rom weights using the fractional offset that we
|
46
|
+
// calculated earlier. These equations are pre-expanded based on our knowledge
|
47
|
+
// of where the texels will be located, which lets us avoid having to evaluate
|
48
|
+
// a piece-wise function.
|
49
|
+
vec2 w0 = f * (-0.5 + f * (1.0 - 0.5 * f));
|
50
|
+
vec2 w1 = 1.0 + f * f * (-2.5 + 1.5 * f);
|
51
|
+
vec2 w2 = f * (0.5 + f * (2.0 - 1.5 * f));
|
52
|
+
vec2 w3 = f * f * (-0.5 + 0.5 * f);
|
53
|
+
|
54
|
+
// Work out weighting factors and sampling offsets that will let us use
|
55
|
+
// bilinear filtering to simultaneously evaluate the middle 2 samples from the
|
56
|
+
// 4x4 grid.
|
57
|
+
vec2 w12 = w1 + w2;
|
58
|
+
vec2 offset12 = w2 / (w1 + w2);
|
59
|
+
|
60
|
+
// Compute the final UV coordinates we'll use for sampling the texture
|
61
|
+
vec2 texPos0 = texPos1 - 1.0;
|
62
|
+
vec2 texPos3 = texPos1 + 2.0;
|
63
|
+
vec2 texPos12 = texPos1 + offset12;
|
64
|
+
|
65
|
+
texPos0 /= texSize;
|
66
|
+
texPos3 /= texSize;
|
67
|
+
texPos12 /= texSize;
|
68
|
+
|
69
|
+
vec4 result = vec4(0.0);
|
70
|
+
result += texture(tex, vec2(texPos0.x, texPos0.y)) * w0.x * w0.y;
|
71
|
+
result += texture(tex, vec2(texPos12.x, texPos0.y)) * w12.x * w0.y;
|
72
|
+
result += texture(tex, vec2(texPos3.x, texPos0.y)) * w3.x * w0.y;
|
73
|
+
|
74
|
+
result += texture(tex, vec2(texPos0.x, texPos12.y)) * w0.x * w12.y;
|
75
|
+
result += texture(tex, vec2(texPos12.x, texPos12.y)) * w12.x * w12.y;
|
76
|
+
result += texture(tex, vec2(texPos3.x, texPos12.y)) * w3.x * w12.y;
|
77
|
+
|
78
|
+
result += texture(tex, vec2(texPos0.x, texPos3.y)) * w0.x * w3.y;
|
79
|
+
result += texture(tex, vec2(texPos12.x, texPos3.y)) * w12.x * w3.y;
|
80
|
+
result += texture(tex, vec2(texPos3.x, texPos3.y)) * w3.x * w3.y;
|
81
|
+
|
82
|
+
return result;
|
83
|
+
}
|
84
|
+
|
85
|
+
vec4 textureCatmullRom(sampler2DArray tex, vec3 uv) {
|
86
|
+
vec2 texSize = vec2(textureSize(tex, 0));
|
87
|
+
vec2 samplePos = uv.xy * texSize;
|
88
|
+
vec2 texPos1 = floor(samplePos - 0.5) + 0.5;
|
89
|
+
vec2 f = samplePos - texPos1;
|
90
|
+
vec2 w0 = f * (-0.5 + f * (1.0 - 0.5 * f));
|
91
|
+
vec2 w1 = 1.0 + f * f * (-2.5 + 1.5 * f);
|
92
|
+
vec2 w2 = f * (0.5 + f * (2.0 - 1.5 * f));
|
93
|
+
vec2 w3 = f * f * (-0.5 + 0.5 * f);
|
94
|
+
vec2 w12 = w1 + w2;
|
95
|
+
vec2 offset12 = w2 / (w1 + w2);
|
96
|
+
vec2 texPos0 = texPos1 - 1.0;
|
97
|
+
vec2 texPos3 = texPos1 + 2.0;
|
98
|
+
vec2 texPos12 = texPos1 + offset12;
|
99
|
+
texPos0 /= texSize;
|
100
|
+
texPos3 /= texSize;
|
101
|
+
texPos12 /= texSize;
|
102
|
+
vec4 result = vec4(0.0);
|
103
|
+
result += texture(tex, vec3(texPos0.x, texPos0.y, uv.z)) * w0.x * w0.y;
|
104
|
+
result += texture(tex, vec3(texPos12.x, texPos0.y, uv.z)) * w12.x * w0.y;
|
105
|
+
result += texture(tex, vec3(texPos3.x, texPos0.y, uv.z)) * w3.x * w0.y;
|
106
|
+
result += texture(tex, vec3(texPos0.x, texPos12.y, uv.z)) * w0.x * w12.y;
|
107
|
+
result += texture(tex, vec3(texPos12.x, texPos12.y, uv.z)) * w12.x * w12.y;
|
108
|
+
result += texture(tex, vec3(texPos3.x, texPos12.y, uv.z)) * w3.x * w12.y;
|
109
|
+
result += texture(tex, vec3(texPos0.x, texPos3.y, uv.z)) * w0.x * w3.y;
|
110
|
+
result += texture(tex, vec3(texPos12.x, texPos3.y, uv.z)) * w12.x * w3.y;
|
111
|
+
result += texture(tex, vec3(texPos3.x, texPos3.y, uv.z)) * w3.x * w3.y;
|
112
|
+
return result;
|
113
|
+
}
|