@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,996 @@
|
|
1
|
+
precision highp float;
|
2
|
+
precision highp sampler3D;
|
3
|
+
precision highp sampler2DArray;
|
4
|
+
|
5
|
+
#include <common>
|
6
|
+
#include <packing>
|
7
|
+
|
8
|
+
#include "core/depth"
|
9
|
+
#include "core/math"
|
10
|
+
#include "core/turbo"
|
11
|
+
#include "core/generators"
|
12
|
+
#include "core/raySphereIntersection"
|
13
|
+
#include "core/cascadedShadowMaps"
|
14
|
+
#include "core/interleavedGradientNoise"
|
15
|
+
#include "core/vogelDisk"
|
16
|
+
#include "atmosphere/parameters"
|
17
|
+
#include "atmosphere/functions"
|
18
|
+
#include "types"
|
19
|
+
#include "parameters"
|
20
|
+
#include "clouds"
|
21
|
+
|
22
|
+
#if !defined(RECIPROCAL_PI4)
|
23
|
+
#define RECIPROCAL_PI4 (0.07957747154594767)
|
24
|
+
#endif // !defined(RECIPROCAL_PI4)
|
25
|
+
|
26
|
+
uniform sampler2D depthBuffer;
|
27
|
+
uniform mat4 viewMatrix;
|
28
|
+
uniform mat4 reprojectionMatrix;
|
29
|
+
uniform float cameraNear;
|
30
|
+
uniform float cameraFar;
|
31
|
+
uniform float cameraHeight;
|
32
|
+
uniform vec2 temporalJitter;
|
33
|
+
uniform vec2 targetUvScale;
|
34
|
+
uniform float mipLevelScale;
|
35
|
+
|
36
|
+
// Scattering
|
37
|
+
const vec2 scatterAnisotropy = vec2(SCATTER_ANISOTROPY_1, SCATTER_ANISOTROPY_2);
|
38
|
+
const float scatterAnisotropyMix = SCATTER_ANISOTROPY_MIX;
|
39
|
+
uniform float skyIrradianceScale;
|
40
|
+
uniform float groundIrradianceScale;
|
41
|
+
uniform float powderScale;
|
42
|
+
uniform float powderExponent;
|
43
|
+
|
44
|
+
// Primary raymarch
|
45
|
+
uniform int maxIterationCount;
|
46
|
+
uniform float minStepSize;
|
47
|
+
uniform float maxStepSize;
|
48
|
+
uniform float maxRayDistance;
|
49
|
+
uniform float perspectiveStepScale;
|
50
|
+
|
51
|
+
// Secondary raymarch
|
52
|
+
uniform int maxIterationCountToSun;
|
53
|
+
uniform int maxIterationCountToGround;
|
54
|
+
uniform float minSecondaryStepSize;
|
55
|
+
uniform float secondaryStepScale;
|
56
|
+
|
57
|
+
// Beer shadow map
|
58
|
+
uniform sampler2DArray shadowBuffer;
|
59
|
+
uniform vec2 shadowTexelSize;
|
60
|
+
uniform vec2 shadowIntervals[SHADOW_CASCADE_COUNT];
|
61
|
+
uniform mat4 shadowMatrices[SHADOW_CASCADE_COUNT];
|
62
|
+
uniform float shadowFar;
|
63
|
+
uniform float maxShadowFilterRadius;
|
64
|
+
|
65
|
+
// Shadow length
|
66
|
+
#ifdef SHADOW_LENGTH
|
67
|
+
uniform int maxShadowLengthIterationCount;
|
68
|
+
uniform float minShadowLengthStepSize;
|
69
|
+
uniform float maxShadowLengthRayDistance;
|
70
|
+
#endif // SHADOW_LENGTH
|
71
|
+
|
72
|
+
in vec2 vUv;
|
73
|
+
in vec3 vCameraPosition;
|
74
|
+
in vec3 vCameraDirection; // Direction to the center of screen
|
75
|
+
in vec3 vRayDirection; // Direction to the texel
|
76
|
+
in vec3 vEllipsoidCenter;
|
77
|
+
in GroundIrradiance vGroundIrradiance;
|
78
|
+
in CloudsIrradiance vCloudsIrradiance;
|
79
|
+
|
80
|
+
layout(location = 0) out vec4 outputColor;
|
81
|
+
layout(location = 1) out vec3 outputDepthVelocity;
|
82
|
+
#ifdef SHADOW_LENGTH
|
83
|
+
layout(location = 2) out float outputShadowLength;
|
84
|
+
#endif // SHADOW_LENGTH
|
85
|
+
|
86
|
+
float readDepth(const vec2 uv) {
|
87
|
+
#if DEPTH_PACKING == 3201
|
88
|
+
return unpackRGBAToDepth(texture(depthBuffer, uv));
|
89
|
+
#else // DEPTH_PACKING == 3201
|
90
|
+
return texture(depthBuffer, uv).r;
|
91
|
+
#endif // DEPTH_PACKING == 3201
|
92
|
+
}
|
93
|
+
|
94
|
+
float getViewZ(const float depth) {
|
95
|
+
#ifdef PERSPECTIVE_CAMERA
|
96
|
+
return perspectiveDepthToViewZ(depth, cameraNear, cameraFar);
|
97
|
+
#else // PERSPECTIVE_CAMERA
|
98
|
+
return orthographicDepthToViewZ(depth, cameraNear, cameraFar);
|
99
|
+
#endif // PERSPECTIVE_CAMERA
|
100
|
+
}
|
101
|
+
|
102
|
+
vec3 ECEFToWorld(const vec3 positionECEF) {
|
103
|
+
return mat3(ellipsoidMatrix) * (positionECEF + vEllipsoidCenter);
|
104
|
+
}
|
105
|
+
|
106
|
+
vec2 getShadowUv(const vec3 worldPosition, const int cascadeIndex) {
|
107
|
+
vec4 clip = shadowMatrices[cascadeIndex] * vec4(worldPosition, 1.0);
|
108
|
+
clip /= clip.w;
|
109
|
+
return clip.xy * 0.5 + 0.5;
|
110
|
+
}
|
111
|
+
|
112
|
+
float getDistanceToShadowTop(const vec3 rayPosition) {
|
113
|
+
// Distance to the top of the shadows along the sun direction, which matches
|
114
|
+
// the ray origin of BSM.
|
115
|
+
return raySphereSecondIntersection(
|
116
|
+
rayPosition,
|
117
|
+
sunDirection,
|
118
|
+
vec3(0.0),
|
119
|
+
bottomRadius + shadowTopHeight
|
120
|
+
);
|
121
|
+
}
|
122
|
+
|
123
|
+
#ifdef DEBUG_SHOW_CASCADES
|
124
|
+
|
125
|
+
const vec3 cascadeColors[4] = vec3[4](
|
126
|
+
vec3(1.0, 0.0, 0.0),
|
127
|
+
vec3(0.0, 1.0, 0.0),
|
128
|
+
vec3(0.0, 0.0, 1.0),
|
129
|
+
vec3(1.0, 1.0, 0.0)
|
130
|
+
);
|
131
|
+
|
132
|
+
vec3 getCascadeColor(const vec3 rayPosition) {
|
133
|
+
vec3 worldPosition = ECEFToWorld(rayPosition);
|
134
|
+
int cascadeIndex = getCascadeIndex(
|
135
|
+
viewMatrix,
|
136
|
+
worldPosition,
|
137
|
+
shadowIntervals,
|
138
|
+
cameraNear,
|
139
|
+
shadowFar
|
140
|
+
);
|
141
|
+
vec2 uv = getShadowUv(worldPosition, cascadeIndex);
|
142
|
+
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
|
143
|
+
return vec3(1.0);
|
144
|
+
}
|
145
|
+
return cascadeColors[cascadeIndex];
|
146
|
+
}
|
147
|
+
|
148
|
+
vec3 getFadedCascadeColor(const vec3 rayPosition, const float jitter) {
|
149
|
+
vec3 worldPosition = ECEFToWorld(rayPosition);
|
150
|
+
int cascadeIndex = getFadedCascadeIndex(
|
151
|
+
viewMatrix,
|
152
|
+
worldPosition,
|
153
|
+
shadowIntervals,
|
154
|
+
cameraNear,
|
155
|
+
shadowFar,
|
156
|
+
jitter
|
157
|
+
);
|
158
|
+
return cascadeIndex >= 0
|
159
|
+
? cascadeColors[cascadeIndex]
|
160
|
+
: vec3(1.0);
|
161
|
+
}
|
162
|
+
|
163
|
+
#endif // DEBUG_SHOW_CASCADES
|
164
|
+
|
165
|
+
float readShadowOpticalDepth(
|
166
|
+
const vec2 uv,
|
167
|
+
const float distanceToTop,
|
168
|
+
const float distanceOffset,
|
169
|
+
const int cascadeIndex
|
170
|
+
) {
|
171
|
+
// r: frontDepth, g: meanExtinction, b: maxOpticalDepth, a: maxOpticalDepthTail
|
172
|
+
// Also see the discussion here: https://x.com/shotamatsuda/status/1885322308908442106
|
173
|
+
vec4 shadow = texture(shadowBuffer, vec3(uv, float(cascadeIndex)));
|
174
|
+
float distanceToFront = max(0.0, distanceToTop - distanceOffset - shadow.r);
|
175
|
+
return min(shadow.b + shadow.a, shadow.g * distanceToFront);
|
176
|
+
}
|
177
|
+
|
178
|
+
float sampleShadowOpticalDepthPCF(
|
179
|
+
const vec3 worldPosition,
|
180
|
+
const float distanceToTop,
|
181
|
+
const float distanceOffset,
|
182
|
+
const float radius,
|
183
|
+
const int cascadeIndex
|
184
|
+
) {
|
185
|
+
vec2 uv = getShadowUv(worldPosition, cascadeIndex);
|
186
|
+
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
|
187
|
+
return 0.0;
|
188
|
+
}
|
189
|
+
if (radius < 0.1) {
|
190
|
+
return readShadowOpticalDepth(uv, distanceToTop, distanceOffset, cascadeIndex);
|
191
|
+
}
|
192
|
+
float sum = 0.0;
|
193
|
+
vec2 offset;
|
194
|
+
#pragma unroll_loop_start
|
195
|
+
for (int i = 0; i < 16; ++i) {
|
196
|
+
#if UNROLLED_LOOP_INDEX < SHADOW_SAMPLE_COUNT
|
197
|
+
offset = vogelDisk(
|
198
|
+
UNROLLED_LOOP_INDEX,
|
199
|
+
SHADOW_SAMPLE_COUNT,
|
200
|
+
interleavedGradientNoise(gl_FragCoord.xy + temporalJitter * resolution) * PI2
|
201
|
+
);
|
202
|
+
sum += readShadowOpticalDepth(
|
203
|
+
uv + offset * radius * shadowTexelSize,
|
204
|
+
distanceToTop,
|
205
|
+
distanceOffset,
|
206
|
+
cascadeIndex
|
207
|
+
);
|
208
|
+
#endif // UNROLLED_LOOP_INDEX < SHADOW_SAMPLE_COUNT
|
209
|
+
}
|
210
|
+
#pragma unroll_loop_end
|
211
|
+
return sum / float(SHADOW_SAMPLE_COUNT);
|
212
|
+
}
|
213
|
+
|
214
|
+
float sampleShadowOpticalDepth(
|
215
|
+
const vec3 rayPosition,
|
216
|
+
const float distanceOffset,
|
217
|
+
const float radius,
|
218
|
+
const float jitter
|
219
|
+
) {
|
220
|
+
float distanceToTop = getDistanceToShadowTop(rayPosition);
|
221
|
+
if (distanceToTop <= 0.0) {
|
222
|
+
return 0.0;
|
223
|
+
}
|
224
|
+
vec3 worldPosition = ECEFToWorld(rayPosition);
|
225
|
+
int cascadeIndex = getFadedCascadeIndex(
|
226
|
+
viewMatrix,
|
227
|
+
worldPosition,
|
228
|
+
shadowIntervals,
|
229
|
+
cameraNear,
|
230
|
+
shadowFar,
|
231
|
+
jitter
|
232
|
+
);
|
233
|
+
return cascadeIndex >= 0
|
234
|
+
? sampleShadowOpticalDepthPCF(
|
235
|
+
worldPosition,
|
236
|
+
distanceToTop,
|
237
|
+
distanceOffset,
|
238
|
+
radius,
|
239
|
+
cascadeIndex
|
240
|
+
)
|
241
|
+
: 0.0;
|
242
|
+
}
|
243
|
+
|
244
|
+
#ifdef DEBUG_SHOW_SHADOW_MAP
|
245
|
+
vec4 getCascadedShadowMaps(vec2 uv) {
|
246
|
+
vec4 coord = vec4(vUv, vUv - 0.5) * 2.0;
|
247
|
+
vec4 shadow = vec4(0.0);
|
248
|
+
if (uv.y > 0.5) {
|
249
|
+
if (uv.x < 0.5) {
|
250
|
+
shadow = texture(shadowBuffer, vec3(coord.xw, 0.0));
|
251
|
+
} else {
|
252
|
+
#if SHADOW_CASCADE_COUNT > 1
|
253
|
+
shadow = texture(shadowBuffer, vec3(coord.zw, 1.0));
|
254
|
+
#endif // SHADOW_CASCADE_COUNT > 1
|
255
|
+
}
|
256
|
+
} else {
|
257
|
+
if (uv.x < 0.5) {
|
258
|
+
#if SHADOW_CASCADE_COUNT > 2
|
259
|
+
shadow = texture(shadowBuffer, vec3(coord.xy, 2.0));
|
260
|
+
#endif // SHADOW_CASCADE_COUNT > 2
|
261
|
+
} else {
|
262
|
+
#if SHADOW_CASCADE_COUNT > 3
|
263
|
+
shadow = texture(shadowBuffer, vec3(coord.zy, 3.0));
|
264
|
+
#endif // SHADOW_CASCADE_COUNT > 3
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
#if !defined(DEBUG_SHOW_SHADOW_MAP_TYPE)
|
269
|
+
#define DEBUG_SHOW_SHADOW_MAP_TYPE (0)
|
270
|
+
#endif // !defined(DEBUG_SHOW_SHADOW_MAP_TYPE
|
271
|
+
|
272
|
+
const float frontDepthScale = 1e-5;
|
273
|
+
const float meanExtinctionScale = 10.0;
|
274
|
+
const float maxOpticalDepthScale = 0.01;
|
275
|
+
vec3 color;
|
276
|
+
#if DEBUG_SHOW_SHADOW_MAP_TYPE == 1
|
277
|
+
color = vec3(shadow.r * frontDepthScale);
|
278
|
+
#elif DEBUG_SHOW_SHADOW_MAP_TYPE == 2
|
279
|
+
color = vec3(shadow.g * meanExtinctionScale);
|
280
|
+
#elif DEBUG_SHOW_SHADOW_MAP_TYPE == 3
|
281
|
+
color = vec3((shadow.b + shadow.a) * maxOpticalDepthScale);
|
282
|
+
#else // DEBUG_SHOW_SHADOW_MAP_TYPE
|
283
|
+
color =
|
284
|
+
(shadow.rgb + vec3(0.0, 0.0, shadow.a)) *
|
285
|
+
vec3(frontDepthScale, meanExtinctionScale, maxOpticalDepthScale);
|
286
|
+
#endif // DEBUG_SHOW_SHADOW_MAP_TYPE
|
287
|
+
return vec4(color, 1.0);
|
288
|
+
}
|
289
|
+
#endif // DEBUG_SHOW_SHADOW_MAP
|
290
|
+
|
291
|
+
vec2 henyeyGreenstein(const vec2 g, const float cosTheta) {
|
292
|
+
vec2 g2 = g * g;
|
293
|
+
// prettier-ignore
|
294
|
+
return RECIPROCAL_PI4 *
|
295
|
+
((1.0 - g2) / max(vec2(1e-7), pow(1.0 + g2 - 2.0 * g * cosTheta, vec2(1.5))));
|
296
|
+
}
|
297
|
+
|
298
|
+
#ifdef ACCURATE_PHASE_FUNCTION
|
299
|
+
|
300
|
+
float draine(float u, float g, float a) {
|
301
|
+
float g2 = g * g;
|
302
|
+
// prettier-ignore
|
303
|
+
return (1.0 - g2) *
|
304
|
+
(1.0 + a * u * u) /
|
305
|
+
(4.0 * (1.0 + a * (1.0 + 2.0 * g2) / 3.0) * PI * pow(1.0 + g2 - 2.0 * g * u, 1.5));
|
306
|
+
}
|
307
|
+
|
308
|
+
// Numerically-fitted large particles (d=10) phase function It won't be
|
309
|
+
// plausible without a more precise multiple scattering.
|
310
|
+
// Reference: https://research.nvidia.com/labs/rtr/approximate-mie/
|
311
|
+
float phaseFunction(const float cosTheta, const float attenuation) {
|
312
|
+
const float gHG = 0.988176691700256; // exp(-0.0990567/(d-1.67154))
|
313
|
+
const float gD = 0.5556712547839497; // exp(-2.20679/(d+3.91029) - 0.428934)
|
314
|
+
const float alpha = 21.995520856274638; // exp(3.62489 - 8.29288/(d+5.52825))
|
315
|
+
const float weight = 0.4819554318404214; // exp(-0.599085/(d-0.641583)-0.665888)
|
316
|
+
return mix(
|
317
|
+
henyeyGreenstein(vec2(gHG) * attenuation, cosTheta).x,
|
318
|
+
draine(cosTheta, gD * attenuation, alpha),
|
319
|
+
weight
|
320
|
+
);
|
321
|
+
}
|
322
|
+
|
323
|
+
#else // ACCURATE_PHASE_FUNCTION
|
324
|
+
|
325
|
+
float phaseFunction(const float cosTheta, const float attenuation) {
|
326
|
+
const vec2 g = scatterAnisotropy;
|
327
|
+
const vec2 weights = vec2(1.0 - scatterAnisotropyMix, scatterAnisotropyMix);
|
328
|
+
// A similar approximation is described in the Frostbite's paper, where phase
|
329
|
+
// angle is attenuated instead of anisotropy.
|
330
|
+
return dot(henyeyGreenstein(g * attenuation, cosTheta), weights);
|
331
|
+
}
|
332
|
+
|
333
|
+
#endif // ACCURATE_PHASE_FUNCTION
|
334
|
+
|
335
|
+
float phaseFunction(const float cosTheta) {
|
336
|
+
return phaseFunction(cosTheta, 1.0);
|
337
|
+
}
|
338
|
+
|
339
|
+
float marchOpticalDepth(
|
340
|
+
const vec3 rayOrigin,
|
341
|
+
const vec3 rayDirection,
|
342
|
+
const int maxIterationCount,
|
343
|
+
const float mipLevel,
|
344
|
+
const float jitter,
|
345
|
+
out float rayDistance
|
346
|
+
) {
|
347
|
+
int iterationCount = int(
|
348
|
+
max(0.0, remap(mipLevel, 0.0, 1.0, float(maxIterationCount + 1), 1.0) - jitter)
|
349
|
+
);
|
350
|
+
if (iterationCount == 0) {
|
351
|
+
// Fudge factor to approximate the mean optical depth.
|
352
|
+
// TODO: Remove it.
|
353
|
+
return 0.5;
|
354
|
+
}
|
355
|
+
float stepSize = minSecondaryStepSize / float(iterationCount);
|
356
|
+
float nextDistance = stepSize * jitter;
|
357
|
+
float opticalDepth = 0.0;
|
358
|
+
for (int i = 0; i < iterationCount; ++i) {
|
359
|
+
rayDistance = nextDistance;
|
360
|
+
vec3 position = rayDistance * rayDirection + rayOrigin;
|
361
|
+
vec2 uv = getGlobeUv(position);
|
362
|
+
float height = length(position) - bottomRadius;
|
363
|
+
WeatherSample weather = sampleWeather(uv, height, mipLevel);
|
364
|
+
MediaSample media = sampleMedia(weather, position, uv, mipLevel, jitter);
|
365
|
+
opticalDepth += media.extinction * stepSize;
|
366
|
+
nextDistance += stepSize;
|
367
|
+
stepSize *= secondaryStepScale;
|
368
|
+
}
|
369
|
+
return opticalDepth;
|
370
|
+
}
|
371
|
+
|
372
|
+
float marchOpticalDepth(
|
373
|
+
const vec3 rayOrigin,
|
374
|
+
const vec3 rayDirection,
|
375
|
+
const int maxIterationCount,
|
376
|
+
const float mipLevel,
|
377
|
+
const float jitter
|
378
|
+
) {
|
379
|
+
float rayDistance;
|
380
|
+
return marchOpticalDepth(
|
381
|
+
rayOrigin,
|
382
|
+
rayDirection,
|
383
|
+
maxIterationCount,
|
384
|
+
mipLevel,
|
385
|
+
jitter,
|
386
|
+
rayDistance
|
387
|
+
);
|
388
|
+
}
|
389
|
+
|
390
|
+
float approximateMultipleScattering(const float opticalDepth, const float cosTheta) {
|
391
|
+
// Multiple scattering approximation
|
392
|
+
// See: https://fpsunflower.github.io/ckulla/data/oz_volumes.pdf
|
393
|
+
// a: attenuation, b: contribution, c: phase attenuation
|
394
|
+
vec3 coeffs = vec3(1.0); // [a, b, c]
|
395
|
+
const vec3 attenuation = vec3(0.5, 0.5, 0.5); // Should satisfy a <= b
|
396
|
+
float scattering = 0.0;
|
397
|
+
float beerLambert;
|
398
|
+
#pragma unroll_loop_start
|
399
|
+
for (int i = 0; i < 12; ++i) {
|
400
|
+
#if UNROLLED_LOOP_INDEX < MULTI_SCATTERING_OCTAVES
|
401
|
+
beerLambert = exp(-opticalDepth * coeffs.y);
|
402
|
+
scattering += coeffs.x * beerLambert * phaseFunction(cosTheta, coeffs.z);
|
403
|
+
coeffs *= attenuation;
|
404
|
+
#endif // UNROLLED_LOOP_INDEX < MULTI_SCATTERING_OCTAVES
|
405
|
+
}
|
406
|
+
#pragma unroll_loop_end
|
407
|
+
return scattering;
|
408
|
+
}
|
409
|
+
|
410
|
+
// TODO: Construct spherical harmonics of degree 2 using 2 sample points
|
411
|
+
// positioned near the horizon occlusion points on the sun direction plane.
|
412
|
+
vec3 getGroundSunSkyIrradiance(
|
413
|
+
const vec3 position,
|
414
|
+
const vec3 surfaceNormal,
|
415
|
+
const float height,
|
416
|
+
out vec3 skyIrradiance
|
417
|
+
) {
|
418
|
+
#ifdef ACCURATE_SUN_SKY_IRRADIANCE
|
419
|
+
return GetSunAndSkyIrradiance(
|
420
|
+
(position - surfaceNormal * height) * METER_TO_LENGTH_UNIT,
|
421
|
+
sunDirection,
|
422
|
+
skyIrradiance
|
423
|
+
);
|
424
|
+
#else // ACCURATE_SUN_SKY_IRRADIANCE
|
425
|
+
skyIrradiance = vGroundIrradiance.sky;
|
426
|
+
return vGroundIrradiance.sun;
|
427
|
+
#endif // ACCURATE_SUN_SKY_IRRADIANCE
|
428
|
+
}
|
429
|
+
|
430
|
+
vec3 getCloudsSunSkyIrradiance(const vec3 position, const float height, out vec3 skyIrradiance) {
|
431
|
+
#ifdef ACCURATE_SUN_SKY_IRRADIANCE
|
432
|
+
return GetSunAndSkyIrradiance(position * METER_TO_LENGTH_UNIT, sunDirection, skyIrradiance);
|
433
|
+
#else // ACCURATE_SUN_SKY_IRRADIANCE
|
434
|
+
float alpha = remapClamped(height, minHeight, maxHeight);
|
435
|
+
skyIrradiance = mix(vCloudsIrradiance.minSky, vCloudsIrradiance.maxSky, alpha);
|
436
|
+
return mix(vCloudsIrradiance.minSun, vCloudsIrradiance.maxSun, alpha);
|
437
|
+
#endif // ACCURATE_SUN_SKY_IRRADIANCE
|
438
|
+
}
|
439
|
+
|
440
|
+
#ifdef GROUND_IRRADIANCE
|
441
|
+
vec3 approximateIrradianceFromGround(
|
442
|
+
const vec3 position,
|
443
|
+
const vec3 surfaceNormal,
|
444
|
+
const float height,
|
445
|
+
const float mipLevel,
|
446
|
+
const float jitter
|
447
|
+
) {
|
448
|
+
float opticalDepthToGround = marchOpticalDepth(
|
449
|
+
position,
|
450
|
+
-surfaceNormal,
|
451
|
+
maxIterationCountToGround,
|
452
|
+
mipLevel,
|
453
|
+
jitter
|
454
|
+
);
|
455
|
+
vec3 skyIrradiance;
|
456
|
+
vec3 sunIrradiance = getGroundSunSkyIrradiance(position, surfaceNormal, height, skyIrradiance);
|
457
|
+
const float groundAlbedo = 0.3;
|
458
|
+
vec3 groundIrradiance = skyIrradiance + (1.0 - coverage) * sunIrradiance * RECIPROCAL_PI2;
|
459
|
+
vec3 bouncedLight = groundAlbedo * RECIPROCAL_PI * groundIrradiance;
|
460
|
+
vec3 bouncedIrradiance = bouncedLight * exp(-opticalDepthToGround);
|
461
|
+
return albedo * bouncedIrradiance * RECIPROCAL_PI4 * groundIrradianceScale;
|
462
|
+
}
|
463
|
+
#endif // GROUND_IRRADIANCE
|
464
|
+
|
465
|
+
vec4 marchClouds(
|
466
|
+
const vec3 rayOrigin,
|
467
|
+
const vec3 rayDirection,
|
468
|
+
const vec2 rayNearFar,
|
469
|
+
const float cosTheta,
|
470
|
+
const float jitter,
|
471
|
+
const float rayStartTexelsPerPixel,
|
472
|
+
out float frontDepth,
|
473
|
+
out ivec3 sampleCount
|
474
|
+
) {
|
475
|
+
vec3 radianceIntegral = vec3(0.0);
|
476
|
+
float transmittanceIntegral = 1.0;
|
477
|
+
float weightedDistanceSum = 0.0;
|
478
|
+
float transmittanceSum = 0.0;
|
479
|
+
|
480
|
+
float maxRayDistance = rayNearFar.y - rayNearFar.x;
|
481
|
+
float stepSize = minStepSize + (perspectiveStepScale - 1.0) * rayNearFar.x;
|
482
|
+
// I don't understand why spatial aliasing remains unless doubling the jitter.
|
483
|
+
float rayDistance = stepSize * jitter * 2.0;
|
484
|
+
|
485
|
+
for (int i = 0; i < maxIterationCount; ++i) {
|
486
|
+
if (rayDistance > maxRayDistance) {
|
487
|
+
break; // Termination
|
488
|
+
}
|
489
|
+
|
490
|
+
vec3 position = rayDistance * rayDirection + rayOrigin;
|
491
|
+
float height = length(position) - bottomRadius;
|
492
|
+
float mipLevel = log2(max(1.0, rayStartTexelsPerPixel + rayDistance * 1e-5));
|
493
|
+
|
494
|
+
#if !defined(DEBUG_MARCH_INTERVALS)
|
495
|
+
if (insideLayerIntervals(height)) {
|
496
|
+
stepSize *= perspectiveStepScale;
|
497
|
+
rayDistance += mix(stepSize, maxStepSize, min(1.0, mipLevel));
|
498
|
+
continue;
|
499
|
+
}
|
500
|
+
#endif // !defined(DEBUG_MARCH_INTERVALS)
|
501
|
+
|
502
|
+
// Sample rough weather.
|
503
|
+
vec2 uv = getGlobeUv(position);
|
504
|
+
WeatherSample weather = sampleWeather(uv, height, mipLevel);
|
505
|
+
|
506
|
+
#ifdef DEBUG_SHOW_SAMPLE_COUNT
|
507
|
+
++sampleCount.x;
|
508
|
+
#endif // DEBUG_SHOW_SAMPLE_COUNT
|
509
|
+
|
510
|
+
if (!any(greaterThan(weather.density, vec4(minDensity)))) {
|
511
|
+
// Step longer in empty space.
|
512
|
+
// TODO: This produces banding artifacts.
|
513
|
+
// Possible improvement: Binary search refinement
|
514
|
+
stepSize *= perspectiveStepScale;
|
515
|
+
rayDistance += mix(stepSize, maxStepSize, min(1.0, mipLevel));
|
516
|
+
continue;
|
517
|
+
}
|
518
|
+
|
519
|
+
// Sample detailed participating media.
|
520
|
+
MediaSample media = sampleMedia(weather, position, uv, mipLevel, jitter, sampleCount);
|
521
|
+
|
522
|
+
if (media.extinction > minExtinction) {
|
523
|
+
vec3 skyIrradiance;
|
524
|
+
vec3 sunIrradiance = getCloudsSunSkyIrradiance(position, height, skyIrradiance);
|
525
|
+
vec3 surfaceNormal = normalize(position);
|
526
|
+
|
527
|
+
// March optical depth to the sun for finer details, which BSM lacks.
|
528
|
+
float sunRayDistance = 0.0;
|
529
|
+
float opticalDepth = marchOpticalDepth(
|
530
|
+
position,
|
531
|
+
sunDirection,
|
532
|
+
maxIterationCountToSun,
|
533
|
+
mipLevel,
|
534
|
+
jitter,
|
535
|
+
sunRayDistance
|
536
|
+
);
|
537
|
+
|
538
|
+
if (height < shadowTopHeight) {
|
539
|
+
// Obtain the optical depth from BSM at the ray position.
|
540
|
+
opticalDepth += sampleShadowOpticalDepth(
|
541
|
+
position,
|
542
|
+
// Take account of only positions further than the marched ray
|
543
|
+
// distance.
|
544
|
+
sunRayDistance,
|
545
|
+
// Apply PCF only when the sun is close to the horizon.
|
546
|
+
maxShadowFilterRadius * remapClamped(dot(sunDirection, surfaceNormal), 0.1, 0.0),
|
547
|
+
jitter
|
548
|
+
);
|
549
|
+
}
|
550
|
+
|
551
|
+
float scattering = approximateMultipleScattering(opticalDepth, cosTheta);
|
552
|
+
vec3 radiance = albedo * sunIrradiance * scattering;
|
553
|
+
|
554
|
+
#ifdef GROUND_IRRADIANCE
|
555
|
+
// Fudge factor for the irradiance from ground.
|
556
|
+
if (height < shadowTopHeight && mipLevel < 0.5) {
|
557
|
+
radiance += approximateIrradianceFromGround(
|
558
|
+
position,
|
559
|
+
surfaceNormal,
|
560
|
+
height,
|
561
|
+
mipLevel,
|
562
|
+
jitter
|
563
|
+
);
|
564
|
+
}
|
565
|
+
#endif // GROUND_IRRADIANCE
|
566
|
+
|
567
|
+
// Crude approximation of sky gradient. Better than none in the shadows.
|
568
|
+
float skyGradient = dot(0.5 + weather.heightFraction, media.weight);
|
569
|
+
radiance += albedo * skyIrradiance * RECIPROCAL_PI4 * skyGradient * skyIrradianceScale;
|
570
|
+
|
571
|
+
// Finally multiply by extinction (redundant but kept for clarity).
|
572
|
+
radiance *= media.extinction;
|
573
|
+
|
574
|
+
#ifdef POWDER
|
575
|
+
radiance *= 1.0 - powderScale * exp(-media.extinction * powderExponent);
|
576
|
+
#endif // POWDER
|
577
|
+
|
578
|
+
#ifdef DEBUG_SHOW_CASCADES
|
579
|
+
if (height < shadowTopHeight) {
|
580
|
+
radiance = 1e-3 * getFadedCascadeColor(position, jitter);
|
581
|
+
}
|
582
|
+
#endif // DEBUG_SHOW_CASCADES
|
583
|
+
|
584
|
+
// Energy-conserving analytical integration of scattered light
|
585
|
+
// See 5.6.3 in https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/s2016-pbs-frostbite-sky-clouds-new.pdf
|
586
|
+
float transmittance = exp(-media.extinction * stepSize);
|
587
|
+
float clampedExtinction = max(media.extinction, 1e-7);
|
588
|
+
vec3 scatteringIntegral = (radiance - radiance * transmittance) / clampedExtinction;
|
589
|
+
radianceIntegral += transmittanceIntegral * scatteringIntegral;
|
590
|
+
transmittanceIntegral *= transmittance;
|
591
|
+
|
592
|
+
// Aerial perspective affecting clouds
|
593
|
+
// See 5.9.1 in https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/s2016-pbs-frostbite-sky-clouds-new.pdf
|
594
|
+
weightedDistanceSum += rayDistance * transmittanceIntegral;
|
595
|
+
transmittanceSum += transmittanceIntegral;
|
596
|
+
}
|
597
|
+
|
598
|
+
if (transmittanceIntegral <= minTransmittance) {
|
599
|
+
break; // Early termination
|
600
|
+
}
|
601
|
+
|
602
|
+
// Take a shorter step because we've already hit the clouds.
|
603
|
+
stepSize *= perspectiveStepScale;
|
604
|
+
rayDistance += stepSize;
|
605
|
+
}
|
606
|
+
|
607
|
+
// The final product of 5.9.1 and we'll evaluate this in aerial perspective.
|
608
|
+
frontDepth = transmittanceSum > 0.0 ? weightedDistanceSum / transmittanceSum : -1.0;
|
609
|
+
|
610
|
+
return vec4(radianceIntegral, remapClamped(transmittanceIntegral, 1.0, minTransmittance));
|
611
|
+
}
|
612
|
+
|
613
|
+
#ifdef SHADOW_LENGTH
|
614
|
+
|
615
|
+
float marchShadowLength(
|
616
|
+
const vec3 rayOrigin,
|
617
|
+
const vec3 rayDirection,
|
618
|
+
const vec2 rayNearFar,
|
619
|
+
const float jitter
|
620
|
+
) {
|
621
|
+
float shadowLength = 0.0;
|
622
|
+
float maxRayDistance = rayNearFar.y - rayNearFar.x;
|
623
|
+
float stepSize = minShadowLengthStepSize;
|
624
|
+
float rayDistance = stepSize * jitter;
|
625
|
+
const float attenuationFactor = 1.0 - 1e-3;
|
626
|
+
float attenuation = 1.0;
|
627
|
+
|
628
|
+
// TODO: This march is closed, and sample resolution can be much lower.
|
629
|
+
// Refining the termination by binary search will make it much more efficient.
|
630
|
+
for (int i = 0; i < maxShadowLengthIterationCount; ++i) {
|
631
|
+
if (rayDistance > maxRayDistance) {
|
632
|
+
break; // Termination
|
633
|
+
}
|
634
|
+
vec3 position = rayDistance * rayDirection + rayOrigin;
|
635
|
+
float opticalDepth = sampleShadowOpticalDepth(position, 0.0, 0.0, jitter);
|
636
|
+
shadowLength += (1.0 - exp(-opticalDepth)) * stepSize * attenuation;
|
637
|
+
|
638
|
+
// Hack to prevent over-integration of shadow length. The shadow should be
|
639
|
+
// attenuated by the inscatter as the ray travels further.
|
640
|
+
attenuation *= attenuationFactor;
|
641
|
+
if (attenuation < 1e-5) {
|
642
|
+
break;
|
643
|
+
}
|
644
|
+
|
645
|
+
stepSize *= perspectiveStepScale;
|
646
|
+
rayDistance += stepSize;
|
647
|
+
}
|
648
|
+
return shadowLength;
|
649
|
+
}
|
650
|
+
|
651
|
+
#endif // SHADOW_LENGTH
|
652
|
+
|
653
|
+
#ifdef HAZE
|
654
|
+
|
655
|
+
vec4 approximateHaze(
|
656
|
+
const vec3 rayOrigin,
|
657
|
+
const vec3 rayDirection,
|
658
|
+
const float maxRayDistance,
|
659
|
+
const float cosTheta,
|
660
|
+
const float shadowLength
|
661
|
+
) {
|
662
|
+
float modulation = remapClamped(coverage, 0.2, 0.4);
|
663
|
+
if (cameraHeight * modulation < 0.0) {
|
664
|
+
return vec4(0.0);
|
665
|
+
}
|
666
|
+
float density = modulation * hazeDensityScale * exp(-cameraHeight * hazeExponent);
|
667
|
+
if (density < 1e-7) {
|
668
|
+
return vec4(0.0); // Prevent artifact in views from space
|
669
|
+
}
|
670
|
+
|
671
|
+
// Analytical optical depth where density exponentially decreases with height.
|
672
|
+
// Based on: https://iquilezles.org/articles/fog/
|
673
|
+
float angle = max(dot(normalize(rayOrigin), rayDirection), 1e-5);
|
674
|
+
float exponent = angle * hazeExponent;
|
675
|
+
// Derive the optical depths separately for with and without shadow length.
|
676
|
+
float expTerm = 1.0 - exp(-maxRayDistance * exponent);
|
677
|
+
float shadowExpTerm = 1.0 - exp(-min(maxRayDistance, shadowLength) * exponent);
|
678
|
+
float linearTerm = density / hazeExponent / angle;
|
679
|
+
float opticalDepth = expTerm * linearTerm;
|
680
|
+
float effectiveOpticalDepth = max((expTerm - shadowExpTerm) * linearTerm, 0.0);
|
681
|
+
|
682
|
+
vec3 skyIrradiance = vGroundIrradiance.sky;
|
683
|
+
vec3 sunIrradiance = vGroundIrradiance.sun;
|
684
|
+
vec3 irradiance = sunIrradiance * phaseFunction(cosTheta);
|
685
|
+
irradiance += skyIrradiance * RECIPROCAL_PI4 * skyIrradianceScale;
|
686
|
+
vec3 inscatter = albedo * irradiance * saturate(1.0 - exp(-effectiveOpticalDepth));
|
687
|
+
|
688
|
+
// Inscatter is attenuated by shadow length, but transmittance is not.
|
689
|
+
return vec4(inscatter, saturate(1.0 - exp(-opticalDepth)));
|
690
|
+
}
|
691
|
+
|
692
|
+
#endif // HAZE
|
693
|
+
|
694
|
+
void applyAerialPerspective(
|
695
|
+
const vec3 cameraPosition,
|
696
|
+
const vec3 frontPosition,
|
697
|
+
const float shadowLength,
|
698
|
+
inout vec4 color
|
699
|
+
) {
|
700
|
+
vec3 transmittance;
|
701
|
+
vec3 inscatter = GetSkyRadianceToPoint(
|
702
|
+
cameraPosition * METER_TO_LENGTH_UNIT,
|
703
|
+
frontPosition * METER_TO_LENGTH_UNIT,
|
704
|
+
shadowLength * METER_TO_LENGTH_UNIT,
|
705
|
+
sunDirection,
|
706
|
+
transmittance
|
707
|
+
);
|
708
|
+
float clampedAlpha = max(color.a, 1e-7);
|
709
|
+
color.rgb = mix(vec3(0.0), color.rgb * transmittance / clampedAlpha + inscatter, color.a);
|
710
|
+
}
|
711
|
+
|
712
|
+
bool rayIntersectsGround(const vec3 cameraPosition, const vec3 rayDirection) {
|
713
|
+
float r = length(cameraPosition);
|
714
|
+
float mu = dot(cameraPosition, rayDirection) / r;
|
715
|
+
return mu < 0.0 && r * r * (mu * mu - 1.0) + bottomRadius * bottomRadius >= 0.0;
|
716
|
+
}
|
717
|
+
|
718
|
+
struct IntersectionResult {
|
719
|
+
bool ground;
|
720
|
+
vec4 first;
|
721
|
+
vec4 second;
|
722
|
+
};
|
723
|
+
|
724
|
+
IntersectionResult getIntersections(const vec3 cameraPosition, const vec3 rayDirection) {
|
725
|
+
IntersectionResult intersections;
|
726
|
+
intersections.ground = rayIntersectsGround(cameraPosition, rayDirection);
|
727
|
+
raySphereIntersections(
|
728
|
+
cameraPosition,
|
729
|
+
rayDirection,
|
730
|
+
bottomRadius + vec4(0.0, minHeight, maxHeight, shadowTopHeight),
|
731
|
+
intersections.first,
|
732
|
+
intersections.second
|
733
|
+
);
|
734
|
+
return intersections;
|
735
|
+
}
|
736
|
+
|
737
|
+
vec2 getRayNearFar(const IntersectionResult intersections) {
|
738
|
+
vec2 nearFar;
|
739
|
+
if (cameraHeight < minHeight) {
|
740
|
+
// View below the clouds
|
741
|
+
if (intersections.ground) {
|
742
|
+
nearFar = vec2(-1.0); // No clouds to the ground
|
743
|
+
} else {
|
744
|
+
nearFar = vec2(intersections.second.y, intersections.second.z);
|
745
|
+
nearFar.y = min(nearFar.y, maxRayDistance);
|
746
|
+
}
|
747
|
+
} else if (cameraHeight < maxHeight) {
|
748
|
+
// View inside the total cloud layer
|
749
|
+
if (intersections.ground) {
|
750
|
+
nearFar = vec2(cameraNear, intersections.first.y);
|
751
|
+
} else {
|
752
|
+
nearFar = vec2(cameraNear, intersections.second.z);
|
753
|
+
}
|
754
|
+
} else {
|
755
|
+
// View above the clouds
|
756
|
+
nearFar = vec2(intersections.first.z, intersections.second.z);
|
757
|
+
if (intersections.ground) {
|
758
|
+
// Clamp the ray at the min height.
|
759
|
+
nearFar.y = intersections.first.y;
|
760
|
+
}
|
761
|
+
}
|
762
|
+
return nearFar;
|
763
|
+
}
|
764
|
+
|
765
|
+
#ifdef SHADOW_LENGTH
|
766
|
+
vec2 getShadowRayNearFar(const IntersectionResult intersections) {
|
767
|
+
vec2 nearFar;
|
768
|
+
if (cameraHeight < shadowTopHeight) {
|
769
|
+
if (intersections.ground) {
|
770
|
+
nearFar = vec2(cameraNear, intersections.first.x);
|
771
|
+
} else {
|
772
|
+
nearFar = vec2(cameraNear, intersections.second.w);
|
773
|
+
}
|
774
|
+
} else {
|
775
|
+
nearFar = vec2(intersections.first.w, intersections.second.w);
|
776
|
+
if (intersections.ground) {
|
777
|
+
// Clamp the ray at the ground.
|
778
|
+
nearFar.y = intersections.first.x;
|
779
|
+
}
|
780
|
+
}
|
781
|
+
nearFar.y = min(nearFar.y, maxShadowLengthRayDistance);
|
782
|
+
return nearFar;
|
783
|
+
}
|
784
|
+
#endif // SHADOW_LENGTH
|
785
|
+
|
786
|
+
#ifdef HAZE
|
787
|
+
vec2 getHazeRayNearFar(const IntersectionResult intersections) {
|
788
|
+
vec2 nearFar;
|
789
|
+
if (cameraHeight < maxHeight) {
|
790
|
+
if (intersections.ground) {
|
791
|
+
nearFar = vec2(cameraNear, intersections.first.x);
|
792
|
+
} else {
|
793
|
+
nearFar = vec2(cameraNear, intersections.second.z);
|
794
|
+
}
|
795
|
+
} else {
|
796
|
+
nearFar = vec2(cameraNear, intersections.second.z);
|
797
|
+
if (intersections.ground) {
|
798
|
+
// Clamp the ray at the ground.
|
799
|
+
nearFar.y = intersections.first.x;
|
800
|
+
}
|
801
|
+
}
|
802
|
+
return nearFar;
|
803
|
+
}
|
804
|
+
#endif // HAZE
|
805
|
+
|
806
|
+
float getRayDistanceToScene(const vec3 rayDirection) {
|
807
|
+
float depth = readDepth(vUv * targetUvScale + temporalJitter);
|
808
|
+
if (depth < 1.0 - 1e-7) {
|
809
|
+
depth = reverseLogDepth(depth, cameraNear, cameraFar);
|
810
|
+
float viewZ = getViewZ(depth);
|
811
|
+
return -viewZ / dot(rayDirection, vCameraDirection);
|
812
|
+
}
|
813
|
+
return -1.0;
|
814
|
+
}
|
815
|
+
|
816
|
+
void main() {
|
817
|
+
#ifdef DEBUG_SHOW_SHADOW_MAP
|
818
|
+
outputColor = getCascadedShadowMaps(vUv);
|
819
|
+
outputDepthVelocity = vec3(0.0);
|
820
|
+
#ifdef SHADOW_LENGTH
|
821
|
+
outputShadowLength = 0.0;
|
822
|
+
#endif // SHADOW_LENGTH
|
823
|
+
return;
|
824
|
+
#endif // DEBUG_SHOW_SHADOW_MAP
|
825
|
+
|
826
|
+
vec3 cameraPosition = vCameraPosition - vEllipsoidCenter;
|
827
|
+
vec3 rayDirection = normalize(vRayDirection);
|
828
|
+
float cosTheta = dot(sunDirection, rayDirection);
|
829
|
+
|
830
|
+
IntersectionResult intersections = getIntersections(cameraPosition, rayDirection);
|
831
|
+
vec2 rayNearFar = getRayNearFar(intersections);
|
832
|
+
#ifdef SHADOW_LENGTH
|
833
|
+
vec2 shadowRayNearFar = getShadowRayNearFar(intersections);
|
834
|
+
#endif // SHADOW_LENGTH
|
835
|
+
#ifdef HAZE
|
836
|
+
vec2 hazeRayNearFar = getHazeRayNearFar(intersections);
|
837
|
+
#endif // HAZE
|
838
|
+
|
839
|
+
float rayDistanceToScene = getRayDistanceToScene(rayDirection);
|
840
|
+
if (rayDistanceToScene >= 0.0) {
|
841
|
+
rayNearFar.y = min(rayNearFar.y, rayDistanceToScene);
|
842
|
+
#ifdef SHADOW_LENGTH
|
843
|
+
shadowRayNearFar.y = min(shadowRayNearFar.y, rayDistanceToScene);
|
844
|
+
#endif // SHADOW_LENGTH
|
845
|
+
#ifdef HAZE
|
846
|
+
hazeRayNearFar.y = min(hazeRayNearFar.y, rayDistanceToScene);
|
847
|
+
#endif // HAZE
|
848
|
+
}
|
849
|
+
|
850
|
+
bool intersectsGround = any(lessThan(rayNearFar, vec2(0.0)));
|
851
|
+
bool intersectsScene = rayNearFar.y < rayNearFar.x;
|
852
|
+
|
853
|
+
float stbn = getSTBN();
|
854
|
+
|
855
|
+
vec4 color = vec4(0.0);
|
856
|
+
float frontDepth = rayNearFar.y;
|
857
|
+
vec3 depthVelocity = vec3(0.0);
|
858
|
+
float shadowLength = 0.0;
|
859
|
+
|
860
|
+
if (!intersectsGround && !intersectsScene) {
|
861
|
+
vec3 rayOrigin = rayNearFar.x * rayDirection + cameraPosition;
|
862
|
+
|
863
|
+
vec2 globeUv = getGlobeUv(rayOrigin);
|
864
|
+
#ifdef DEBUG_SHOW_UV
|
865
|
+
outputColor = vec4(vec3(checker(globeUv, localWeatherRepeat + localWeatherOffset)), 1.0);
|
866
|
+
outputDepthVelocity = vec3(0.0);
|
867
|
+
#ifdef SHADOW_LENGTH
|
868
|
+
outputShadowLength = 0.0;
|
869
|
+
#endif // SHADOW_LENGTH
|
870
|
+
return;
|
871
|
+
#endif // DEBUG_SHOW_UV
|
872
|
+
|
873
|
+
float mipLevel = getMipLevel(globeUv * localWeatherRepeat) * mipLevelScale;
|
874
|
+
mipLevel = mix(0.0, mipLevel, min(1.0, 0.2 * cameraHeight / maxHeight));
|
875
|
+
|
876
|
+
float marchedFrontDepth;
|
877
|
+
ivec3 sampleCount = ivec3(0);
|
878
|
+
color = marchClouds(
|
879
|
+
rayOrigin,
|
880
|
+
rayDirection,
|
881
|
+
rayNearFar,
|
882
|
+
cosTheta,
|
883
|
+
stbn,
|
884
|
+
pow(2.0, mipLevel),
|
885
|
+
marchedFrontDepth,
|
886
|
+
sampleCount
|
887
|
+
);
|
888
|
+
|
889
|
+
#ifdef DEBUG_SHOW_SAMPLE_COUNT
|
890
|
+
outputColor = vec4(vec3(sampleCount) / vec3(500.0, 5.0, 5.0), 1.0);
|
891
|
+
outputDepthVelocity = vec3(0.0);
|
892
|
+
#ifdef SHADOW_LENGTH
|
893
|
+
outputShadowLength = 0.0;
|
894
|
+
#endif // SHADOW_LENGTH
|
895
|
+
return;
|
896
|
+
#endif // DEBUG_SHOW_SAMPLE_COUNT
|
897
|
+
|
898
|
+
// Front depth will be -1.0 when no samples are accumulated.
|
899
|
+
if (marchedFrontDepth >= 0.0) {
|
900
|
+
frontDepth = rayNearFar.x + marchedFrontDepth;
|
901
|
+
|
902
|
+
#ifdef SHADOW_LENGTH
|
903
|
+
// Clamp the shadow length ray at the clouds.
|
904
|
+
shadowRayNearFar.y = mix(
|
905
|
+
shadowRayNearFar.y,
|
906
|
+
min(frontDepth, shadowRayNearFar.y),
|
907
|
+
color.a // Interpolate by the alpha for smoother edges.
|
908
|
+
);
|
909
|
+
#endif // SHADOW_LENGTH
|
910
|
+
|
911
|
+
#ifdef HAZE
|
912
|
+
// Clamp the haze ray at the clouds.
|
913
|
+
hazeRayNearFar.y = mix(
|
914
|
+
hazeRayNearFar.y,
|
915
|
+
min(frontDepth, hazeRayNearFar.y),
|
916
|
+
color.a // Interpolate by the alpha for smoother edges.
|
917
|
+
);
|
918
|
+
#endif // HAZE
|
919
|
+
}
|
920
|
+
|
921
|
+
#ifdef SHADOW_LENGTH
|
922
|
+
if (all(greaterThanEqual(shadowRayNearFar, vec2(0.0)))) {
|
923
|
+
shadowLength = marchShadowLength(
|
924
|
+
shadowRayNearFar.x * rayDirection + cameraPosition,
|
925
|
+
rayDirection,
|
926
|
+
shadowRayNearFar,
|
927
|
+
stbn
|
928
|
+
);
|
929
|
+
}
|
930
|
+
#endif // SHADOW_LENGTH
|
931
|
+
|
932
|
+
// Apply aerial perspective.
|
933
|
+
vec3 frontPosition = cameraPosition + frontDepth * rayDirection;
|
934
|
+
applyAerialPerspective(cameraPosition, frontPosition, shadowLength, color);
|
935
|
+
|
936
|
+
// Velocity for temporal resolution.
|
937
|
+
vec3 frontPositionWorld = ECEFToWorld(frontPosition);
|
938
|
+
vec4 prevClip = reprojectionMatrix * vec4(frontPositionWorld, 1.0);
|
939
|
+
prevClip /= prevClip.w;
|
940
|
+
vec2 prevUv = prevClip.xy * 0.5 + 0.5;
|
941
|
+
vec2 velocity = (vUv - prevUv) * resolution;
|
942
|
+
depthVelocity = vec3(frontDepth, velocity);
|
943
|
+
|
944
|
+
} else {
|
945
|
+
#ifdef SHADOW_LENGTH
|
946
|
+
if (all(greaterThanEqual(shadowRayNearFar, vec2(0.0)))) {
|
947
|
+
shadowLength = marchShadowLength(
|
948
|
+
shadowRayNearFar.x * rayDirection + cameraPosition,
|
949
|
+
rayDirection,
|
950
|
+
shadowRayNearFar,
|
951
|
+
stbn
|
952
|
+
);
|
953
|
+
}
|
954
|
+
#endif // SHADOW_LENGTH
|
955
|
+
|
956
|
+
// TODO: We can calculate velocity to reduce occlusion errors at the edges,
|
957
|
+
// but suffers from floating-point precision errors on near objects.
|
958
|
+
|
959
|
+
// if (intersectsScene) {
|
960
|
+
// vec3 frontPosition = cameraPosition + rayNearFar.y * rayDirection;
|
961
|
+
// vec3 frontPositionWorld = ECEFToWorld(frontPosition);
|
962
|
+
// vec4 prevClip = reprojectionMatrix * vec4(frontPositionWorld, 1.0);
|
963
|
+
// prevClip /= prevClip.w;
|
964
|
+
// vec2 prevUv = prevClip.xy * 0.5 + 0.5;
|
965
|
+
// vec2 velocity = (vUv - prevUv) * resolution;
|
966
|
+
// depthVelocity = vec3(rayNearFar.y, velocity);
|
967
|
+
// }
|
968
|
+
|
969
|
+
}
|
970
|
+
|
971
|
+
#ifdef DEBUG_SHOW_FRONT_DEPTH
|
972
|
+
outputColor = vec4(turbo(frontDepth / maxRayDistance), 1.0);
|
973
|
+
outputDepthVelocity = vec3(0.0);
|
974
|
+
#ifdef SHADOW_LENGTH
|
975
|
+
outputShadowLength = 0.0;
|
976
|
+
#endif // SHADOW_LENGTH
|
977
|
+
return;
|
978
|
+
#endif // DEBUG_SHOW_FRONT_DEPTH
|
979
|
+
|
980
|
+
#ifdef HAZE
|
981
|
+
vec4 haze = approximateHaze(
|
982
|
+
cameraNear * rayDirection + cameraPosition,
|
983
|
+
rayDirection,
|
984
|
+
hazeRayNearFar.y - hazeRayNearFar.x,
|
985
|
+
cosTheta,
|
986
|
+
shadowLength
|
987
|
+
);
|
988
|
+
color = color * (1.0 - haze.a) + haze;
|
989
|
+
#endif // HAZE
|
990
|
+
|
991
|
+
outputColor = color;
|
992
|
+
outputDepthVelocity = depthVelocity;
|
993
|
+
#ifdef SHADOW_LENGTH
|
994
|
+
outputShadowLength = shadowLength * METER_TO_LENGTH_UNIT;
|
995
|
+
#endif // SHADOW_LENGTH
|
996
|
+
}
|