@takram/three-clouds 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +1130 -0
  3. package/assets/local_weather.png +0 -0
  4. package/assets/shape.bin +1 -0
  5. package/assets/shape_detail.bin +1 -0
  6. package/assets/turbulence.png +0 -0
  7. package/build/index.cjs +583 -0
  8. package/build/index.cjs.map +1 -0
  9. package/build/index.js +728 -0
  10. package/build/index.js.map +1 -0
  11. package/build/r3f.cjs +2 -0
  12. package/build/r3f.cjs.map +1 -0
  13. package/build/r3f.js +205 -0
  14. package/build/r3f.js.map +1 -0
  15. package/build/shared.cjs +2189 -0
  16. package/build/shared.cjs.map +1 -0
  17. package/build/shared.js +3825 -0
  18. package/build/shared.js.map +1 -0
  19. package/package.json +77 -0
  20. package/src/CascadedShadowMaps.ts +288 -0
  21. package/src/CloudLayer.ts +85 -0
  22. package/src/CloudLayers.test.ts +61 -0
  23. package/src/CloudLayers.ts +181 -0
  24. package/src/CloudShape.ts +22 -0
  25. package/src/CloudShapeDetail.ts +22 -0
  26. package/src/CloudsEffect.ts +810 -0
  27. package/src/CloudsMaterial.ts +467 -0
  28. package/src/CloudsPass.ts +285 -0
  29. package/src/CloudsResolveMaterial.ts +108 -0
  30. package/src/DensityProfile.ts +38 -0
  31. package/src/LocalWeather.ts +21 -0
  32. package/src/PassBase.ts +28 -0
  33. package/src/Procedural3DTexture.ts +94 -0
  34. package/src/ProceduralTexture.ts +94 -0
  35. package/src/ShaderArrayPass.ts +32 -0
  36. package/src/ShadowMaterial.ts +141 -0
  37. package/src/ShadowPass.ts +185 -0
  38. package/src/ShadowResolveMaterial.ts +72 -0
  39. package/src/Turbulence.ts +21 -0
  40. package/src/bayer.ts +23 -0
  41. package/src/constants.ts +8 -0
  42. package/src/helpers/FrustumCorners.ts +138 -0
  43. package/src/helpers/setArrayRenderTargetLayers.ts +32 -0
  44. package/src/helpers/splitFrustum.ts +59 -0
  45. package/src/index.ts +14 -0
  46. package/src/qualityPresets.ts +117 -0
  47. package/src/r3f/CloudLayer.tsx +95 -0
  48. package/src/r3f/CloudLayers.tsx +54 -0
  49. package/src/r3f/Clouds.tsx +278 -0
  50. package/src/r3f/index.ts +2 -0
  51. package/src/shaders/catmullRomSampling.glsl +113 -0
  52. package/src/shaders/cloudShape.frag +78 -0
  53. package/src/shaders/cloudShapeDetail.frag +56 -0
  54. package/src/shaders/clouds.frag +996 -0
  55. package/src/shaders/clouds.glsl +190 -0
  56. package/src/shaders/clouds.vert +69 -0
  57. package/src/shaders/cloudsEffect.frag +11 -0
  58. package/src/shaders/cloudsResolve.frag +202 -0
  59. package/src/shaders/cloudsResolve.vert +10 -0
  60. package/src/shaders/localWeather.frag +83 -0
  61. package/src/shaders/parameters.glsl +64 -0
  62. package/src/shaders/perlin.glsl +211 -0
  63. package/src/shaders/shadow.frag +197 -0
  64. package/src/shaders/shadow.vert +16 -0
  65. package/src/shaders/shadowResolve.frag +76 -0
  66. package/src/shaders/shadowResolve.vert +10 -0
  67. package/src/shaders/structuredSampling.glsl +101 -0
  68. package/src/shaders/tileableNoise.glsl +88 -0
  69. package/src/shaders/turbulence.frag +51 -0
  70. package/src/shaders/types.glsl +18 -0
  71. package/src/shaders/varianceClipping.glsl +114 -0
  72. package/src/uniforms.ts +218 -0
  73. package/types/CascadedShadowMaps.d.ts +52 -0
  74. package/types/CloudLayer.d.ts +26 -0
  75. package/types/CloudLayers.d.ts +21 -0
  76. package/types/CloudShape.d.ts +5 -0
  77. package/types/CloudShapeDetail.d.ts +5 -0
  78. package/types/CloudsEffect.d.ts +170 -0
  79. package/types/CloudsMaterial.d.ts +86 -0
  80. package/types/CloudsPass.d.ts +44 -0
  81. package/types/CloudsResolveMaterial.d.ts +30 -0
  82. package/types/DensityProfile.d.ts +12 -0
  83. package/types/LocalWeather.d.ts +5 -0
  84. package/types/PassBase.d.ts +14 -0
  85. package/types/Procedural3DTexture.d.ts +20 -0
  86. package/types/ProceduralTexture.d.ts +24 -0
  87. package/types/ShaderArrayPass.d.ts +7 -0
  88. package/types/ShadowMaterial.d.ts +34 -0
  89. package/types/ShadowPass.d.ts +34 -0
  90. package/types/ShadowResolveMaterial.d.ts +20 -0
  91. package/types/Turbulence.d.ts +5 -0
  92. package/types/bayer.d.ts +4 -0
  93. package/types/constants.d.ts +6 -0
  94. package/types/helpers/FrustumCorners.d.ts +18 -0
  95. package/types/helpers/setArrayRenderTargetLayers.d.ts +3 -0
  96. package/types/helpers/splitFrustum.d.ts +9 -0
  97. package/types/index.d.ts +13 -0
  98. package/types/qualityPresets.d.ts +46 -0
  99. package/types/r3f/CloudLayer.d.ts +7 -0
  100. package/types/r3f/CloudLayers.d.ts +15 -0
  101. package/types/r3f/Clouds.d.ts +16 -0
  102. package/types/r3f/index.d.ts +2 -0
  103. package/types/uniforms.d.ts +66 -0
@@ -0,0 +1,3825 @@
1
+ import { Pass as He, ShaderPass as Z, Resolution as q, Effect as Fe, EffectAttribute as ze } from "postprocessing";
2
+ import { Vector3 as f, Vector2 as m, Matrix4 as v, Object3D as se, Box3 as We, Uniform as r, GLSL3 as W, Vector4 as x, RawShaderMaterial as X, Camera as xe, WebGLRenderTarget as Ge, HalfFloatType as we, LinearFilter as z, RedFormat as Be, WebGLArrayRenderTarget as Ve, EventDispatcher as ke, Texture as ce, Data3DTexture as le } from "three";
3
+ import { AtmosphereMaterialBase as je, AtmosphereParameters as Ce, getAltitudeCorrectionOffset as Ye } from "@takram/three-atmosphere";
4
+ import { lerp as Te, defineInt as O, defineExpression as De, define as C, defineFloat as J, unrollLoops as G, resolveIncludes as M, assertType as Ze, Geodetic as qe, definePropertyShorthand as ue, defineUniformShorthand as he } from "@takram/three-geospatial";
5
+ import { functions as de, parameters as pe } from "@takram/three-atmosphere/shaders";
6
+ import { vogelDisk as Ke, interleavedGradientNoise as $e, cascadedShadowMaps as Xe, raySphereIntersection as Ee, generators as Je, turbo as Ae, math as _e, depth as Qe } from "@takram/three-geospatial/shaders";
7
+ class B {
8
+ constructor(e = 0, t = 0, n = 0, a = 0) {
9
+ this.expTerm = e, this.exponent = t, this.linearTerm = n, this.constantTerm = a;
10
+ }
11
+ set(e = 0, t = 0, n = 0, a = 0) {
12
+ return this.expTerm = e, this.exponent = t, this.linearTerm = n, this.constantTerm = a, this;
13
+ }
14
+ clone() {
15
+ return new B(
16
+ this.expTerm,
17
+ this.exponent,
18
+ this.linearTerm,
19
+ this.constantTerm
20
+ );
21
+ }
22
+ copy(e) {
23
+ return this.expTerm = e.expTerm, this.exponent = e.exponent, this.linearTerm = e.linearTerm, this.constantTerm = e.constantTerm, this;
24
+ }
25
+ }
26
+ const et = [
27
+ "channel",
28
+ "altitude",
29
+ "height",
30
+ "densityScale",
31
+ "shapeAmount",
32
+ "shapeDetailAmount",
33
+ "weatherExponent",
34
+ "shapeAlteringBias",
35
+ "coverageFilterWidth",
36
+ "shadow",
37
+ "densityProfile"
38
+ ];
39
+ function tt(o, e) {
40
+ if (e != null)
41
+ for (const t of et) {
42
+ const n = e[t];
43
+ n != null && (o[t] instanceof B ? o[t].copy(n) : o[t] = n);
44
+ }
45
+ }
46
+ const N = class N {
47
+ constructor(e) {
48
+ this.channel = "r", this.altitude = 0, this.height = 0, this.densityScale = 0.2, this.shapeAmount = 1, this.shapeDetailAmount = 1, this.weatherExponent = 1, this.shapeAlteringBias = 0.35, this.coverageFilterWidth = 0.6, this.densityProfile = new B(0, 0, 0.75, 0.25), this.shadow = !1, this.set(e);
49
+ }
50
+ set(e) {
51
+ return tt(this, e), this;
52
+ }
53
+ clone() {
54
+ return new N(this);
55
+ }
56
+ copy(e) {
57
+ return this.channel = e.channel, this.altitude = e.altitude, this.height = e.height, this.densityScale = e.densityScale, this.shapeAmount = e.shapeAmount, this.shapeDetailAmount = e.shapeDetailAmount, this.weatherExponent = e.weatherExponent, this.shapeAlteringBias = e.shapeAlteringBias, this.coverageFilterWidth = e.coverageFilterWidth, this.densityProfile.copy(e.densityProfile), this.shadow = e.shadow, this;
58
+ }
59
+ };
60
+ N.DEFAULT = /* @__PURE__ */ new N();
61
+ let T = N;
62
+ const P = /* @__PURE__ */ Array.from(
63
+ { length: 8 },
64
+ () => ({ value: 0, flag: 0 })
65
+ ), L = /* @__PURE__ */ Array.from(
66
+ { length: 3 },
67
+ () => ({ min: 0, max: 0 })
68
+ );
69
+ function nt(o, e) {
70
+ return o.value !== e.value ? o.value - e.value : o.flag - e.flag;
71
+ }
72
+ const b = class b extends Array {
73
+ constructor(e) {
74
+ super(
75
+ new T(e == null ? void 0 : e[0]),
76
+ new T(e == null ? void 0 : e[1]),
77
+ new T(e == null ? void 0 : e[2]),
78
+ new T(e == null ? void 0 : e[3])
79
+ );
80
+ }
81
+ set(e) {
82
+ return this[0].set(e == null ? void 0 : e[0]), this[1].set(e == null ? void 0 : e[1]), this[2].set(e == null ? void 0 : e[2]), this[3].set(e == null ? void 0 : e[3]), this;
83
+ }
84
+ reset() {
85
+ return this[0].copy(T.DEFAULT), this[1].copy(T.DEFAULT), this[2].copy(T.DEFAULT), this[3].copy(T.DEFAULT), this;
86
+ }
87
+ clone() {
88
+ return new b(this);
89
+ }
90
+ copy(e) {
91
+ return this[0].copy(e[0]), this[1].copy(e[1]), this[2].copy(e[2]), this[3].copy(e[3]), this;
92
+ }
93
+ get localWeatherChannels() {
94
+ return this[0].channel + this[1].channel + this[2].channel + this[3].channel;
95
+ }
96
+ packValues(e, t) {
97
+ return t.set(this[0][e], this[1][e], this[2][e], this[3][e]);
98
+ }
99
+ packSums(e, t, n) {
100
+ return n.set(
101
+ this[0][e] + this[0][t],
102
+ this[1][e] + this[1][t],
103
+ this[2][e] + this[2][t],
104
+ this[3][e] + this[3][t]
105
+ );
106
+ }
107
+ packDensityProfiles(e, t) {
108
+ return t.set(
109
+ this[0].densityProfile[e],
110
+ this[1].densityProfile[e],
111
+ this[2].densityProfile[e],
112
+ this[3].densityProfile[e]
113
+ );
114
+ }
115
+ // Redundant, but need to avoid creating garbage here as this runs every frame.
116
+ packIntervalHeights(e, t) {
117
+ for (let s = 0; s < 4; ++s) {
118
+ const c = this[s];
119
+ let u = P[s];
120
+ u.value = c.altitude, u.flag = 0, u = P[s + 4], u.value = c.altitude + c.height, u.flag = 1;
121
+ }
122
+ P.sort(nt);
123
+ let n = 0, a = 0;
124
+ for (let s = 0; s < P.length; ++s) {
125
+ const { value: c, flag: u } = P[s];
126
+ if (a === 0 && s > 0) {
127
+ const h = L[n++];
128
+ h.min = P[s - 1].value, h.max = c;
129
+ }
130
+ a += u === 0 ? 1 : -1;
131
+ }
132
+ for (; n < 3; ++n) {
133
+ const s = L[n];
134
+ s.min = 0, s.max = 0;
135
+ }
136
+ let i = L[0];
137
+ e.x = i.min, t.x = i.max, i = L[1], e.y = i.min, t.y = i.max, i = L[2], e.z = i.min, t.z = i.max;
138
+ }
139
+ };
140
+ b.DEFAULT = /* @__PURE__ */ new b([
141
+ {
142
+ channel: "r",
143
+ altitude: 750,
144
+ height: 650,
145
+ densityScale: 0.2,
146
+ shapeAmount: 1,
147
+ shapeDetailAmount: 1,
148
+ weatherExponent: 1,
149
+ shapeAlteringBias: 0.35,
150
+ coverageFilterWidth: 0.6,
151
+ shadow: !0
152
+ },
153
+ {
154
+ channel: "g",
155
+ altitude: 1e3,
156
+ height: 1200,
157
+ densityScale: 0.2,
158
+ shapeAmount: 1,
159
+ shapeDetailAmount: 1,
160
+ weatherExponent: 1,
161
+ shapeAlteringBias: 0.35,
162
+ coverageFilterWidth: 0.6,
163
+ shadow: !0
164
+ },
165
+ {
166
+ channel: "b",
167
+ altitude: 7500,
168
+ height: 500,
169
+ densityScale: 3e-3,
170
+ shapeAmount: 0.4,
171
+ shapeDetailAmount: 0,
172
+ weatherExponent: 1,
173
+ shapeAlteringBias: 0.35,
174
+ coverageFilterWidth: 0.5
175
+ },
176
+ { channel: "a" }
177
+ ]);
178
+ let K = b;
179
+ var at = process.env.NODE_ENV === "production", fe = "Invariant failed";
180
+ function A(o, e) {
181
+ if (!o) {
182
+ if (at)
183
+ throw new Error(fe);
184
+ var t = fe;
185
+ throw new Error(t);
186
+ }
187
+ }
188
+ class U {
189
+ constructor(e, t) {
190
+ this.near = [new f(), new f(), new f(), new f()], this.far = [new f(), new f(), new f(), new f()], e != null && t != null && this.setFromCamera(e, t);
191
+ }
192
+ clone() {
193
+ return new U().copy(this);
194
+ }
195
+ copy(e) {
196
+ for (let t = 0; t < 4; ++t)
197
+ this.near[t].copy(e.near[t]), this.far[t].copy(e.far[t]);
198
+ return this;
199
+ }
200
+ setFromCamera(e, t) {
201
+ const n = e.isOrthographicCamera === !0, a = e.projectionMatrixInverse;
202
+ this.near[0].set(1, 1, -1), this.near[1].set(1, -1, -1), this.near[2].set(-1, -1, -1), this.near[3].set(-1, 1, -1);
203
+ for (let i = 0; i < 4; ++i)
204
+ this.near[i].applyMatrix4(a);
205
+ this.far[0].set(1, 1, 1), this.far[1].set(1, -1, 1), this.far[2].set(-1, -1, 1), this.far[3].set(-1, 1, 1);
206
+ for (let i = 0; i < 4; ++i) {
207
+ const s = this.far[i];
208
+ s.applyMatrix4(a);
209
+ const c = Math.abs(s.z);
210
+ n ? s.z *= Math.min(t / c, 1) : s.multiplyScalar(Math.min(t / c, 1));
211
+ }
212
+ return this;
213
+ }
214
+ split(e, t = []) {
215
+ for (let n = 0; n < e.length; ++n) {
216
+ const a = t[n] ?? (t[n] = new U());
217
+ if (n === 0)
218
+ for (let i = 0; i < 4; ++i)
219
+ a.near[i].copy(this.near[i]);
220
+ else
221
+ for (let i = 0; i < 4; ++i)
222
+ a.near[i].lerpVectors(
223
+ this.near[i],
224
+ this.far[i],
225
+ e[n - 1]
226
+ );
227
+ if (n === e.length - 1)
228
+ for (let i = 0; i < 4; ++i)
229
+ a.far[i].copy(this.far[i]);
230
+ else
231
+ for (let i = 0; i < 4; ++i)
232
+ a.far[i].lerpVectors(
233
+ this.near[i],
234
+ this.far[i],
235
+ e[n]
236
+ );
237
+ }
238
+ return t.length = e.length, t;
239
+ }
240
+ applyMatrix4(e) {
241
+ for (let t = 0; t < 4; ++t)
242
+ this.near[t].applyMatrix4(e), this.far[t].applyMatrix4(e);
243
+ return this;
244
+ }
245
+ }
246
+ const it = {
247
+ uniform: (o, e, t, n, a = []) => {
248
+ for (let i = 0; i < o; ++i)
249
+ a[i] = (e + (t - e) * (i + 1) / o) / t;
250
+ return a.length = o, a;
251
+ },
252
+ logarithmic: (o, e, t, n, a = []) => {
253
+ for (let i = 0; i < o; ++i)
254
+ a[i] = e * (t / e) ** ((i + 1) / o) / t;
255
+ return a.length = o, a;
256
+ },
257
+ practical: (o, e, t, n = 0.5, a = []) => {
258
+ for (let i = 0; i < o; ++i) {
259
+ const s = (e + (t - e) * (i + 1) / o) / t, c = e * (t / e) ** ((i + 1) / o) / t;
260
+ a[i] = Te(s, c, n);
261
+ }
262
+ return a.length = o, a;
263
+ }
264
+ };
265
+ function rt(o, e, t, n, a, i = []) {
266
+ return it[o](e, t, n, a, i);
267
+ }
268
+ const me = /* @__PURE__ */ new f(), ve = /* @__PURE__ */ new f(), ot = /* @__PURE__ */ new v(), ge = /* @__PURE__ */ new v(), st = /* @__PURE__ */ new U(), ct = /* @__PURE__ */ new We(), lt = {
269
+ maxFar: null,
270
+ farScale: 1,
271
+ splitMode: "practical",
272
+ splitLambda: 0.5,
273
+ margin: 0,
274
+ fade: !0
275
+ };
276
+ class ut {
277
+ constructor(e) {
278
+ this.cascades = [], this.mapSize = new m(), this.cameraFrustum = new U(), this.frusta = [], this.splits = [], this._far = 0;
279
+ const {
280
+ cascadeCount: t,
281
+ mapSize: n,
282
+ maxFar: a,
283
+ farScale: i,
284
+ splitMode: s,
285
+ splitLambda: c,
286
+ margin: u,
287
+ fade: h
288
+ } = {
289
+ ...lt,
290
+ ...e
291
+ };
292
+ this.cascadeCount = t, this.mapSize.copy(n), this.maxFar = a, this.farScale = i, this.splitMode = s, this.splitLambda = c, this.margin = u, this.fade = h;
293
+ }
294
+ get cascadeCount() {
295
+ return this.cascades.length;
296
+ }
297
+ set cascadeCount(e) {
298
+ var t;
299
+ if (e !== this.cascadeCount) {
300
+ for (let n = 0; n < e; ++n)
301
+ (t = this.cascades)[n] ?? (t[n] = {
302
+ interval: new m(),
303
+ matrix: new v(),
304
+ inverseMatrix: new v(),
305
+ projectionMatrix: new v(),
306
+ inverseProjectionMatrix: new v(),
307
+ viewMatrix: new v(),
308
+ inverseViewMatrix: new v()
309
+ });
310
+ this.cascades.length = e;
311
+ }
312
+ }
313
+ get far() {
314
+ return this._far;
315
+ }
316
+ updateIntervals(e) {
317
+ const t = this.cascadeCount, n = this.splits, a = this.far;
318
+ rt(
319
+ this.splitMode,
320
+ t,
321
+ e.near,
322
+ a,
323
+ this.splitLambda,
324
+ n
325
+ ), this.cameraFrustum.setFromCamera(e, a), this.cameraFrustum.split(n, this.frusta);
326
+ const i = this.cascades;
327
+ for (let s = 0; s < t; ++s)
328
+ i[s].interval.set(n[s - 1] ?? 0, n[s] ?? 0);
329
+ }
330
+ getFrustumRadius(e, t) {
331
+ const n = t.near, a = t.far;
332
+ let i = Math.max(
333
+ a[0].distanceTo(a[2]),
334
+ a[0].distanceTo(n[2])
335
+ );
336
+ if (this.fade) {
337
+ const s = e.near, c = this.far, u = a[0].z / (c - s);
338
+ i += 0.25 * u ** 2 * (c - s);
339
+ }
340
+ return i * 0.5;
341
+ }
342
+ updateMatrices(e, t, n = 1) {
343
+ const a = ot.lookAt(
344
+ me.setScalar(0),
345
+ ve.copy(t).multiplyScalar(-1),
346
+ se.DEFAULT_UP
347
+ ), i = ge.multiplyMatrices(
348
+ ge.copy(a).invert(),
349
+ e.matrixWorld
350
+ ), s = this.frusta, c = this.cascades;
351
+ A(s.length === c.length);
352
+ const u = this.margin, h = this.mapSize;
353
+ for (let d = 0; d < s.length; ++d) {
354
+ const p = s[d], g = c[d], S = this.getFrustumRadius(e, s[d]), ee = -S, te = S, ne = S, ae = -S;
355
+ g.projectionMatrix.makeOrthographic(
356
+ ee,
357
+ te,
358
+ ne,
359
+ ae,
360
+ -this.margin,
361
+ // near
362
+ S * 2 + this.margin
363
+ // far
364
+ );
365
+ const { near: Me, far: Ue } = st.copy(p).applyMatrix4(i), H = ct.makeEmpty();
366
+ for (let F = 0; F < 4; F++)
367
+ H.expandByPoint(Me[F]), H.expandByPoint(Ue[F]);
368
+ const E = H.getCenter(me);
369
+ E.z = H.max.z + u;
370
+ const ie = (te - ee) / h.width, re = (ne - ae) / h.height;
371
+ E.x = Math.round(E.x / ie) * ie, E.y = Math.round(E.y / re) * re, E.applyMatrix4(a);
372
+ const oe = ve.copy(t).multiplyScalar(n).add(E);
373
+ g.inverseViewMatrix.lookAt(E, oe, se.DEFAULT_UP).setPosition(oe);
374
+ }
375
+ }
376
+ update(e, t, n) {
377
+ this._far = this.maxFar != null ? Math.min(this.maxFar, e.far * this.farScale) : e.far * this.farScale, this.updateIntervals(e), this.updateMatrices(e, t, n);
378
+ const a = this.cascades, i = this.cascadeCount;
379
+ for (let s = 0; s < i; ++s) {
380
+ const {
381
+ matrix: c,
382
+ inverseMatrix: u,
383
+ projectionMatrix: h,
384
+ inverseProjectionMatrix: d,
385
+ viewMatrix: p,
386
+ inverseViewMatrix: g
387
+ } = a[s];
388
+ d.copy(h).invert(), p.copy(g).invert(), c.copy(h).multiply(p), u.copy(g).multiply(d);
389
+ }
390
+ }
391
+ }
392
+ const Se = [
393
+ 0,
394
+ 8,
395
+ 2,
396
+ 10,
397
+ 12,
398
+ 4,
399
+ 14,
400
+ 6,
401
+ 3,
402
+ 11,
403
+ 1,
404
+ 9,
405
+ 15,
406
+ 7,
407
+ 13,
408
+ 5
409
+ ], Pe = /* @__PURE__ */ Se.reduce(
410
+ (o, e, t) => {
411
+ const n = new m();
412
+ for (let a = 0; a < 16; ++a)
413
+ if (Se[a] === t) {
414
+ n.set((a % 4 + 0.5) / 4, (Math.floor(a / 4) + 0.5) / 4);
415
+ break;
416
+ }
417
+ return [...o, n];
418
+ },
419
+ []
420
+ ), ht = {
421
+ resolutionScale: 1,
422
+ lightShafts: !0,
423
+ shapeDetail: !0,
424
+ turbulence: !0,
425
+ haze: !0,
426
+ clouds: {
427
+ multiScatteringOctaves: 8,
428
+ accurateSunSkyIrradiance: !0,
429
+ accuratePhaseFunction: !1,
430
+ // Primary raymarch
431
+ maxIterationCount: 500,
432
+ minStepSize: 50,
433
+ maxStepSize: 1e3,
434
+ maxRayDistance: 2e5,
435
+ perspectiveStepScale: 1.01,
436
+ minDensity: 1e-5,
437
+ minExtinction: 1e-5,
438
+ minTransmittance: 0.01,
439
+ // Secondary raymarch
440
+ maxIterationCountToGround: 3,
441
+ maxIterationCountToSun: 2,
442
+ minSecondaryStepSize: 100,
443
+ secondaryStepScale: 2,
444
+ // Shadow length
445
+ maxShadowLengthIterationCount: 500,
446
+ minShadowLengthStepSize: 50,
447
+ maxShadowLengthRayDistance: 2e5
448
+ },
449
+ shadow: {
450
+ cascadeCount: 3,
451
+ mapSize: /* @__PURE__ */ new m(512, 512),
452
+ // Primary raymarch
453
+ maxIterationCount: 50,
454
+ minStepSize: 100,
455
+ maxStepSize: 1e3,
456
+ minDensity: 1e-5,
457
+ minExtinction: 1e-5,
458
+ minTransmittance: 1e-4
459
+ }
460
+ }, l = ht, dt = {
461
+ // TODO: We cloud decrease multi-scattering octaves for lower quality presets,
462
+ // but it leads to a loss of higher frequency scattering, making it darker
463
+ // overall, which suggests the need for a fudge factor to scale the radiance.
464
+ low: {
465
+ ...l,
466
+ lightShafts: !1,
467
+ // Expensive
468
+ shapeDetail: !1,
469
+ // Expensive
470
+ turbulence: !1,
471
+ // Expensive
472
+ clouds: {
473
+ ...l.clouds,
474
+ accurateSunSkyIrradiance: !1,
475
+ // Greatly reduces texel reads.
476
+ maxIterationCount: 200,
477
+ minStepSize: 100,
478
+ maxRayDistance: 1e5,
479
+ minDensity: 1e-4,
480
+ minExtinction: 1e-4,
481
+ minTransmittance: 0.1,
482
+ // Makes the primary march terminate earlier.
483
+ maxIterationCountToGround: 0,
484
+ // Expensive
485
+ maxIterationCountToSun: 1
486
+ // Only 1 march makes big difference
487
+ },
488
+ shadow: {
489
+ ...l.shadow,
490
+ maxIterationCount: 25,
491
+ minDensity: 1e-4,
492
+ minExtinction: 1e-4,
493
+ minTransmittance: 0.01,
494
+ // Makes the primary march terminate earlier.
495
+ cascadeCount: 2,
496
+ // Obvious
497
+ mapSize: /* @__PURE__ */ new m(256, 256)
498
+ // Obvious
499
+ }
500
+ },
501
+ medium: {
502
+ ...l,
503
+ lightShafts: !1,
504
+ // Expensive
505
+ turbulence: !1,
506
+ // Expensive
507
+ clouds: {
508
+ ...l.clouds,
509
+ minDensity: 1e-4,
510
+ minExtinction: 1e-4,
511
+ accurateSunSkyIrradiance: !1,
512
+ maxIterationCountToSun: 2,
513
+ maxIterationCountToGround: 1
514
+ },
515
+ shadow: {
516
+ ...l.shadow,
517
+ minDensity: 1e-4,
518
+ minExtinction: 1e-4,
519
+ mapSize: /* @__PURE__ */ new m(256, 256)
520
+ }
521
+ },
522
+ high: l,
523
+ // Consider high quality preset as default.
524
+ ultra: {
525
+ ...l,
526
+ shadow: {
527
+ ...l.shadow,
528
+ mapSize: /* @__PURE__ */ new m(1024, 1024)
529
+ }
530
+ }
531
+ }, pt = `precision highp float;
532
+ precision highp sampler3D;
533
+ precision highp sampler2DArray;
534
+
535
+ #include <common>
536
+ #include <packing>
537
+
538
+ #include "core/depth"
539
+ #include "core/math"
540
+ #include "core/turbo"
541
+ #include "core/generators"
542
+ #include "core/raySphereIntersection"
543
+ #include "core/cascadedShadowMaps"
544
+ #include "core/interleavedGradientNoise"
545
+ #include "core/vogelDisk"
546
+ #include "atmosphere/parameters"
547
+ #include "atmosphere/functions"
548
+ #include "types"
549
+ #include "parameters"
550
+ #include "clouds"
551
+
552
+ #if !defined(RECIPROCAL_PI4)
553
+ #define RECIPROCAL_PI4 (0.07957747154594767)
554
+ #endif // !defined(RECIPROCAL_PI4)
555
+
556
+ uniform sampler2D depthBuffer;
557
+ uniform mat4 viewMatrix;
558
+ uniform mat4 reprojectionMatrix;
559
+ uniform float cameraNear;
560
+ uniform float cameraFar;
561
+ uniform float cameraHeight;
562
+ uniform vec2 temporalJitter;
563
+ uniform vec2 targetUvScale;
564
+ uniform float mipLevelScale;
565
+
566
+ // Scattering
567
+ const vec2 scatterAnisotropy = vec2(SCATTER_ANISOTROPY_1, SCATTER_ANISOTROPY_2);
568
+ const float scatterAnisotropyMix = SCATTER_ANISOTROPY_MIX;
569
+ uniform float skyIrradianceScale;
570
+ uniform float groundIrradianceScale;
571
+ uniform float powderScale;
572
+ uniform float powderExponent;
573
+
574
+ // Primary raymarch
575
+ uniform int maxIterationCount;
576
+ uniform float minStepSize;
577
+ uniform float maxStepSize;
578
+ uniform float maxRayDistance;
579
+ uniform float perspectiveStepScale;
580
+
581
+ // Secondary raymarch
582
+ uniform int maxIterationCountToSun;
583
+ uniform int maxIterationCountToGround;
584
+ uniform float minSecondaryStepSize;
585
+ uniform float secondaryStepScale;
586
+
587
+ // Beer shadow map
588
+ uniform sampler2DArray shadowBuffer;
589
+ uniform vec2 shadowTexelSize;
590
+ uniform vec2 shadowIntervals[SHADOW_CASCADE_COUNT];
591
+ uniform mat4 shadowMatrices[SHADOW_CASCADE_COUNT];
592
+ uniform float shadowFar;
593
+ uniform float maxShadowFilterRadius;
594
+
595
+ // Shadow length
596
+ #ifdef SHADOW_LENGTH
597
+ uniform int maxShadowLengthIterationCount;
598
+ uniform float minShadowLengthStepSize;
599
+ uniform float maxShadowLengthRayDistance;
600
+ #endif // SHADOW_LENGTH
601
+
602
+ in vec2 vUv;
603
+ in vec3 vCameraPosition;
604
+ in vec3 vCameraDirection; // Direction to the center of screen
605
+ in vec3 vRayDirection; // Direction to the texel
606
+ in vec3 vEllipsoidCenter;
607
+ in GroundIrradiance vGroundIrradiance;
608
+ in CloudsIrradiance vCloudsIrradiance;
609
+
610
+ layout(location = 0) out vec4 outputColor;
611
+ layout(location = 1) out vec3 outputDepthVelocity;
612
+ #ifdef SHADOW_LENGTH
613
+ layout(location = 2) out float outputShadowLength;
614
+ #endif // SHADOW_LENGTH
615
+
616
+ float readDepth(const vec2 uv) {
617
+ #if DEPTH_PACKING == 3201
618
+ return unpackRGBAToDepth(texture(depthBuffer, uv));
619
+ #else // DEPTH_PACKING == 3201
620
+ return texture(depthBuffer, uv).r;
621
+ #endif // DEPTH_PACKING == 3201
622
+ }
623
+
624
+ float getViewZ(const float depth) {
625
+ #ifdef PERSPECTIVE_CAMERA
626
+ return perspectiveDepthToViewZ(depth, cameraNear, cameraFar);
627
+ #else // PERSPECTIVE_CAMERA
628
+ return orthographicDepthToViewZ(depth, cameraNear, cameraFar);
629
+ #endif // PERSPECTIVE_CAMERA
630
+ }
631
+
632
+ vec3 ECEFToWorld(const vec3 positionECEF) {
633
+ return mat3(ellipsoidMatrix) * (positionECEF + vEllipsoidCenter);
634
+ }
635
+
636
+ vec2 getShadowUv(const vec3 worldPosition, const int cascadeIndex) {
637
+ vec4 clip = shadowMatrices[cascadeIndex] * vec4(worldPosition, 1.0);
638
+ clip /= clip.w;
639
+ return clip.xy * 0.5 + 0.5;
640
+ }
641
+
642
+ float getDistanceToShadowTop(const vec3 rayPosition) {
643
+ // Distance to the top of the shadows along the sun direction, which matches
644
+ // the ray origin of BSM.
645
+ return raySphereSecondIntersection(
646
+ rayPosition,
647
+ sunDirection,
648
+ vec3(0.0),
649
+ bottomRadius + shadowTopHeight
650
+ );
651
+ }
652
+
653
+ #ifdef DEBUG_SHOW_CASCADES
654
+
655
+ const vec3 cascadeColors[4] = vec3[4](
656
+ vec3(1.0, 0.0, 0.0),
657
+ vec3(0.0, 1.0, 0.0),
658
+ vec3(0.0, 0.0, 1.0),
659
+ vec3(1.0, 1.0, 0.0)
660
+ );
661
+
662
+ vec3 getCascadeColor(const vec3 rayPosition) {
663
+ vec3 worldPosition = ECEFToWorld(rayPosition);
664
+ int cascadeIndex = getCascadeIndex(
665
+ viewMatrix,
666
+ worldPosition,
667
+ shadowIntervals,
668
+ cameraNear,
669
+ shadowFar
670
+ );
671
+ vec2 uv = getShadowUv(worldPosition, cascadeIndex);
672
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
673
+ return vec3(1.0);
674
+ }
675
+ return cascadeColors[cascadeIndex];
676
+ }
677
+
678
+ vec3 getFadedCascadeColor(const vec3 rayPosition, const float jitter) {
679
+ vec3 worldPosition = ECEFToWorld(rayPosition);
680
+ int cascadeIndex = getFadedCascadeIndex(
681
+ viewMatrix,
682
+ worldPosition,
683
+ shadowIntervals,
684
+ cameraNear,
685
+ shadowFar,
686
+ jitter
687
+ );
688
+ return cascadeIndex >= 0
689
+ ? cascadeColors[cascadeIndex]
690
+ : vec3(1.0);
691
+ }
692
+
693
+ #endif // DEBUG_SHOW_CASCADES
694
+
695
+ float readShadowOpticalDepth(
696
+ const vec2 uv,
697
+ const float distanceToTop,
698
+ const float distanceOffset,
699
+ const int cascadeIndex
700
+ ) {
701
+ // r: frontDepth, g: meanExtinction, b: maxOpticalDepth, a: maxOpticalDepthTail
702
+ // Also see the discussion here: https://x.com/shotamatsuda/status/1885322308908442106
703
+ vec4 shadow = texture(shadowBuffer, vec3(uv, float(cascadeIndex)));
704
+ float distanceToFront = max(0.0, distanceToTop - distanceOffset - shadow.r);
705
+ return min(shadow.b + shadow.a, shadow.g * distanceToFront);
706
+ }
707
+
708
+ float sampleShadowOpticalDepthPCF(
709
+ const vec3 worldPosition,
710
+ const float distanceToTop,
711
+ const float distanceOffset,
712
+ const float radius,
713
+ const int cascadeIndex
714
+ ) {
715
+ vec2 uv = getShadowUv(worldPosition, cascadeIndex);
716
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
717
+ return 0.0;
718
+ }
719
+ if (radius < 0.1) {
720
+ return readShadowOpticalDepth(uv, distanceToTop, distanceOffset, cascadeIndex);
721
+ }
722
+ float sum = 0.0;
723
+ vec2 offset;
724
+ #pragma unroll_loop_start
725
+ for (int i = 0; i < 16; ++i) {
726
+ #if UNROLLED_LOOP_INDEX < SHADOW_SAMPLE_COUNT
727
+ offset = vogelDisk(
728
+ UNROLLED_LOOP_INDEX,
729
+ SHADOW_SAMPLE_COUNT,
730
+ interleavedGradientNoise(gl_FragCoord.xy + temporalJitter * resolution) * PI2
731
+ );
732
+ sum += readShadowOpticalDepth(
733
+ uv + offset * radius * shadowTexelSize,
734
+ distanceToTop,
735
+ distanceOffset,
736
+ cascadeIndex
737
+ );
738
+ #endif // UNROLLED_LOOP_INDEX < SHADOW_SAMPLE_COUNT
739
+ }
740
+ #pragma unroll_loop_end
741
+ return sum / float(SHADOW_SAMPLE_COUNT);
742
+ }
743
+
744
+ float sampleShadowOpticalDepth(
745
+ const vec3 rayPosition,
746
+ const float distanceOffset,
747
+ const float radius,
748
+ const float jitter
749
+ ) {
750
+ float distanceToTop = getDistanceToShadowTop(rayPosition);
751
+ if (distanceToTop <= 0.0) {
752
+ return 0.0;
753
+ }
754
+ vec3 worldPosition = ECEFToWorld(rayPosition);
755
+ int cascadeIndex = getFadedCascadeIndex(
756
+ viewMatrix,
757
+ worldPosition,
758
+ shadowIntervals,
759
+ cameraNear,
760
+ shadowFar,
761
+ jitter
762
+ );
763
+ return cascadeIndex >= 0
764
+ ? sampleShadowOpticalDepthPCF(
765
+ worldPosition,
766
+ distanceToTop,
767
+ distanceOffset,
768
+ radius,
769
+ cascadeIndex
770
+ )
771
+ : 0.0;
772
+ }
773
+
774
+ #ifdef DEBUG_SHOW_SHADOW_MAP
775
+ vec4 getCascadedShadowMaps(vec2 uv) {
776
+ vec4 coord = vec4(vUv, vUv - 0.5) * 2.0;
777
+ vec4 shadow = vec4(0.0);
778
+ if (uv.y > 0.5) {
779
+ if (uv.x < 0.5) {
780
+ shadow = texture(shadowBuffer, vec3(coord.xw, 0.0));
781
+ } else {
782
+ #if SHADOW_CASCADE_COUNT > 1
783
+ shadow = texture(shadowBuffer, vec3(coord.zw, 1.0));
784
+ #endif // SHADOW_CASCADE_COUNT > 1
785
+ }
786
+ } else {
787
+ if (uv.x < 0.5) {
788
+ #if SHADOW_CASCADE_COUNT > 2
789
+ shadow = texture(shadowBuffer, vec3(coord.xy, 2.0));
790
+ #endif // SHADOW_CASCADE_COUNT > 2
791
+ } else {
792
+ #if SHADOW_CASCADE_COUNT > 3
793
+ shadow = texture(shadowBuffer, vec3(coord.zy, 3.0));
794
+ #endif // SHADOW_CASCADE_COUNT > 3
795
+ }
796
+ }
797
+
798
+ #if !defined(DEBUG_SHOW_SHADOW_MAP_TYPE)
799
+ #define DEBUG_SHOW_SHADOW_MAP_TYPE (0)
800
+ #endif // !defined(DEBUG_SHOW_SHADOW_MAP_TYPE
801
+
802
+ const float frontDepthScale = 1e-5;
803
+ const float meanExtinctionScale = 10.0;
804
+ const float maxOpticalDepthScale = 0.01;
805
+ vec3 color;
806
+ #if DEBUG_SHOW_SHADOW_MAP_TYPE == 1
807
+ color = vec3(shadow.r * frontDepthScale);
808
+ #elif DEBUG_SHOW_SHADOW_MAP_TYPE == 2
809
+ color = vec3(shadow.g * meanExtinctionScale);
810
+ #elif DEBUG_SHOW_SHADOW_MAP_TYPE == 3
811
+ color = vec3((shadow.b + shadow.a) * maxOpticalDepthScale);
812
+ #else // DEBUG_SHOW_SHADOW_MAP_TYPE
813
+ color =
814
+ (shadow.rgb + vec3(0.0, 0.0, shadow.a)) *
815
+ vec3(frontDepthScale, meanExtinctionScale, maxOpticalDepthScale);
816
+ #endif // DEBUG_SHOW_SHADOW_MAP_TYPE
817
+ return vec4(color, 1.0);
818
+ }
819
+ #endif // DEBUG_SHOW_SHADOW_MAP
820
+
821
+ vec2 henyeyGreenstein(const vec2 g, const float cosTheta) {
822
+ vec2 g2 = g * g;
823
+ // prettier-ignore
824
+ return RECIPROCAL_PI4 *
825
+ ((1.0 - g2) / max(vec2(1e-7), pow(1.0 + g2 - 2.0 * g * cosTheta, vec2(1.5))));
826
+ }
827
+
828
+ #ifdef ACCURATE_PHASE_FUNCTION
829
+
830
+ float draine(float u, float g, float a) {
831
+ float g2 = g * g;
832
+ // prettier-ignore
833
+ return (1.0 - g2) *
834
+ (1.0 + a * u * u) /
835
+ (4.0 * (1.0 + a * (1.0 + 2.0 * g2) / 3.0) * PI * pow(1.0 + g2 - 2.0 * g * u, 1.5));
836
+ }
837
+
838
+ // Numerically-fitted large particles (d=10) phase function It won't be
839
+ // plausible without a more precise multiple scattering.
840
+ // Reference: https://research.nvidia.com/labs/rtr/approximate-mie/
841
+ float phaseFunction(const float cosTheta, const float attenuation) {
842
+ const float gHG = 0.988176691700256; // exp(-0.0990567/(d-1.67154))
843
+ const float gD = 0.5556712547839497; // exp(-2.20679/(d+3.91029) - 0.428934)
844
+ const float alpha = 21.995520856274638; // exp(3.62489 - 8.29288/(d+5.52825))
845
+ const float weight = 0.4819554318404214; // exp(-0.599085/(d-0.641583)-0.665888)
846
+ return mix(
847
+ henyeyGreenstein(vec2(gHG) * attenuation, cosTheta).x,
848
+ draine(cosTheta, gD * attenuation, alpha),
849
+ weight
850
+ );
851
+ }
852
+
853
+ #else // ACCURATE_PHASE_FUNCTION
854
+
855
+ float phaseFunction(const float cosTheta, const float attenuation) {
856
+ const vec2 g = scatterAnisotropy;
857
+ const vec2 weights = vec2(1.0 - scatterAnisotropyMix, scatterAnisotropyMix);
858
+ // A similar approximation is described in the Frostbite's paper, where phase
859
+ // angle is attenuated instead of anisotropy.
860
+ return dot(henyeyGreenstein(g * attenuation, cosTheta), weights);
861
+ }
862
+
863
+ #endif // ACCURATE_PHASE_FUNCTION
864
+
865
+ float phaseFunction(const float cosTheta) {
866
+ return phaseFunction(cosTheta, 1.0);
867
+ }
868
+
869
+ float marchOpticalDepth(
870
+ const vec3 rayOrigin,
871
+ const vec3 rayDirection,
872
+ const int maxIterationCount,
873
+ const float mipLevel,
874
+ const float jitter,
875
+ out float rayDistance
876
+ ) {
877
+ int iterationCount = int(
878
+ max(0.0, remap(mipLevel, 0.0, 1.0, float(maxIterationCount + 1), 1.0) - jitter)
879
+ );
880
+ if (iterationCount == 0) {
881
+ // Fudge factor to approximate the mean optical depth.
882
+ // TODO: Remove it.
883
+ return 0.5;
884
+ }
885
+ float stepSize = minSecondaryStepSize / float(iterationCount);
886
+ float nextDistance = stepSize * jitter;
887
+ float opticalDepth = 0.0;
888
+ for (int i = 0; i < iterationCount; ++i) {
889
+ rayDistance = nextDistance;
890
+ vec3 position = rayDistance * rayDirection + rayOrigin;
891
+ vec2 uv = getGlobeUv(position);
892
+ float height = length(position) - bottomRadius;
893
+ WeatherSample weather = sampleWeather(uv, height, mipLevel);
894
+ MediaSample media = sampleMedia(weather, position, uv, mipLevel, jitter);
895
+ opticalDepth += media.extinction * stepSize;
896
+ nextDistance += stepSize;
897
+ stepSize *= secondaryStepScale;
898
+ }
899
+ return opticalDepth;
900
+ }
901
+
902
+ float marchOpticalDepth(
903
+ const vec3 rayOrigin,
904
+ const vec3 rayDirection,
905
+ const int maxIterationCount,
906
+ const float mipLevel,
907
+ const float jitter
908
+ ) {
909
+ float rayDistance;
910
+ return marchOpticalDepth(
911
+ rayOrigin,
912
+ rayDirection,
913
+ maxIterationCount,
914
+ mipLevel,
915
+ jitter,
916
+ rayDistance
917
+ );
918
+ }
919
+
920
+ float approximateMultipleScattering(const float opticalDepth, const float cosTheta) {
921
+ // Multiple scattering approximation
922
+ // See: https://fpsunflower.github.io/ckulla/data/oz_volumes.pdf
923
+ // a: attenuation, b: contribution, c: phase attenuation
924
+ vec3 coeffs = vec3(1.0); // [a, b, c]
925
+ const vec3 attenuation = vec3(0.5, 0.5, 0.5); // Should satisfy a <= b
926
+ float scattering = 0.0;
927
+ float beerLambert;
928
+ #pragma unroll_loop_start
929
+ for (int i = 0; i < 12; ++i) {
930
+ #if UNROLLED_LOOP_INDEX < MULTI_SCATTERING_OCTAVES
931
+ beerLambert = exp(-opticalDepth * coeffs.y);
932
+ scattering += coeffs.x * beerLambert * phaseFunction(cosTheta, coeffs.z);
933
+ coeffs *= attenuation;
934
+ #endif // UNROLLED_LOOP_INDEX < MULTI_SCATTERING_OCTAVES
935
+ }
936
+ #pragma unroll_loop_end
937
+ return scattering;
938
+ }
939
+
940
+ // TODO: Construct spherical harmonics of degree 2 using 2 sample points
941
+ // positioned near the horizon occlusion points on the sun direction plane.
942
+ vec3 getGroundSunSkyIrradiance(
943
+ const vec3 position,
944
+ const vec3 surfaceNormal,
945
+ const float height,
946
+ out vec3 skyIrradiance
947
+ ) {
948
+ #ifdef ACCURATE_SUN_SKY_IRRADIANCE
949
+ return GetSunAndSkyIrradiance(
950
+ (position - surfaceNormal * height) * METER_TO_LENGTH_UNIT,
951
+ sunDirection,
952
+ skyIrradiance
953
+ );
954
+ #else // ACCURATE_SUN_SKY_IRRADIANCE
955
+ skyIrradiance = vGroundIrradiance.sky;
956
+ return vGroundIrradiance.sun;
957
+ #endif // ACCURATE_SUN_SKY_IRRADIANCE
958
+ }
959
+
960
+ vec3 getCloudsSunSkyIrradiance(const vec3 position, const float height, out vec3 skyIrradiance) {
961
+ #ifdef ACCURATE_SUN_SKY_IRRADIANCE
962
+ return GetSunAndSkyIrradiance(position * METER_TO_LENGTH_UNIT, sunDirection, skyIrradiance);
963
+ #else // ACCURATE_SUN_SKY_IRRADIANCE
964
+ float alpha = remapClamped(height, minHeight, maxHeight);
965
+ skyIrradiance = mix(vCloudsIrradiance.minSky, vCloudsIrradiance.maxSky, alpha);
966
+ return mix(vCloudsIrradiance.minSun, vCloudsIrradiance.maxSun, alpha);
967
+ #endif // ACCURATE_SUN_SKY_IRRADIANCE
968
+ }
969
+
970
+ #ifdef GROUND_IRRADIANCE
971
+ vec3 approximateIrradianceFromGround(
972
+ const vec3 position,
973
+ const vec3 surfaceNormal,
974
+ const float height,
975
+ const float mipLevel,
976
+ const float jitter
977
+ ) {
978
+ float opticalDepthToGround = marchOpticalDepth(
979
+ position,
980
+ -surfaceNormal,
981
+ maxIterationCountToGround,
982
+ mipLevel,
983
+ jitter
984
+ );
985
+ vec3 skyIrradiance;
986
+ vec3 sunIrradiance = getGroundSunSkyIrradiance(position, surfaceNormal, height, skyIrradiance);
987
+ const float groundAlbedo = 0.3;
988
+ vec3 groundIrradiance = skyIrradiance + (1.0 - coverage) * sunIrradiance * RECIPROCAL_PI2;
989
+ vec3 bouncedLight = groundAlbedo * RECIPROCAL_PI * groundIrradiance;
990
+ vec3 bouncedIrradiance = bouncedLight * exp(-opticalDepthToGround);
991
+ return albedo * bouncedIrradiance * RECIPROCAL_PI4 * groundIrradianceScale;
992
+ }
993
+ #endif // GROUND_IRRADIANCE
994
+
995
+ vec4 marchClouds(
996
+ const vec3 rayOrigin,
997
+ const vec3 rayDirection,
998
+ const vec2 rayNearFar,
999
+ const float cosTheta,
1000
+ const float jitter,
1001
+ const float rayStartTexelsPerPixel,
1002
+ out float frontDepth,
1003
+ out ivec3 sampleCount
1004
+ ) {
1005
+ vec3 radianceIntegral = vec3(0.0);
1006
+ float transmittanceIntegral = 1.0;
1007
+ float weightedDistanceSum = 0.0;
1008
+ float transmittanceSum = 0.0;
1009
+
1010
+ float maxRayDistance = rayNearFar.y - rayNearFar.x;
1011
+ float stepSize = minStepSize + (perspectiveStepScale - 1.0) * rayNearFar.x;
1012
+ // I don't understand why spatial aliasing remains unless doubling the jitter.
1013
+ float rayDistance = stepSize * jitter * 2.0;
1014
+
1015
+ for (int i = 0; i < maxIterationCount; ++i) {
1016
+ if (rayDistance > maxRayDistance) {
1017
+ break; // Termination
1018
+ }
1019
+
1020
+ vec3 position = rayDistance * rayDirection + rayOrigin;
1021
+ float height = length(position) - bottomRadius;
1022
+ float mipLevel = log2(max(1.0, rayStartTexelsPerPixel + rayDistance * 1e-5));
1023
+
1024
+ #if !defined(DEBUG_MARCH_INTERVALS)
1025
+ if (insideLayerIntervals(height)) {
1026
+ stepSize *= perspectiveStepScale;
1027
+ rayDistance += mix(stepSize, maxStepSize, min(1.0, mipLevel));
1028
+ continue;
1029
+ }
1030
+ #endif // !defined(DEBUG_MARCH_INTERVALS)
1031
+
1032
+ // Sample rough weather.
1033
+ vec2 uv = getGlobeUv(position);
1034
+ WeatherSample weather = sampleWeather(uv, height, mipLevel);
1035
+
1036
+ #ifdef DEBUG_SHOW_SAMPLE_COUNT
1037
+ ++sampleCount.x;
1038
+ #endif // DEBUG_SHOW_SAMPLE_COUNT
1039
+
1040
+ if (!any(greaterThan(weather.density, vec4(minDensity)))) {
1041
+ // Step longer in empty space.
1042
+ // TODO: This produces banding artifacts.
1043
+ // Possible improvement: Binary search refinement
1044
+ stepSize *= perspectiveStepScale;
1045
+ rayDistance += mix(stepSize, maxStepSize, min(1.0, mipLevel));
1046
+ continue;
1047
+ }
1048
+
1049
+ // Sample detailed participating media.
1050
+ MediaSample media = sampleMedia(weather, position, uv, mipLevel, jitter, sampleCount);
1051
+
1052
+ if (media.extinction > minExtinction) {
1053
+ vec3 skyIrradiance;
1054
+ vec3 sunIrradiance = getCloudsSunSkyIrradiance(position, height, skyIrradiance);
1055
+ vec3 surfaceNormal = normalize(position);
1056
+
1057
+ // March optical depth to the sun for finer details, which BSM lacks.
1058
+ float sunRayDistance = 0.0;
1059
+ float opticalDepth = marchOpticalDepth(
1060
+ position,
1061
+ sunDirection,
1062
+ maxIterationCountToSun,
1063
+ mipLevel,
1064
+ jitter,
1065
+ sunRayDistance
1066
+ );
1067
+
1068
+ if (height < shadowTopHeight) {
1069
+ // Obtain the optical depth from BSM at the ray position.
1070
+ opticalDepth += sampleShadowOpticalDepth(
1071
+ position,
1072
+ // Take account of only positions further than the marched ray
1073
+ // distance.
1074
+ sunRayDistance,
1075
+ // Apply PCF only when the sun is close to the horizon.
1076
+ maxShadowFilterRadius * remapClamped(dot(sunDirection, surfaceNormal), 0.1, 0.0),
1077
+ jitter
1078
+ );
1079
+ }
1080
+
1081
+ float scattering = approximateMultipleScattering(opticalDepth, cosTheta);
1082
+ vec3 radiance = albedo * sunIrradiance * scattering;
1083
+
1084
+ #ifdef GROUND_IRRADIANCE
1085
+ // Fudge factor for the irradiance from ground.
1086
+ if (height < shadowTopHeight && mipLevel < 0.5) {
1087
+ radiance += approximateIrradianceFromGround(
1088
+ position,
1089
+ surfaceNormal,
1090
+ height,
1091
+ mipLevel,
1092
+ jitter
1093
+ );
1094
+ }
1095
+ #endif // GROUND_IRRADIANCE
1096
+
1097
+ // Crude approximation of sky gradient. Better than none in the shadows.
1098
+ float skyGradient = dot(0.5 + weather.heightFraction, media.weight);
1099
+ radiance += albedo * skyIrradiance * RECIPROCAL_PI4 * skyGradient * skyIrradianceScale;
1100
+
1101
+ // Finally multiply by extinction (redundant but kept for clarity).
1102
+ radiance *= media.extinction;
1103
+
1104
+ #ifdef POWDER
1105
+ radiance *= 1.0 - powderScale * exp(-media.extinction * powderExponent);
1106
+ #endif // POWDER
1107
+
1108
+ #ifdef DEBUG_SHOW_CASCADES
1109
+ if (height < shadowTopHeight) {
1110
+ radiance = 1e-3 * getFadedCascadeColor(position, jitter);
1111
+ }
1112
+ #endif // DEBUG_SHOW_CASCADES
1113
+
1114
+ // Energy-conserving analytical integration of scattered light
1115
+ // See 5.6.3 in https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/s2016-pbs-frostbite-sky-clouds-new.pdf
1116
+ float transmittance = exp(-media.extinction * stepSize);
1117
+ float clampedExtinction = max(media.extinction, 1e-7);
1118
+ vec3 scatteringIntegral = (radiance - radiance * transmittance) / clampedExtinction;
1119
+ radianceIntegral += transmittanceIntegral * scatteringIntegral;
1120
+ transmittanceIntegral *= transmittance;
1121
+
1122
+ // Aerial perspective affecting clouds
1123
+ // See 5.9.1 in https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/s2016-pbs-frostbite-sky-clouds-new.pdf
1124
+ weightedDistanceSum += rayDistance * transmittanceIntegral;
1125
+ transmittanceSum += transmittanceIntegral;
1126
+ }
1127
+
1128
+ if (transmittanceIntegral <= minTransmittance) {
1129
+ break; // Early termination
1130
+ }
1131
+
1132
+ // Take a shorter step because we've already hit the clouds.
1133
+ stepSize *= perspectiveStepScale;
1134
+ rayDistance += stepSize;
1135
+ }
1136
+
1137
+ // The final product of 5.9.1 and we'll evaluate this in aerial perspective.
1138
+ frontDepth = transmittanceSum > 0.0 ? weightedDistanceSum / transmittanceSum : -1.0;
1139
+
1140
+ return vec4(radianceIntegral, remapClamped(transmittanceIntegral, 1.0, minTransmittance));
1141
+ }
1142
+
1143
+ #ifdef SHADOW_LENGTH
1144
+
1145
+ float marchShadowLength(
1146
+ const vec3 rayOrigin,
1147
+ const vec3 rayDirection,
1148
+ const vec2 rayNearFar,
1149
+ const float jitter
1150
+ ) {
1151
+ float shadowLength = 0.0;
1152
+ float maxRayDistance = rayNearFar.y - rayNearFar.x;
1153
+ float stepSize = minShadowLengthStepSize;
1154
+ float rayDistance = stepSize * jitter;
1155
+ const float attenuationFactor = 1.0 - 1e-3;
1156
+ float attenuation = 1.0;
1157
+
1158
+ // TODO: This march is closed, and sample resolution can be much lower.
1159
+ // Refining the termination by binary search will make it much more efficient.
1160
+ for (int i = 0; i < maxShadowLengthIterationCount; ++i) {
1161
+ if (rayDistance > maxRayDistance) {
1162
+ break; // Termination
1163
+ }
1164
+ vec3 position = rayDistance * rayDirection + rayOrigin;
1165
+ float opticalDepth = sampleShadowOpticalDepth(position, 0.0, 0.0, jitter);
1166
+ shadowLength += (1.0 - exp(-opticalDepth)) * stepSize * attenuation;
1167
+
1168
+ // Hack to prevent over-integration of shadow length. The shadow should be
1169
+ // attenuated by the inscatter as the ray travels further.
1170
+ attenuation *= attenuationFactor;
1171
+ if (attenuation < 1e-5) {
1172
+ break;
1173
+ }
1174
+
1175
+ stepSize *= perspectiveStepScale;
1176
+ rayDistance += stepSize;
1177
+ }
1178
+ return shadowLength;
1179
+ }
1180
+
1181
+ #endif // SHADOW_LENGTH
1182
+
1183
+ #ifdef HAZE
1184
+
1185
+ vec4 approximateHaze(
1186
+ const vec3 rayOrigin,
1187
+ const vec3 rayDirection,
1188
+ const float maxRayDistance,
1189
+ const float cosTheta,
1190
+ const float shadowLength
1191
+ ) {
1192
+ float modulation = remapClamped(coverage, 0.2, 0.4);
1193
+ if (cameraHeight * modulation < 0.0) {
1194
+ return vec4(0.0);
1195
+ }
1196
+ float density = modulation * hazeDensityScale * exp(-cameraHeight * hazeExponent);
1197
+ if (density < 1e-7) {
1198
+ return vec4(0.0); // Prevent artifact in views from space
1199
+ }
1200
+
1201
+ // Analytical optical depth where density exponentially decreases with height.
1202
+ // Based on: https://iquilezles.org/articles/fog/
1203
+ float angle = max(dot(normalize(rayOrigin), rayDirection), 1e-5);
1204
+ float exponent = angle * hazeExponent;
1205
+ // Derive the optical depths separately for with and without shadow length.
1206
+ float expTerm = 1.0 - exp(-maxRayDistance * exponent);
1207
+ float shadowExpTerm = 1.0 - exp(-min(maxRayDistance, shadowLength) * exponent);
1208
+ float linearTerm = density / hazeExponent / angle;
1209
+ float opticalDepth = expTerm * linearTerm;
1210
+ float effectiveOpticalDepth = max((expTerm - shadowExpTerm) * linearTerm, 0.0);
1211
+
1212
+ vec3 skyIrradiance = vGroundIrradiance.sky;
1213
+ vec3 sunIrradiance = vGroundIrradiance.sun;
1214
+ vec3 irradiance = sunIrradiance * phaseFunction(cosTheta);
1215
+ irradiance += skyIrradiance * RECIPROCAL_PI4 * skyIrradianceScale;
1216
+ vec3 inscatter = albedo * irradiance * saturate(1.0 - exp(-effectiveOpticalDepth));
1217
+
1218
+ // Inscatter is attenuated by shadow length, but transmittance is not.
1219
+ return vec4(inscatter, saturate(1.0 - exp(-opticalDepth)));
1220
+ }
1221
+
1222
+ #endif // HAZE
1223
+
1224
+ void applyAerialPerspective(
1225
+ const vec3 cameraPosition,
1226
+ const vec3 frontPosition,
1227
+ const float shadowLength,
1228
+ inout vec4 color
1229
+ ) {
1230
+ vec3 transmittance;
1231
+ vec3 inscatter = GetSkyRadianceToPoint(
1232
+ cameraPosition * METER_TO_LENGTH_UNIT,
1233
+ frontPosition * METER_TO_LENGTH_UNIT,
1234
+ shadowLength * METER_TO_LENGTH_UNIT,
1235
+ sunDirection,
1236
+ transmittance
1237
+ );
1238
+ float clampedAlpha = max(color.a, 1e-7);
1239
+ color.rgb = mix(vec3(0.0), color.rgb * transmittance / clampedAlpha + inscatter, color.a);
1240
+ }
1241
+
1242
+ bool rayIntersectsGround(const vec3 cameraPosition, const vec3 rayDirection) {
1243
+ float r = length(cameraPosition);
1244
+ float mu = dot(cameraPosition, rayDirection) / r;
1245
+ return mu < 0.0 && r * r * (mu * mu - 1.0) + bottomRadius * bottomRadius >= 0.0;
1246
+ }
1247
+
1248
+ struct IntersectionResult {
1249
+ bool ground;
1250
+ vec4 first;
1251
+ vec4 second;
1252
+ };
1253
+
1254
+ IntersectionResult getIntersections(const vec3 cameraPosition, const vec3 rayDirection) {
1255
+ IntersectionResult intersections;
1256
+ intersections.ground = rayIntersectsGround(cameraPosition, rayDirection);
1257
+ raySphereIntersections(
1258
+ cameraPosition,
1259
+ rayDirection,
1260
+ bottomRadius + vec4(0.0, minHeight, maxHeight, shadowTopHeight),
1261
+ intersections.first,
1262
+ intersections.second
1263
+ );
1264
+ return intersections;
1265
+ }
1266
+
1267
+ vec2 getRayNearFar(const IntersectionResult intersections) {
1268
+ vec2 nearFar;
1269
+ if (cameraHeight < minHeight) {
1270
+ // View below the clouds
1271
+ if (intersections.ground) {
1272
+ nearFar = vec2(-1.0); // No clouds to the ground
1273
+ } else {
1274
+ nearFar = vec2(intersections.second.y, intersections.second.z);
1275
+ nearFar.y = min(nearFar.y, maxRayDistance);
1276
+ }
1277
+ } else if (cameraHeight < maxHeight) {
1278
+ // View inside the total cloud layer
1279
+ if (intersections.ground) {
1280
+ nearFar = vec2(cameraNear, intersections.first.y);
1281
+ } else {
1282
+ nearFar = vec2(cameraNear, intersections.second.z);
1283
+ }
1284
+ } else {
1285
+ // View above the clouds
1286
+ nearFar = vec2(intersections.first.z, intersections.second.z);
1287
+ if (intersections.ground) {
1288
+ // Clamp the ray at the min height.
1289
+ nearFar.y = intersections.first.y;
1290
+ }
1291
+ }
1292
+ return nearFar;
1293
+ }
1294
+
1295
+ #ifdef SHADOW_LENGTH
1296
+ vec2 getShadowRayNearFar(const IntersectionResult intersections) {
1297
+ vec2 nearFar;
1298
+ if (cameraHeight < shadowTopHeight) {
1299
+ if (intersections.ground) {
1300
+ nearFar = vec2(cameraNear, intersections.first.x);
1301
+ } else {
1302
+ nearFar = vec2(cameraNear, intersections.second.w);
1303
+ }
1304
+ } else {
1305
+ nearFar = vec2(intersections.first.w, intersections.second.w);
1306
+ if (intersections.ground) {
1307
+ // Clamp the ray at the ground.
1308
+ nearFar.y = intersections.first.x;
1309
+ }
1310
+ }
1311
+ nearFar.y = min(nearFar.y, maxShadowLengthRayDistance);
1312
+ return nearFar;
1313
+ }
1314
+ #endif // SHADOW_LENGTH
1315
+
1316
+ #ifdef HAZE
1317
+ vec2 getHazeRayNearFar(const IntersectionResult intersections) {
1318
+ vec2 nearFar;
1319
+ if (cameraHeight < maxHeight) {
1320
+ if (intersections.ground) {
1321
+ nearFar = vec2(cameraNear, intersections.first.x);
1322
+ } else {
1323
+ nearFar = vec2(cameraNear, intersections.second.z);
1324
+ }
1325
+ } else {
1326
+ nearFar = vec2(cameraNear, intersections.second.z);
1327
+ if (intersections.ground) {
1328
+ // Clamp the ray at the ground.
1329
+ nearFar.y = intersections.first.x;
1330
+ }
1331
+ }
1332
+ return nearFar;
1333
+ }
1334
+ #endif // HAZE
1335
+
1336
+ float getRayDistanceToScene(const vec3 rayDirection) {
1337
+ float depth = readDepth(vUv * targetUvScale + temporalJitter);
1338
+ if (depth < 1.0 - 1e-7) {
1339
+ depth = reverseLogDepth(depth, cameraNear, cameraFar);
1340
+ float viewZ = getViewZ(depth);
1341
+ return -viewZ / dot(rayDirection, vCameraDirection);
1342
+ }
1343
+ return -1.0;
1344
+ }
1345
+
1346
+ void main() {
1347
+ #ifdef DEBUG_SHOW_SHADOW_MAP
1348
+ outputColor = getCascadedShadowMaps(vUv);
1349
+ outputDepthVelocity = vec3(0.0);
1350
+ #ifdef SHADOW_LENGTH
1351
+ outputShadowLength = 0.0;
1352
+ #endif // SHADOW_LENGTH
1353
+ return;
1354
+ #endif // DEBUG_SHOW_SHADOW_MAP
1355
+
1356
+ vec3 cameraPosition = vCameraPosition - vEllipsoidCenter;
1357
+ vec3 rayDirection = normalize(vRayDirection);
1358
+ float cosTheta = dot(sunDirection, rayDirection);
1359
+
1360
+ IntersectionResult intersections = getIntersections(cameraPosition, rayDirection);
1361
+ vec2 rayNearFar = getRayNearFar(intersections);
1362
+ #ifdef SHADOW_LENGTH
1363
+ vec2 shadowRayNearFar = getShadowRayNearFar(intersections);
1364
+ #endif // SHADOW_LENGTH
1365
+ #ifdef HAZE
1366
+ vec2 hazeRayNearFar = getHazeRayNearFar(intersections);
1367
+ #endif // HAZE
1368
+
1369
+ float rayDistanceToScene = getRayDistanceToScene(rayDirection);
1370
+ if (rayDistanceToScene >= 0.0) {
1371
+ rayNearFar.y = min(rayNearFar.y, rayDistanceToScene);
1372
+ #ifdef SHADOW_LENGTH
1373
+ shadowRayNearFar.y = min(shadowRayNearFar.y, rayDistanceToScene);
1374
+ #endif // SHADOW_LENGTH
1375
+ #ifdef HAZE
1376
+ hazeRayNearFar.y = min(hazeRayNearFar.y, rayDistanceToScene);
1377
+ #endif // HAZE
1378
+ }
1379
+
1380
+ bool intersectsGround = any(lessThan(rayNearFar, vec2(0.0)));
1381
+ bool intersectsScene = rayNearFar.y < rayNearFar.x;
1382
+
1383
+ float stbn = getSTBN();
1384
+
1385
+ vec4 color = vec4(0.0);
1386
+ float frontDepth = rayNearFar.y;
1387
+ vec3 depthVelocity = vec3(0.0);
1388
+ float shadowLength = 0.0;
1389
+
1390
+ if (!intersectsGround && !intersectsScene) {
1391
+ vec3 rayOrigin = rayNearFar.x * rayDirection + cameraPosition;
1392
+
1393
+ vec2 globeUv = getGlobeUv(rayOrigin);
1394
+ #ifdef DEBUG_SHOW_UV
1395
+ outputColor = vec4(vec3(checker(globeUv, localWeatherRepeat + localWeatherOffset)), 1.0);
1396
+ outputDepthVelocity = vec3(0.0);
1397
+ #ifdef SHADOW_LENGTH
1398
+ outputShadowLength = 0.0;
1399
+ #endif // SHADOW_LENGTH
1400
+ return;
1401
+ #endif // DEBUG_SHOW_UV
1402
+
1403
+ float mipLevel = getMipLevel(globeUv * localWeatherRepeat) * mipLevelScale;
1404
+ mipLevel = mix(0.0, mipLevel, min(1.0, 0.2 * cameraHeight / maxHeight));
1405
+
1406
+ float marchedFrontDepth;
1407
+ ivec3 sampleCount = ivec3(0);
1408
+ color = marchClouds(
1409
+ rayOrigin,
1410
+ rayDirection,
1411
+ rayNearFar,
1412
+ cosTheta,
1413
+ stbn,
1414
+ pow(2.0, mipLevel),
1415
+ marchedFrontDepth,
1416
+ sampleCount
1417
+ );
1418
+
1419
+ #ifdef DEBUG_SHOW_SAMPLE_COUNT
1420
+ outputColor = vec4(vec3(sampleCount) / vec3(500.0, 5.0, 5.0), 1.0);
1421
+ outputDepthVelocity = vec3(0.0);
1422
+ #ifdef SHADOW_LENGTH
1423
+ outputShadowLength = 0.0;
1424
+ #endif // SHADOW_LENGTH
1425
+ return;
1426
+ #endif // DEBUG_SHOW_SAMPLE_COUNT
1427
+
1428
+ // Front depth will be -1.0 when no samples are accumulated.
1429
+ if (marchedFrontDepth >= 0.0) {
1430
+ frontDepth = rayNearFar.x + marchedFrontDepth;
1431
+
1432
+ #ifdef SHADOW_LENGTH
1433
+ // Clamp the shadow length ray at the clouds.
1434
+ shadowRayNearFar.y = mix(
1435
+ shadowRayNearFar.y,
1436
+ min(frontDepth, shadowRayNearFar.y),
1437
+ color.a // Interpolate by the alpha for smoother edges.
1438
+ );
1439
+ #endif // SHADOW_LENGTH
1440
+
1441
+ #ifdef HAZE
1442
+ // Clamp the haze ray at the clouds.
1443
+ hazeRayNearFar.y = mix(
1444
+ hazeRayNearFar.y,
1445
+ min(frontDepth, hazeRayNearFar.y),
1446
+ color.a // Interpolate by the alpha for smoother edges.
1447
+ );
1448
+ #endif // HAZE
1449
+ }
1450
+
1451
+ #ifdef SHADOW_LENGTH
1452
+ if (all(greaterThanEqual(shadowRayNearFar, vec2(0.0)))) {
1453
+ shadowLength = marchShadowLength(
1454
+ shadowRayNearFar.x * rayDirection + cameraPosition,
1455
+ rayDirection,
1456
+ shadowRayNearFar,
1457
+ stbn
1458
+ );
1459
+ }
1460
+ #endif // SHADOW_LENGTH
1461
+
1462
+ // Apply aerial perspective.
1463
+ vec3 frontPosition = cameraPosition + frontDepth * rayDirection;
1464
+ applyAerialPerspective(cameraPosition, frontPosition, shadowLength, color);
1465
+
1466
+ // Velocity for temporal resolution.
1467
+ vec3 frontPositionWorld = ECEFToWorld(frontPosition);
1468
+ vec4 prevClip = reprojectionMatrix * vec4(frontPositionWorld, 1.0);
1469
+ prevClip /= prevClip.w;
1470
+ vec2 prevUv = prevClip.xy * 0.5 + 0.5;
1471
+ vec2 velocity = (vUv - prevUv) * resolution;
1472
+ depthVelocity = vec3(frontDepth, velocity);
1473
+
1474
+ } else {
1475
+ #ifdef SHADOW_LENGTH
1476
+ if (all(greaterThanEqual(shadowRayNearFar, vec2(0.0)))) {
1477
+ shadowLength = marchShadowLength(
1478
+ shadowRayNearFar.x * rayDirection + cameraPosition,
1479
+ rayDirection,
1480
+ shadowRayNearFar,
1481
+ stbn
1482
+ );
1483
+ }
1484
+ #endif // SHADOW_LENGTH
1485
+
1486
+ // TODO: We can calculate velocity to reduce occlusion errors at the edges,
1487
+ // but suffers from floating-point precision errors on near objects.
1488
+
1489
+ // if (intersectsScene) {
1490
+ // vec3 frontPosition = cameraPosition + rayNearFar.y * rayDirection;
1491
+ // vec3 frontPositionWorld = ECEFToWorld(frontPosition);
1492
+ // vec4 prevClip = reprojectionMatrix * vec4(frontPositionWorld, 1.0);
1493
+ // prevClip /= prevClip.w;
1494
+ // vec2 prevUv = prevClip.xy * 0.5 + 0.5;
1495
+ // vec2 velocity = (vUv - prevUv) * resolution;
1496
+ // depthVelocity = vec3(rayNearFar.y, velocity);
1497
+ // }
1498
+
1499
+ }
1500
+
1501
+ #ifdef DEBUG_SHOW_FRONT_DEPTH
1502
+ outputColor = vec4(turbo(frontDepth / maxRayDistance), 1.0);
1503
+ outputDepthVelocity = vec3(0.0);
1504
+ #ifdef SHADOW_LENGTH
1505
+ outputShadowLength = 0.0;
1506
+ #endif // SHADOW_LENGTH
1507
+ return;
1508
+ #endif // DEBUG_SHOW_FRONT_DEPTH
1509
+
1510
+ #ifdef HAZE
1511
+ vec4 haze = approximateHaze(
1512
+ cameraNear * rayDirection + cameraPosition,
1513
+ rayDirection,
1514
+ hazeRayNearFar.y - hazeRayNearFar.x,
1515
+ cosTheta,
1516
+ shadowLength
1517
+ );
1518
+ color = color * (1.0 - haze.a) + haze;
1519
+ #endif // HAZE
1520
+
1521
+ outputColor = color;
1522
+ outputDepthVelocity = depthVelocity;
1523
+ #ifdef SHADOW_LENGTH
1524
+ outputShadowLength = shadowLength * METER_TO_LENGTH_UNIT;
1525
+ #endif // SHADOW_LENGTH
1526
+ }
1527
+ `, Oe = `float getSTBN() {
1528
+ ivec3 size = textureSize(stbnTexture, 0);
1529
+ vec3 scale = 1.0 / vec3(size);
1530
+ return texture(stbnTexture, vec3(gl_FragCoord.xy, float(frame % size.z)) * scale).r;
1531
+ }
1532
+
1533
+ // Straightforward spherical mapping
1534
+ vec2 getSphericalUv(const vec3 position) {
1535
+ vec2 st = normalize(position.yx);
1536
+ float phi = atan(st.x, st.y);
1537
+ float theta = asin(normalize(position).z);
1538
+ return vec2(phi * RECIPROCAL_PI2 + 0.5, theta * RECIPROCAL_PI + 0.5);
1539
+ }
1540
+
1541
+ vec2 getCubeSphereUv(const vec3 position) {
1542
+ // Cube-sphere relaxation by: http://mathproofs.blogspot.com/2005/07/mapping-cube-to-sphere.html
1543
+ // TODO: Tile and fix seams.
1544
+ // Possible improvements:
1545
+ // https://iquilezles.org/articles/texturerepetition/
1546
+ // https://gamedev.stackexchange.com/questions/184388/fragment-shader-map-dot-texture-repeatedly-over-the-sphere
1547
+ // https://github.com/mmikk/hextile-demo
1548
+
1549
+ vec3 n = normalize(position);
1550
+ vec3 f = abs(n);
1551
+ vec3 c = n / max(f.x, max(f.y, f.z));
1552
+ vec2 m;
1553
+ if (all(greaterThan(f.yy, f.xz))) {
1554
+ m = c.y > 0.0 ? vec2(-n.x, n.z) : n.xz;
1555
+ } else if (all(greaterThan(f.xx, f.yz))) {
1556
+ m = c.x > 0.0 ? n.yz : vec2(-n.y, n.z);
1557
+ } else {
1558
+ m = c.z > 0.0 ? n.xy : vec2(n.x, -n.y);
1559
+ }
1560
+
1561
+ vec2 m2 = m * m;
1562
+ float q = dot(m2.xy, vec2(-2.0, 2.0)) - 3.0;
1563
+ float q2 = q * q;
1564
+ vec2 uv;
1565
+ uv.x = sqrt(1.5 + m2.x - m2.y - 0.5 * sqrt(-24.0 * m2.x + q2)) * (m.x > 0.0 ? 1.0 : -1.0);
1566
+ uv.y = sqrt(6.0 / (3.0 - uv.x * uv.x)) * m.y;
1567
+ return uv * 0.5 + 0.5;
1568
+ }
1569
+
1570
+ vec2 getGlobeUv(const vec3 position) {
1571
+ return getCubeSphereUv(position);
1572
+ }
1573
+
1574
+ float getMipLevel(const vec2 uv) {
1575
+ const float mipLevelScale = 0.1;
1576
+ vec2 coord = uv * resolution;
1577
+ vec2 ddx = dFdx(coord);
1578
+ vec2 ddy = dFdy(coord);
1579
+ float deltaMaxSqr = max(dot(ddx, ddx), dot(ddy, ddy)) * mipLevelScale;
1580
+ return max(0.0, 0.5 * log2(max(1.0, deltaMaxSqr)));
1581
+ }
1582
+
1583
+ bool insideLayerIntervals(const float height) {
1584
+ bvec3 gt = greaterThan(vec3(height), minIntervalHeights);
1585
+ bvec3 lt = lessThan(vec3(height), maxIntervalHeights);
1586
+ return any(bvec3(gt.x && lt.x, gt.y && lt.y, gt.z && lt.z));
1587
+ }
1588
+
1589
+ struct WeatherSample {
1590
+ vec4 heightFraction; // Normalized height of each layer
1591
+ vec4 density;
1592
+ };
1593
+
1594
+ vec4 shapeAlteringFunction(const vec4 heightFraction, const vec4 bias) {
1595
+ // Apply a semi-circle transform to round the clouds towards the top.
1596
+ vec4 biased = pow(heightFraction, bias);
1597
+ vec4 x = clamp(biased * 2.0 - 1.0, -1.0, 1.0);
1598
+ return 1.0 - x * x;
1599
+ }
1600
+
1601
+ WeatherSample sampleWeather(const vec2 uv, const float height, const float mipLevel) {
1602
+ WeatherSample weather;
1603
+ weather.heightFraction = remapClamped(vec4(height), minLayerHeights, maxLayerHeights);
1604
+
1605
+ vec4 localWeather = pow(
1606
+ textureLod(
1607
+ localWeatherTexture,
1608
+ uv * localWeatherRepeat + localWeatherOffset,
1609
+ mipLevel
1610
+ ).LOCAL_WEATHER_CHANNELS,
1611
+ weatherExponents
1612
+ );
1613
+ #ifdef SHADOW
1614
+ localWeather *= shadowLayerMask;
1615
+ #endif // SHADOW
1616
+
1617
+ vec4 heightScale = shapeAlteringFunction(weather.heightFraction, shapeAlteringBiases);
1618
+
1619
+ // Modulation to control weather by coverage parameter.
1620
+ // Reference: https://github.com/Prograda/Skybolt/blob/master/Assets/Core/Shaders/Clouds.h#L63
1621
+ vec4 factor = 1.0 - coverage * heightScale;
1622
+ weather.density = remapClamped(
1623
+ mix(localWeather, vec4(1.0), coverageFilterWidths),
1624
+ factor,
1625
+ factor + coverageFilterWidths
1626
+ );
1627
+
1628
+ return weather;
1629
+ }
1630
+
1631
+ vec4 getLayerDensity(const vec4 heightFraction) {
1632
+ // prettier-ignore
1633
+ return densityProfile.expTerms * exp(densityProfile.exponents * heightFraction) +
1634
+ densityProfile.linearTerms * heightFraction +
1635
+ densityProfile.constantTerms;
1636
+ }
1637
+
1638
+ struct MediaSample {
1639
+ float density;
1640
+ vec4 weight;
1641
+ float scattering;
1642
+ float extinction;
1643
+ };
1644
+
1645
+ MediaSample sampleMedia(
1646
+ const WeatherSample weather,
1647
+ const vec3 position,
1648
+ const vec2 uv,
1649
+ const float mipLevel,
1650
+ const float jitter,
1651
+ out ivec3 sampleCount
1652
+ ) {
1653
+ vec4 density = weather.density;
1654
+
1655
+ // TODO: Define in physical length.
1656
+ vec3 surfaceNormal = normalize(position);
1657
+ float localWeatherSpeed = length(localWeatherOffset);
1658
+ vec3 evolution = -surfaceNormal * localWeatherSpeed * 2e4;
1659
+
1660
+ vec3 turbulence = vec3(0.0);
1661
+ #ifdef TURBULENCE
1662
+ vec2 turbulenceUv = uv * localWeatherRepeat * turbulenceRepeat;
1663
+ turbulence =
1664
+ turbulenceDisplacement *
1665
+ (texture(turbulenceTexture, turbulenceUv).rgb * 2.0 - 1.0) *
1666
+ dot(density, remapClamped(weather.heightFraction, vec4(0.3), vec4(0.0)));
1667
+ #endif // TURBULENCE
1668
+
1669
+ vec3 shapePosition = (position + evolution + turbulence) * shapeRepeat + shapeOffset;
1670
+ float shape = texture(shapeTexture, shapePosition).r;
1671
+ density = remapClamped(density, vec4(1.0 - shape) * shapeAmounts, vec4(1.0));
1672
+
1673
+ #ifdef DEBUG_SHOW_SAMPLE_COUNT
1674
+ ++sampleCount.y;
1675
+ #endif // DEBUG_SHOW_SAMPLE_COUNT
1676
+
1677
+ #ifdef SHAPE_DETAIL
1678
+ if (mipLevel * 0.5 + (jitter - 0.5) * 0.5 < 0.5) {
1679
+ vec3 detailPosition = (position + turbulence) * shapeDetailRepeat + shapeDetailOffset;
1680
+ float detail = texture(shapeDetailTexture, detailPosition).r;
1681
+ // Fluffy at the top and whippy at the bottom.
1682
+ vec4 modifier = mix(
1683
+ vec4(pow(detail, 6.0)),
1684
+ vec4(1.0 - detail),
1685
+ remapClamped(weather.heightFraction, vec4(0.2), vec4(0.4))
1686
+ );
1687
+ modifier = mix(vec4(0.0), modifier, shapeDetailAmounts);
1688
+ density = remapClamped(density * 2.0, vec4(modifier * 0.5), vec4(1.0));
1689
+
1690
+ #ifdef DEBUG_SHOW_SAMPLE_COUNT
1691
+ ++sampleCount.z;
1692
+ #endif // DEBUG_SHOW_SAMPLE_COUNT
1693
+ }
1694
+ #endif // SHAPE_DETAIL
1695
+
1696
+ // Nicely decrease the density at the bottom.
1697
+ density = saturate(density * densityScales * getLayerDensity(weather.heightFraction));
1698
+
1699
+ MediaSample media;
1700
+ float densitySum = density.x + density.y + density.z + density.w;
1701
+ media.weight = density / densitySum;
1702
+ media.scattering = densitySum * scatteringCoefficient;
1703
+ media.extinction = densitySum * absorptionCoefficient + media.scattering;
1704
+ return media;
1705
+ }
1706
+
1707
+ MediaSample sampleMedia(
1708
+ const WeatherSample weather,
1709
+ const vec3 position,
1710
+ const vec2 uv,
1711
+ const float mipLevel,
1712
+ const float jitter
1713
+ ) {
1714
+ ivec3 sampleCount;
1715
+ return sampleMedia(weather, position, uv, mipLevel, jitter, sampleCount);
1716
+ }
1717
+ `, ft = `precision highp float;
1718
+ precision highp sampler3D;
1719
+
1720
+ #include "atmosphere/parameters"
1721
+ #include "atmosphere/functions"
1722
+ #include "types"
1723
+
1724
+ uniform mat4 inverseProjectionMatrix;
1725
+ uniform mat4 inverseViewMatrix;
1726
+ uniform vec3 cameraPosition;
1727
+ uniform vec3 ellipsoidCenter;
1728
+ uniform mat4 inverseEllipsoidMatrix;
1729
+ uniform vec3 altitudeCorrection;
1730
+
1731
+ // Atmosphere
1732
+ uniform float bottomRadius;
1733
+ uniform vec3 sunDirection;
1734
+
1735
+ // Cloud layers
1736
+ uniform float minHeight;
1737
+ uniform float maxHeight;
1738
+
1739
+ layout(location = 0) in vec3 position;
1740
+
1741
+ out vec2 vUv;
1742
+ out vec3 vCameraPosition;
1743
+ out vec3 vCameraDirection; // Direction to the center of screen
1744
+ out vec3 vRayDirection; // Direction to the texel
1745
+ out vec3 vEllipsoidCenter;
1746
+
1747
+ out GroundIrradiance vGroundIrradiance;
1748
+ out CloudsIrradiance vCloudsIrradiance;
1749
+
1750
+ void sampleSunSkyIrradiance(const vec3 positionECEF) {
1751
+ vGroundIrradiance.sun = GetSunAndSkyIrradiance(
1752
+ positionECEF * METER_TO_LENGTH_UNIT,
1753
+ sunDirection,
1754
+ vGroundIrradiance.sky
1755
+ );
1756
+
1757
+ vec3 surfaceNormal = normalize(positionECEF);
1758
+ vec2 radii = (bottomRadius + vec2(minHeight, maxHeight)) * METER_TO_LENGTH_UNIT;
1759
+ vCloudsIrradiance.minSun = GetSunAndSkyIrradiance(
1760
+ surfaceNormal * radii.x,
1761
+ sunDirection,
1762
+ vCloudsIrradiance.minSky
1763
+ );
1764
+ vCloudsIrradiance.maxSun = GetSunAndSkyIrradiance(
1765
+ surfaceNormal * radii.y,
1766
+ sunDirection,
1767
+ vCloudsIrradiance.maxSky
1768
+ );
1769
+ }
1770
+
1771
+ void main() {
1772
+ vUv = position.xy * 0.5 + 0.5;
1773
+
1774
+ vec4 viewPosition = inverseProjectionMatrix * vec4(position, 1.0);
1775
+ vec4 worldDirection = inverseViewMatrix * vec4(viewPosition.xyz, 0.0);
1776
+ mat3 rotation = mat3(inverseEllipsoidMatrix);
1777
+ vCameraPosition = rotation * cameraPosition;
1778
+ vCameraDirection = rotation * normalize((inverseViewMatrix * vec4(0.0, 0.0, -1.0, 0.0)).xyz);
1779
+ vRayDirection = rotation * worldDirection.xyz;
1780
+ vEllipsoidCenter = ellipsoidCenter + altitudeCorrection;
1781
+
1782
+ sampleSunSkyIrradiance(vCameraPosition - vEllipsoidCenter);
1783
+
1784
+ gl_Position = vec4(position.xy, 1.0, 1.0);
1785
+ }
1786
+ `, Re = `uniform vec2 resolution;
1787
+ uniform int frame;
1788
+ uniform sampler3D stbnTexture;
1789
+
1790
+ // Atmosphere
1791
+ uniform float bottomRadius;
1792
+ uniform mat4 ellipsoidMatrix;
1793
+ uniform mat4 inverseEllipsoidMatrix;
1794
+ uniform vec3 sunDirection;
1795
+
1796
+ // Participating medium
1797
+ uniform float scatteringCoefficient;
1798
+ uniform float absorptionCoefficient;
1799
+ uniform vec3 albedo;
1800
+
1801
+ // Primary raymarch
1802
+ uniform float minDensity;
1803
+ uniform float minExtinction;
1804
+ uniform float minTransmittance;
1805
+
1806
+ // Shape and weather
1807
+ uniform sampler2D localWeatherTexture;
1808
+ uniform vec2 localWeatherRepeat;
1809
+ uniform vec2 localWeatherOffset;
1810
+ uniform float coverage;
1811
+ uniform sampler3D shapeTexture;
1812
+ uniform vec3 shapeRepeat;
1813
+ uniform vec3 shapeOffset;
1814
+
1815
+ #ifdef SHAPE_DETAIL
1816
+ uniform sampler3D shapeDetailTexture;
1817
+ uniform vec3 shapeDetailRepeat;
1818
+ uniform vec3 shapeDetailOffset;
1819
+ #endif // SHAPE_DETAIL
1820
+
1821
+ #ifdef TURBULENCE
1822
+ uniform sampler2D turbulenceTexture;
1823
+ uniform vec2 turbulenceRepeat;
1824
+ uniform float turbulenceDisplacement;
1825
+ #endif // TURBULENCE
1826
+
1827
+ // Haze
1828
+ #ifdef HAZE
1829
+ uniform float hazeDensityScale;
1830
+ uniform float hazeExponent;
1831
+ #endif // HAZE
1832
+
1833
+ // Cloud layers
1834
+ uniform vec4 minLayerHeights;
1835
+ uniform vec4 maxLayerHeights;
1836
+ uniform vec3 minIntervalHeights;
1837
+ uniform vec3 maxIntervalHeights;
1838
+ uniform vec4 densityScales;
1839
+ uniform vec4 shapeAmounts;
1840
+ uniform vec4 shapeDetailAmounts;
1841
+ uniform vec4 weatherExponents;
1842
+ uniform vec4 shapeAlteringBiases;
1843
+ uniform vec4 coverageFilterWidths;
1844
+ uniform float minHeight;
1845
+ uniform float maxHeight;
1846
+ uniform float shadowTopHeight;
1847
+ uniform float shadowBottomHeight;
1848
+ uniform vec4 shadowLayerMask;
1849
+ uniform DensityProfile densityProfile;
1850
+ `, $ = `struct GroundIrradiance {
1851
+ vec3 sun;
1852
+ vec3 sky;
1853
+ };
1854
+
1855
+ struct CloudsIrradiance {
1856
+ vec3 minSun;
1857
+ vec3 minSky;
1858
+ vec3 maxSun;
1859
+ vec3 maxSky;
1860
+ };
1861
+
1862
+ struct DensityProfile {
1863
+ vec4 expTerms;
1864
+ vec4 exponents;
1865
+ vec4 linearTerms;
1866
+ vec4 constantTerms;
1867
+ };
1868
+ `;
1869
+ var mt = Object.defineProperty, w = (o, e, t, n) => {
1870
+ for (var a = void 0, i = o.length - 1, s; i >= 0; i--)
1871
+ (s = o[i]) && (a = s(e, t, a) || a);
1872
+ return a && mt(e, t, a), a;
1873
+ };
1874
+ const vt = /* @__PURE__ */ new f(), gt = /* @__PURE__ */ new qe();
1875
+ class y extends je {
1876
+ constructor({
1877
+ parameterUniforms: e,
1878
+ layerUniforms: t,
1879
+ atmosphereUniforms: n
1880
+ }, a = Ce.DEFAULT) {
1881
+ super(
1882
+ {
1883
+ name: "CloudsMaterial",
1884
+ glslVersion: W,
1885
+ vertexShader: M(ft, {
1886
+ atmosphere: {
1887
+ parameters: pe,
1888
+ functions: de
1889
+ },
1890
+ types: $
1891
+ }),
1892
+ fragmentShader: G(
1893
+ M(pt, {
1894
+ core: {
1895
+ depth: Qe,
1896
+ math: _e,
1897
+ turbo: Ae,
1898
+ generators: Je,
1899
+ raySphereIntersection: Ee,
1900
+ cascadedShadowMaps: Xe,
1901
+ interleavedGradientNoise: $e,
1902
+ vogelDisk: Ke
1903
+ },
1904
+ atmosphere: {
1905
+ parameters: pe,
1906
+ functions: de
1907
+ },
1908
+ types: $,
1909
+ parameters: Re,
1910
+ clouds: Oe
1911
+ })
1912
+ ),
1913
+ // prettier-ignore
1914
+ uniforms: {
1915
+ ...e,
1916
+ ...t,
1917
+ ...n,
1918
+ depthBuffer: new r(null),
1919
+ viewMatrix: new r(new v()),
1920
+ inverseProjectionMatrix: new r(new v()),
1921
+ inverseViewMatrix: new r(new v()),
1922
+ reprojectionMatrix: new r(new v()),
1923
+ resolution: new r(new m()),
1924
+ cameraNear: new r(0),
1925
+ cameraFar: new r(0),
1926
+ cameraHeight: new r(0),
1927
+ frame: new r(0),
1928
+ temporalJitter: new r(new m()),
1929
+ targetUvScale: new r(new m()),
1930
+ mipLevelScale: new r(1),
1931
+ stbnTexture: new r(null),
1932
+ // Scattering
1933
+ albedo: new r(new f()),
1934
+ skyIrradianceScale: new r(2.5),
1935
+ groundIrradianceScale: new r(3),
1936
+ powderScale: new r(0.8),
1937
+ powderExponent: new r(150),
1938
+ // Primary raymarch
1939
+ maxIterationCount: new r(l.clouds.maxIterationCount),
1940
+ minStepSize: new r(l.clouds.minStepSize),
1941
+ maxStepSize: new r(l.clouds.maxStepSize),
1942
+ maxRayDistance: new r(l.clouds.maxRayDistance),
1943
+ perspectiveStepScale: new r(l.clouds.perspectiveStepScale),
1944
+ minDensity: new r(l.clouds.minDensity),
1945
+ minExtinction: new r(l.clouds.minExtinction),
1946
+ minTransmittance: new r(l.clouds.minTransmittance),
1947
+ // Secondary raymarch
1948
+ maxIterationCountToSun: new r(l.clouds.maxIterationCountToSun),
1949
+ maxIterationCountToGround: new r(l.clouds.maxIterationCountToGround),
1950
+ minSecondaryStepSize: new r(l.clouds.minSecondaryStepSize),
1951
+ secondaryStepScale: new r(l.clouds.secondaryStepScale),
1952
+ // Beer shadow map
1953
+ shadowBuffer: new r(null),
1954
+ shadowTexelSize: new r(new m()),
1955
+ shadowIntervals: new r(
1956
+ Array.from({ length: 4 }, () => new m())
1957
+ // Populate the max number of elements
1958
+ ),
1959
+ shadowMatrices: new r(
1960
+ Array.from({ length: 4 }, () => new v())
1961
+ // Populate the max number of elements
1962
+ ),
1963
+ shadowFar: new r(0),
1964
+ maxShadowFilterRadius: new r(6),
1965
+ shadowLayerMask: new r(new x().setScalar(1)),
1966
+ // Disable mask
1967
+ // Shadow length
1968
+ maxShadowLengthIterationCount: new r(l.clouds.maxShadowLengthIterationCount),
1969
+ minShadowLengthStepSize: new r(l.clouds.minShadowLengthStepSize),
1970
+ maxShadowLengthRayDistance: new r(l.clouds.maxShadowLengthRayDistance),
1971
+ // Haze
1972
+ hazeDensityScale: new r(3e-5),
1973
+ hazeExponent: new r(1e-3)
1974
+ }
1975
+ },
1976
+ a
1977
+ ), this.temporalUpscale = !0, this.depthPacking = 0, this.localWeatherChannels = "rgba", this.shapeDetail = l.shapeDetail, this.turbulence = l.turbulence, this.shadowLength = l.lightShafts, this.haze = l.haze, this.multiScatteringOctaves = l.clouds.multiScatteringOctaves, this.accurateSunSkyIrradiance = l.clouds.accurateSunSkyIrradiance, this.accuratePhaseFunction = l.clouds.accuratePhaseFunction, this.shadowCascadeCount = l.shadow.cascadeCount, this.shadowSampleCount = 8, this.scatterAnisotropy1 = 0.7, this.scatterAnisotropy2 = -0.2, this.scatterAnisotropyMix = 0.5;
1978
+ }
1979
+ onBeforeRender(e, t, n, a, i, s) {
1980
+ const c = this.uniforms;
1981
+ c.albedo.value.setScalar(
1982
+ c.scatteringCoefficient.value / (c.absorptionCoefficient.value + c.scatteringCoefficient.value)
1983
+ );
1984
+ const u = this.defines.POWDER != null, h = this.uniforms.powderScale.value > 0;
1985
+ h !== u && (h ? this.defines.POWDER = "1" : delete this.defines.POWDER, this.needsUpdate = !0);
1986
+ const d = this.defines.GROUND_IRRADIANCE != null;
1987
+ (this.uniforms.groundIrradianceScale.value > 0 && this.uniforms.maxIterationCountToGround.value > 0) !== d && (h ? this.defines.GROUND_IRRADIANCE = "1" : delete this.defines.GROUND_IRRADIANCE, this.needsUpdate = !0);
1988
+ }
1989
+ copyCameraSettings(e) {
1990
+ e.isPerspectiveCamera === !0 ? this.defines.PERSPECTIVE_CAMERA !== "1" && (this.defines.PERSPECTIVE_CAMERA = "1", this.needsUpdate = !0) : this.defines.PERSPECTIVE_CAMERA != null && (delete this.defines.PERSPECTIVE_CAMERA, this.needsUpdate = !0);
1991
+ const t = this.uniforms;
1992
+ t.viewMatrix.value.copy(e.matrixWorldInverse), t.inverseViewMatrix.value.copy(e.matrixWorld);
1993
+ const n = this.previousProjectionMatrix ?? e.projectionMatrix, a = this.previousViewMatrix ?? e.matrixWorldInverse, i = t.inverseProjectionMatrix.value, s = t.reprojectionMatrix.value;
1994
+ if (this.temporalUpscale) {
1995
+ const h = t.frame.value % 16, d = t.resolution.value, p = Pe[h], g = (p.x - 0.5) / d.x * 4, S = (p.y - 0.5) / d.y * 4;
1996
+ t.temporalJitter.value.set(g, S), t.mipLevelScale.value = 0.25, i.copy(e.projectionMatrix), i.elements[8] += g * 2, i.elements[9] += S * 2, i.invert(), s.copy(n), s.elements[8] += g * 2, s.elements[9] += S * 2, s.multiply(a);
1997
+ } else
1998
+ t.temporalJitter.value.setScalar(0), t.mipLevelScale.value = 1, i.copy(e.projectionMatrixInverse), s.copy(n).multiply(a);
1999
+ Ze(e), t.cameraNear.value = e.near, t.cameraFar.value = e.far;
2000
+ const c = e.getWorldPosition(
2001
+ t.cameraPosition.value
2002
+ ), u = vt.copy(c).applyMatrix4(t.inverseEllipsoidMatrix.value).sub(t.ellipsoidCenter.value);
2003
+ try {
2004
+ t.cameraHeight.value = gt.setFromECEF(u).height;
2005
+ } catch {
2006
+ }
2007
+ }
2008
+ // copyCameraSettings can be called multiple times within a frame. Only
2009
+ // reliable way is to explicitly store the matrices.
2010
+ copyReprojectionMatrix(e) {
2011
+ this.previousProjectionMatrix ?? (this.previousProjectionMatrix = new v()), this.previousViewMatrix ?? (this.previousViewMatrix = new v()), this.previousProjectionMatrix.copy(e.projectionMatrix), this.previousViewMatrix.copy(e.matrixWorldInverse);
2012
+ }
2013
+ setSize(e, t, n, a) {
2014
+ this.uniforms.resolution.value.set(e, t), n != null && a != null ? this.uniforms.targetUvScale.value.set(
2015
+ e / n,
2016
+ t / a
2017
+ ) : this.uniforms.targetUvScale.value.setScalar(1), this.previousProjectionMatrix = void 0, this.previousViewMatrix = void 0;
2018
+ }
2019
+ setShadowSize(e, t) {
2020
+ this.uniforms.shadowTexelSize.value.set(1 / e, 1 / t);
2021
+ }
2022
+ get depthBuffer() {
2023
+ return this.uniforms.depthBuffer.value;
2024
+ }
2025
+ set depthBuffer(e) {
2026
+ this.uniforms.depthBuffer.value = e;
2027
+ }
2028
+ }
2029
+ w([
2030
+ O("DEPTH_PACKING")
2031
+ ], y.prototype, "depthPacking");
2032
+ w([
2033
+ De("LOCAL_WEATHER_CHANNELS", {
2034
+ validate: (o) => /^[rgba]{4}$/.test(o)
2035
+ })
2036
+ ], y.prototype, "localWeatherChannels");
2037
+ w([
2038
+ C("SHAPE_DETAIL")
2039
+ ], y.prototype, "shapeDetail");
2040
+ w([
2041
+ C("TURBULENCE")
2042
+ ], y.prototype, "turbulence");
2043
+ w([
2044
+ C("SHADOW_LENGTH")
2045
+ ], y.prototype, "shadowLength");
2046
+ w([
2047
+ C("HAZE")
2048
+ ], y.prototype, "haze");
2049
+ w([
2050
+ O("MULTI_SCATTERING_OCTAVES", { min: 1, max: 12 })
2051
+ ], y.prototype, "multiScatteringOctaves");
2052
+ w([
2053
+ C("ACCURATE_SUN_SKY_IRRADIANCE")
2054
+ ], y.prototype, "accurateSunSkyIrradiance");
2055
+ w([
2056
+ C("ACCURATE_PHASE_FUNCTION")
2057
+ ], y.prototype, "accuratePhaseFunction");
2058
+ w([
2059
+ O("SHADOW_CASCADE_COUNT", { min: 1, max: 4 })
2060
+ ], y.prototype, "shadowCascadeCount");
2061
+ w([
2062
+ O("SHADOW_SAMPLE_COUNT", { min: 1, max: 16 })
2063
+ ], y.prototype, "shadowSampleCount");
2064
+ w([
2065
+ J("SCATTER_ANISOTROPY_1")
2066
+ ], y.prototype, "scatterAnisotropy1");
2067
+ w([
2068
+ J("SCATTER_ANISOTROPY_2")
2069
+ ], y.prototype, "scatterAnisotropy2");
2070
+ w([
2071
+ J("SCATTER_ANISOTROPY_MIX")
2072
+ ], y.prototype, "scatterAnisotropyMix");
2073
+ const St = `// Taken from https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1
2074
+ // TODO: Use 5-taps version: https://www.shadertoy.com/view/MtVGWz
2075
+ // Or even 4 taps (requires preprocessing in the input buffer):
2076
+ // https://www.shadertoy.com/view/4tyGDD
2077
+
2078
+ /**
2079
+ * MIT License
2080
+ *
2081
+ * Copyright (c) 2019 MJP
2082
+ *
2083
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
2084
+ * of this software and associated documentation files (the "Software"), to deal
2085
+ * in the Software without restriction, including without limitation the rights
2086
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2087
+ * copies of the Software, and to permit persons to whom the Software is
2088
+ * furnished to do so, subject to the following conditions:
2089
+ *
2090
+ * The above copyright notice and this permission notice shall be included in all
2091
+ * copies or substantial portions of the Software.
2092
+ *
2093
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2094
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2095
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2096
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2097
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2098
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2099
+ * SOFTWARE.
2100
+ */
2101
+
2102
+ vec4 textureCatmullRom(sampler2D tex, vec2 uv) {
2103
+ vec2 texSize = vec2(textureSize(tex, 0));
2104
+
2105
+ // We're going to sample a a 4x4 grid of texels surrounding the target UV
2106
+ // coordinate. We'll do this by rounding down the sample location to get the
2107
+ // exact center of our "starting" texel. The starting texel will be at
2108
+ // location [1, 1] in the grid, where [0, 0] is the top left corner.
2109
+ vec2 samplePos = uv * texSize;
2110
+ vec2 texPos1 = floor(samplePos - 0.5) + 0.5;
2111
+
2112
+ // Compute the fractional offset from our starting texel to our original
2113
+ // sample location, which we'll feed into the Catmull-Rom spline function to
2114
+ // get our filter weights.
2115
+ vec2 f = samplePos - texPos1;
2116
+
2117
+ // Compute the Catmull-Rom weights using the fractional offset that we
2118
+ // calculated earlier. These equations are pre-expanded based on our knowledge
2119
+ // of where the texels will be located, which lets us avoid having to evaluate
2120
+ // a piece-wise function.
2121
+ vec2 w0 = f * (-0.5 + f * (1.0 - 0.5 * f));
2122
+ vec2 w1 = 1.0 + f * f * (-2.5 + 1.5 * f);
2123
+ vec2 w2 = f * (0.5 + f * (2.0 - 1.5 * f));
2124
+ vec2 w3 = f * f * (-0.5 + 0.5 * f);
2125
+
2126
+ // Work out weighting factors and sampling offsets that will let us use
2127
+ // bilinear filtering to simultaneously evaluate the middle 2 samples from the
2128
+ // 4x4 grid.
2129
+ vec2 w12 = w1 + w2;
2130
+ vec2 offset12 = w2 / (w1 + w2);
2131
+
2132
+ // Compute the final UV coordinates we'll use for sampling the texture
2133
+ vec2 texPos0 = texPos1 - 1.0;
2134
+ vec2 texPos3 = texPos1 + 2.0;
2135
+ vec2 texPos12 = texPos1 + offset12;
2136
+
2137
+ texPos0 /= texSize;
2138
+ texPos3 /= texSize;
2139
+ texPos12 /= texSize;
2140
+
2141
+ vec4 result = vec4(0.0);
2142
+ result += texture(tex, vec2(texPos0.x, texPos0.y)) * w0.x * w0.y;
2143
+ result += texture(tex, vec2(texPos12.x, texPos0.y)) * w12.x * w0.y;
2144
+ result += texture(tex, vec2(texPos3.x, texPos0.y)) * w3.x * w0.y;
2145
+
2146
+ result += texture(tex, vec2(texPos0.x, texPos12.y)) * w0.x * w12.y;
2147
+ result += texture(tex, vec2(texPos12.x, texPos12.y)) * w12.x * w12.y;
2148
+ result += texture(tex, vec2(texPos3.x, texPos12.y)) * w3.x * w12.y;
2149
+
2150
+ result += texture(tex, vec2(texPos0.x, texPos3.y)) * w0.x * w3.y;
2151
+ result += texture(tex, vec2(texPos12.x, texPos3.y)) * w12.x * w3.y;
2152
+ result += texture(tex, vec2(texPos3.x, texPos3.y)) * w3.x * w3.y;
2153
+
2154
+ return result;
2155
+ }
2156
+
2157
+ vec4 textureCatmullRom(sampler2DArray tex, vec3 uv) {
2158
+ vec2 texSize = vec2(textureSize(tex, 0));
2159
+ vec2 samplePos = uv.xy * texSize;
2160
+ vec2 texPos1 = floor(samplePos - 0.5) + 0.5;
2161
+ vec2 f = samplePos - texPos1;
2162
+ vec2 w0 = f * (-0.5 + f * (1.0 - 0.5 * f));
2163
+ vec2 w1 = 1.0 + f * f * (-2.5 + 1.5 * f);
2164
+ vec2 w2 = f * (0.5 + f * (2.0 - 1.5 * f));
2165
+ vec2 w3 = f * f * (-0.5 + 0.5 * f);
2166
+ vec2 w12 = w1 + w2;
2167
+ vec2 offset12 = w2 / (w1 + w2);
2168
+ vec2 texPos0 = texPos1 - 1.0;
2169
+ vec2 texPos3 = texPos1 + 2.0;
2170
+ vec2 texPos12 = texPos1 + offset12;
2171
+ texPos0 /= texSize;
2172
+ texPos3 /= texSize;
2173
+ texPos12 /= texSize;
2174
+ vec4 result = vec4(0.0);
2175
+ result += texture(tex, vec3(texPos0.x, texPos0.y, uv.z)) * w0.x * w0.y;
2176
+ result += texture(tex, vec3(texPos12.x, texPos0.y, uv.z)) * w12.x * w0.y;
2177
+ result += texture(tex, vec3(texPos3.x, texPos0.y, uv.z)) * w3.x * w0.y;
2178
+ result += texture(tex, vec3(texPos0.x, texPos12.y, uv.z)) * w0.x * w12.y;
2179
+ result += texture(tex, vec3(texPos12.x, texPos12.y, uv.z)) * w12.x * w12.y;
2180
+ result += texture(tex, vec3(texPos3.x, texPos12.y, uv.z)) * w3.x * w12.y;
2181
+ result += texture(tex, vec3(texPos0.x, texPos3.y, uv.z)) * w0.x * w3.y;
2182
+ result += texture(tex, vec3(texPos12.x, texPos3.y, uv.z)) * w12.x * w3.y;
2183
+ result += texture(tex, vec3(texPos3.x, texPos3.y, uv.z)) * w3.x * w3.y;
2184
+ return result;
2185
+ }
2186
+ `, yt = `precision highp float;
2187
+ precision highp sampler2DArray;
2188
+
2189
+ #include "core/turbo"
2190
+ #include "catmullRomSampling"
2191
+ #include "varianceClipping"
2192
+
2193
+ uniform sampler2D colorBuffer;
2194
+ uniform sampler2D depthVelocityBuffer;
2195
+ uniform sampler2D colorHistoryBuffer;
2196
+
2197
+ #ifdef SHADOW_LENGTH
2198
+ uniform sampler2D shadowLengthBuffer;
2199
+ uniform sampler2D shadowLengthHistoryBuffer;
2200
+ #endif // SHADOW_LENGTH
2201
+
2202
+ uniform vec2 texelSize;
2203
+ uniform int frame;
2204
+ uniform float varianceGamma;
2205
+ uniform float temporalAlpha;
2206
+ uniform vec2 jitterOffset;
2207
+
2208
+ in vec2 vUv;
2209
+
2210
+ layout(location = 0) out vec4 outputColor;
2211
+ #ifdef SHADOW_LENGTH
2212
+ layout(location = 1) out float outputShadowLength;
2213
+ #endif // SHADOW_LENGTH
2214
+
2215
+ const ivec2 neighborOffsets[9] = ivec2[9](
2216
+ ivec2(-1, -1),
2217
+ ivec2(-1, 0),
2218
+ ivec2(-1, 1),
2219
+ ivec2(0, -1),
2220
+ ivec2(0, 0),
2221
+ ivec2(0, 1),
2222
+ ivec2(1, -1),
2223
+ ivec2(1, 0),
2224
+ ivec2(1, 1)
2225
+ );
2226
+
2227
+ const ivec4[4] bayerIndices = ivec4[4](
2228
+ ivec4(0, 12, 3, 15),
2229
+ ivec4(8, 4, 11, 7),
2230
+ ivec4(2, 14, 1, 13),
2231
+ ivec4(10, 6, 9, 5)
2232
+ );
2233
+
2234
+ vec2 getUnjitteredUv(ivec2 coord) {
2235
+ return (vec2(coord) + 0.5 - jitterOffset) * texelSize;
2236
+ }
2237
+
2238
+ vec4 getClosestFragment(const vec2 uv) {
2239
+ vec4 result = vec4(1e7, 0.0, 0.0, 0.0);
2240
+ vec4 neighbor;
2241
+ #pragma unroll_loop_start
2242
+ for (int i = 0; i < 9; ++i) {
2243
+ neighbor = textureOffset(depthVelocityBuffer, uv, neighborOffsets[i]);
2244
+ if (neighbor.r < result.r) {
2245
+ result = neighbor;
2246
+ }
2247
+ }
2248
+ #pragma unroll_loop_end
2249
+ return result;
2250
+ }
2251
+
2252
+ vec4 getClosestFragment(const ivec2 coord) {
2253
+ vec4 result = vec4(1e7, 0.0, 0.0, 0.0);
2254
+ vec4 neighbor;
2255
+ #pragma unroll_loop_start
2256
+ for (int i = 0; i < 9; ++i) {
2257
+ neighbor = texelFetchOffset(depthVelocityBuffer, coord, 0, neighborOffsets[i]);
2258
+ if (neighbor.r < result.r) {
2259
+ result = neighbor;
2260
+ }
2261
+ }
2262
+ #pragma unroll_loop_end
2263
+ return result;
2264
+ }
2265
+
2266
+ void temporalUpscale(
2267
+ const ivec2 coord,
2268
+ const ivec2 lowResCoord,
2269
+ const bool currentFrame,
2270
+ out vec4 outputColor,
2271
+ out float outputShadowLength
2272
+ ) {
2273
+ #if !defined(DEBUG_SHOW_VELOCITY)
2274
+ if (currentFrame) {
2275
+ // Use the texel just rendered without any accumulation.
2276
+ outputColor = texelFetch(colorBuffer, lowResCoord, 0);
2277
+ #ifdef SHADOW_LENGTH
2278
+ outputShadowLength = texelFetch(shadowLengthBuffer, lowResCoord, 0).r;
2279
+ #endif // SHADOW_LENGTH
2280
+ return;
2281
+ }
2282
+ #endif // !defined(DEBUG_SHOW_VELOCITY)
2283
+
2284
+ vec2 unjitteredUv = getUnjitteredUv(coord);
2285
+ vec4 currentColor = texture(colorBuffer, unjitteredUv);
2286
+ #ifdef SHADOW_LENGTH
2287
+ vec4 currentShadowLength = vec4(texture(shadowLengthBuffer, unjitteredUv).rgb, 1.0);
2288
+ #endif // SHADOW_LENGTH
2289
+
2290
+ vec4 depthVelocity = getClosestFragment(unjitteredUv);
2291
+ vec2 velocity = depthVelocity.gb * texelSize;
2292
+ vec2 prevUv = vUv - velocity;
2293
+ if (prevUv.x < 0.0 || prevUv.x > 1.0 || prevUv.y < 0.0 || prevUv.y > 1.0) {
2294
+ outputColor = currentColor;
2295
+ #ifdef SHADOW_LENGTH
2296
+ outputShadowLength = currentShadowLength.r;
2297
+ #endif // SHADOW_LENGTH
2298
+ return; // Rejection
2299
+ }
2300
+
2301
+ // Variance clipping with a large variance gamma seems to work fine for
2302
+ // upsampling. This increases ghosting, of course, but it's hard to notice on
2303
+ // clouds.
2304
+ // vec4 historyColor = textureCatmullRom(colorHistoryBuffer, prevUv);
2305
+ vec4 historyColor = texture(colorHistoryBuffer, prevUv);
2306
+ vec4 clippedColor = varianceClipping(colorBuffer, vUv, currentColor, historyColor, varianceGamma);
2307
+ outputColor = clippedColor;
2308
+
2309
+ #ifdef DEBUG_SHOW_VELOCITY
2310
+ outputColor.rgb = outputColor.rgb + vec3(abs(velocity), 0.0);
2311
+ #endif // DEBUG_SHOW_VELOCITY
2312
+
2313
+ #ifdef SHADOW_LENGTH
2314
+ // Sampling the shadow length history using scene depth doesn't make much
2315
+ // sense, but it's too hard to derive it properly. At least this approach
2316
+ // resolves the edges of scene objects.
2317
+ // vec4 historyShadowLength = vec4(textureCatmullRom(shadowLengthHistoryBuffer, prevUv).rgb, 1.0);
2318
+ vec4 historyShadowLength = vec4(texture(shadowLengthHistoryBuffer, prevUv).rgb, 1.0);
2319
+ vec4 clippedShadowLength = varianceClipping(
2320
+ shadowLengthBuffer,
2321
+ vUv,
2322
+ currentShadowLength,
2323
+ historyShadowLength,
2324
+ varianceGamma
2325
+ );
2326
+ outputShadowLength = clippedShadowLength.r;
2327
+ #endif // SHADOW_LENGTH
2328
+ }
2329
+
2330
+ void temporalAntialiasing(const ivec2 coord, out vec4 outputColor, out float outputShadowLength) {
2331
+ vec4 currentColor = texelFetch(colorBuffer, coord, 0);
2332
+ #ifdef SHADOW_LENGTH
2333
+ vec4 currentShadowLength = vec4(texelFetch(shadowLengthBuffer, coord, 0).rgb, 1.0);
2334
+ #endif // SHADOW_LENGTH
2335
+
2336
+ vec4 depthVelocity = getClosestFragment(coord);
2337
+ vec2 velocity = depthVelocity.gb * texelSize;
2338
+
2339
+ vec2 prevUv = vUv - velocity;
2340
+ if (prevUv.x < 0.0 || prevUv.x > 1.0 || prevUv.y < 0.0 || prevUv.y > 1.0) {
2341
+ outputColor = currentColor;
2342
+ #ifdef SHADOW_LENGTH
2343
+ outputShadowLength = currentShadowLength.r;
2344
+ #endif // SHADOW_LENGTH
2345
+ return; // Rejection
2346
+ }
2347
+
2348
+ vec4 historyColor = texture(colorHistoryBuffer, prevUv);
2349
+ vec4 clippedColor = varianceClipping(colorBuffer, coord, currentColor, historyColor);
2350
+ outputColor = mix(clippedColor, currentColor, temporalAlpha);
2351
+
2352
+ #ifdef DEBUG_SHOW_VELOCITY
2353
+ outputColor.rgb = outputColor.rgb + vec3(abs(velocity), 0.0);
2354
+ #endif // DEBUG_SHOW_VELOCITY
2355
+
2356
+ #ifdef SHADOW_LENGTH
2357
+ vec4 historyShadowLength = vec4(texture(shadowLengthHistoryBuffer, prevUv).rgb, 1.0);
2358
+ vec4 clippedShadowLength = varianceClipping(
2359
+ shadowLengthBuffer,
2360
+ coord,
2361
+ currentShadowLength,
2362
+ historyShadowLength
2363
+ );
2364
+ outputShadowLength = mix(clippedShadowLength.r, currentShadowLength.r, temporalAlpha);
2365
+ #endif // SHADOW_LENGTH
2366
+ }
2367
+
2368
+ void main() {
2369
+ ivec2 coord = ivec2(gl_FragCoord.xy);
2370
+
2371
+ #if !defined(SHADOW_LENGTH)
2372
+ float outputShadowLength;
2373
+ #endif // !defined(SHADOW_LENGTH)
2374
+
2375
+ #ifdef TEMPORAL_UPSCALE
2376
+ ivec2 lowResCoord = coord / 4;
2377
+ int bayerValue = bayerIndices[coord.x % 4][coord.y % 4];
2378
+ bool currentFrame = bayerValue == frame % 16;
2379
+ temporalUpscale(coord, lowResCoord, currentFrame, outputColor, outputShadowLength);
2380
+ #else // TEMPORAL_UPSCALE
2381
+ temporalAntialiasing(coord, outputColor, outputShadowLength);
2382
+ #endif // TEMPORAL_UPSCALE
2383
+
2384
+ #if defined(SHADOW_LENGTH) && defined(DEBUG_SHOW_SHADOW_LENGTH)
2385
+ outputColor = vec4(turbo(outputShadowLength * 0.05), 1.0);
2386
+ #endif // defined(SHADOW_LENGTH) && defined(DEBUG_SHOW_SHADOW_LENGTH)
2387
+ }
2388
+ `, xt = `precision highp float;
2389
+
2390
+ layout(location = 0) in vec3 position;
2391
+
2392
+ out vec2 vUv;
2393
+
2394
+ void main() {
2395
+ vUv = position.xy * 0.5 + 0.5;
2396
+ gl_Position = vec4(position.xy, 1.0, 1.0);
2397
+ }
2398
+ `, Le = `#ifdef VARIANCE_9_SAMPLES
2399
+ #define VARIANCE_OFFSET_COUNT (8)
2400
+ const ivec2 varianceOffsets[8] = ivec2[8](
2401
+ ivec2(-1, -1),
2402
+ ivec2(-1, 1),
2403
+ ivec2(1, -1),
2404
+ ivec2(1, 1),
2405
+ ivec2(1, 0),
2406
+ ivec2(0, -1),
2407
+ ivec2(0, 1),
2408
+ ivec2(-1, 0)
2409
+ );
2410
+ #else // VARIANCE_9_SAMPLES
2411
+ #define VARIANCE_OFFSET_COUNT (4)
2412
+ const ivec2 varianceOffsets[4] = ivec2[4](ivec2(1, 0), ivec2(0, -1), ivec2(0, 1), ivec2(-1, 0));
2413
+ #endif // VARIANCE_9_SAMPLES
2414
+
2415
+ // Reference: https://github.com/playdeadgames/temporal
2416
+ vec4 clipAABB(const vec4 current, const vec4 history, const vec4 minColor, const vec4 maxColor) {
2417
+ vec3 pClip = 0.5 * (maxColor.rgb + minColor.rgb);
2418
+ vec3 eClip = 0.5 * (maxColor.rgb - minColor.rgb) + 1e-7;
2419
+ vec4 vClip = history - vec4(pClip, current.a);
2420
+ vec3 vUnit = vClip.xyz / eClip;
2421
+ vec3 aUnit = abs(vUnit);
2422
+ float maUnit = max(aUnit.x, max(aUnit.y, aUnit.z));
2423
+ if (maUnit > 1.0) {
2424
+ return vec4(pClip, current.a) + vClip / maUnit;
2425
+ }
2426
+ return history;
2427
+ }
2428
+
2429
+ #ifdef VARIANCE_SAMPLER_ARRAY
2430
+ #define VARIANCE_SAMPLER sampler2DArray
2431
+ #define VARIANCE_SAMPLER_COORD ivec3
2432
+ #else // VARIANCE_SAMPLER_ARRAY
2433
+ #define VARIANCE_SAMPLER sampler2D
2434
+ #define VARIANCE_SAMPLER_COORD ivec2
2435
+ #endif // VARIANCE_SAMPLER_ARRAY
2436
+
2437
+ // Variance clipping
2438
+ // Reference: https://developer.download.nvidia.com/gameworks/events/GDC2016/msalvi_temporal_supersampling.pdf
2439
+ vec4 varianceClipping(
2440
+ const VARIANCE_SAMPLER inputBuffer,
2441
+ const VARIANCE_SAMPLER_COORD coord,
2442
+ const vec4 current,
2443
+ const vec4 history,
2444
+ const float gamma
2445
+ ) {
2446
+ vec4 moment1 = current;
2447
+ vec4 moment2 = current * current;
2448
+ vec4 neighbor;
2449
+ #pragma unroll_loop_start
2450
+ for (int i = 0; i < 8; ++i) {
2451
+ #if UNROLLED_LOOP_INDEX < VARIANCE_OFFSET_COUNT
2452
+ neighbor = texelFetchOffset(inputBuffer, coord, 0, varianceOffsets[i]);
2453
+ moment1 += neighbor;
2454
+ moment2 += neighbor * neighbor;
2455
+ #endif // UNROLLED_LOOP_INDEX < VARIANCE_OFFSET_COUNT
2456
+ }
2457
+ #pragma unroll_loop_end
2458
+
2459
+ const float N = float(VARIANCE_OFFSET_COUNT + 1);
2460
+ vec4 mean = moment1 / N;
2461
+ vec4 varianceGamma = sqrt(max(moment2 / N - mean * mean, 0.0)) * gamma;
2462
+ vec4 minColor = mean - varianceGamma;
2463
+ vec4 maxColor = mean + varianceGamma;
2464
+ return clipAABB(clamp(mean, minColor, maxColor), history, minColor, maxColor);
2465
+ }
2466
+
2467
+ vec4 varianceClipping(
2468
+ const VARIANCE_SAMPLER inputBuffer,
2469
+ const VARIANCE_SAMPLER_COORD coord,
2470
+ const vec4 current,
2471
+ const vec4 history
2472
+ ) {
2473
+ return varianceClipping(inputBuffer, coord, current, history, 1.0);
2474
+ }
2475
+
2476
+ vec4 varianceClipping(
2477
+ const sampler2D inputBuffer,
2478
+ const vec2 coord,
2479
+ const vec4 current,
2480
+ const vec4 history,
2481
+ const float gamma
2482
+ ) {
2483
+ vec4 moment1 = current;
2484
+ vec4 moment2 = current * current;
2485
+ vec4 neighbor;
2486
+ #pragma unroll_loop_start
2487
+ for (int i = 0; i < 8; ++i) {
2488
+ #if UNROLLED_LOOP_INDEX < VARIANCE_OFFSET_COUNT
2489
+ neighbor = textureOffset(inputBuffer, coord, varianceOffsets[i]);
2490
+ moment1 += neighbor;
2491
+ moment2 += neighbor * neighbor;
2492
+ #endif // UNROLLED_LOOP_INDEX < VARIANCE_OFFSET_COUNT
2493
+ }
2494
+ #pragma unroll_loop_end
2495
+
2496
+ const float N = float(VARIANCE_OFFSET_COUNT + 1);
2497
+ vec4 mean = moment1 / N;
2498
+ vec4 varianceGamma = sqrt(max(moment2 / N - mean * mean, 0.0)) * gamma;
2499
+ vec4 minColor = mean - varianceGamma;
2500
+ vec4 maxColor = mean + varianceGamma;
2501
+ return clipAABB(clamp(mean, minColor, maxColor), history, minColor, maxColor);
2502
+ }
2503
+
2504
+ vec4 varianceClipping(
2505
+ const sampler2D inputBuffer,
2506
+ const vec2 coord,
2507
+ const vec4 current,
2508
+ const vec4 history
2509
+ ) {
2510
+ return varianceClipping(inputBuffer, coord, current, history, 1.0);
2511
+ }
2512
+ `;
2513
+ var wt = Object.defineProperty, Ie = (o, e, t, n) => {
2514
+ for (var a = void 0, i = o.length - 1, s; i >= 0; i--)
2515
+ (s = o[i]) && (a = s(e, t, a) || a);
2516
+ return a && wt(e, t, a), a;
2517
+ };
2518
+ class Q extends X {
2519
+ constructor({
2520
+ colorBuffer: e = null,
2521
+ depthVelocityBuffer: t = null,
2522
+ shadowLengthBuffer: n = null,
2523
+ colorHistoryBuffer: a = null,
2524
+ shadowLengthHistoryBuffer: i = null
2525
+ } = {}) {
2526
+ super({
2527
+ name: "CloudsResolveMaterial",
2528
+ glslVersion: W,
2529
+ vertexShader: xt,
2530
+ fragmentShader: G(
2531
+ M(yt, {
2532
+ core: { turbo: Ae },
2533
+ catmullRomSampling: St,
2534
+ varianceClipping: Le
2535
+ })
2536
+ ),
2537
+ uniforms: {
2538
+ colorBuffer: new r(e),
2539
+ depthVelocityBuffer: new r(t),
2540
+ shadowLengthBuffer: new r(n),
2541
+ colorHistoryBuffer: new r(a),
2542
+ shadowLengthHistoryBuffer: new r(i),
2543
+ texelSize: new r(new m()),
2544
+ frame: new r(0),
2545
+ jitterOffset: new r(new m()),
2546
+ varianceGamma: new r(2),
2547
+ temporalAlpha: new r(0.1)
2548
+ }
2549
+ }), this.temporalUpscale = !0, this.shadowLength = !0;
2550
+ }
2551
+ setSize(e, t) {
2552
+ this.uniforms.texelSize.value.set(1 / e, 1 / t);
2553
+ }
2554
+ onBeforeRender(e, t, n, a, i, s) {
2555
+ const u = this.uniforms.frame.value % 16, h = Pe[u], d = (h.x - 0.5) * 4, p = (h.y - 0.5) * 4;
2556
+ this.uniforms.jitterOffset.value.set(d, p);
2557
+ }
2558
+ }
2559
+ Ie([
2560
+ C("TEMPORAL_UPSCALE")
2561
+ ], Q.prototype, "temporalUpscale");
2562
+ Ie([
2563
+ C("SHADOW_LENGTH")
2564
+ ], Q.prototype, "shadowLength");
2565
+ class Ne extends He {
2566
+ constructor(e, t) {
2567
+ super(e), this._mainCamera = new xe();
2568
+ const { shadow: n } = t;
2569
+ this.shadow = n;
2570
+ }
2571
+ get mainCamera() {
2572
+ return this._mainCamera;
2573
+ }
2574
+ set mainCamera(e) {
2575
+ this._mainCamera = e;
2576
+ }
2577
+ }
2578
+ function k(o, { depthVelocity: e, shadowLength: t }) {
2579
+ const n = new Ge(1, 1, {
2580
+ depthBuffer: !1,
2581
+ stencilBuffer: !1,
2582
+ type: we
2583
+ });
2584
+ n.texture.minFilter = z, n.texture.magFilter = z, n.texture.name = o;
2585
+ let a;
2586
+ e && (a = n.texture.clone(), a.isRenderTargetTexture = !0, n.depthVelocity = a, n.textures.push(a));
2587
+ let i;
2588
+ return t && (i = n.texture.clone(), i.isRenderTargetTexture = !0, i.format = Be, n.shadowLength = i, n.textures.push(i)), Object.assign(n, {
2589
+ depthVelocity: a ?? null,
2590
+ shadowLength: i ?? null
2591
+ });
2592
+ }
2593
+ class Ct extends Ne {
2594
+ constructor({
2595
+ parameterUniforms: e,
2596
+ layerUniforms: t,
2597
+ atmosphereUniforms: n,
2598
+ ...a
2599
+ }, i) {
2600
+ super("CloudsPass", a), this.atmosphere = i, this.width = 0, this.height = 0, this.currentMaterial = new y(
2601
+ {
2602
+ parameterUniforms: e,
2603
+ layerUniforms: t,
2604
+ atmosphereUniforms: n
2605
+ },
2606
+ i
2607
+ ), this.currentPass = new Z(this.currentMaterial), this.resolveMaterial = new Q(), this.resolvePass = new Z(this.resolveMaterial), this.initRenderTargets({
2608
+ depthVelocity: !0,
2609
+ shadowLength: l.lightShafts
2610
+ });
2611
+ }
2612
+ copyCameraSettings(e) {
2613
+ this.currentMaterial.copyCameraSettings(e);
2614
+ }
2615
+ initialize(e, t, n) {
2616
+ this.currentPass.initialize(e, t, n), this.resolvePass.initialize(e, t, n);
2617
+ }
2618
+ initRenderTargets(e) {
2619
+ var s, c, u;
2620
+ (s = this.currentRenderTarget) == null || s.dispose(), (c = this.resolveRenderTarget) == null || c.dispose(), (u = this.historyRenderTarget) == null || u.dispose();
2621
+ const t = k("Clouds", e), n = k("Clouds.A", {
2622
+ ...e,
2623
+ depthVelocity: !1
2624
+ }), a = k("Clouds.B", {
2625
+ ...e,
2626
+ depthVelocity: !1
2627
+ });
2628
+ this.currentRenderTarget = t, this.resolveRenderTarget = n, this.historyRenderTarget = a;
2629
+ const i = this.resolveMaterial.uniforms;
2630
+ i.colorBuffer.value = t.texture, i.depthVelocityBuffer.value = t.depthVelocity, i.shadowLengthBuffer.value = t.shadowLength, i.colorHistoryBuffer.value = a.texture, i.shadowLengthHistoryBuffer.value = a.shadowLength;
2631
+ }
2632
+ copyShadow() {
2633
+ const e = this.shadow, t = this.currentMaterial.uniforms;
2634
+ for (let n = 0; n < e.cascadeCount; ++n) {
2635
+ const a = e.cascades[n];
2636
+ t.shadowIntervals.value[n].copy(a.interval), t.shadowMatrices.value[n].copy(a.matrix);
2637
+ }
2638
+ t.shadowFar.value = e.far;
2639
+ }
2640
+ copyReprojection() {
2641
+ this.currentMaterial.copyReprojectionMatrix(this.mainCamera);
2642
+ }
2643
+ swapBuffers() {
2644
+ const e = this.historyRenderTarget, t = this.resolveRenderTarget;
2645
+ this.resolveRenderTarget = e, this.historyRenderTarget = t;
2646
+ const n = this.resolveMaterial.uniforms;
2647
+ n.colorHistoryBuffer.value = t.texture, n.shadowLengthHistoryBuffer.value = t.shadowLength;
2648
+ }
2649
+ update(e, t, n) {
2650
+ this.currentMaterial.uniforms.frame.value = t, this.resolveMaterial.uniforms.frame.value = t, this.copyCameraSettings(this.mainCamera), this.copyShadow(), this.currentPass.render(e, null, this.currentRenderTarget), this.resolvePass.render(e, null, this.resolveRenderTarget), this.copyReprojection(), this.swapBuffers();
2651
+ }
2652
+ setSize(e, t) {
2653
+ if (this.width = e, this.height = t, this.temporalUpscale) {
2654
+ const n = Math.ceil(e / 4), a = Math.ceil(t / 4);
2655
+ this.currentRenderTarget.setSize(n, a), this.currentMaterial.setSize(
2656
+ n * 4,
2657
+ a * 4,
2658
+ e,
2659
+ t
2660
+ );
2661
+ } else
2662
+ this.currentRenderTarget.setSize(e, t), this.currentMaterial.setSize(e, t);
2663
+ this.resolveRenderTarget.setSize(e, t), this.resolveMaterial.setSize(e, t), this.historyRenderTarget.setSize(e, t);
2664
+ }
2665
+ setShadowSize(e, t, n) {
2666
+ this.currentMaterial.shadowCascadeCount = n, this.currentMaterial.setShadowSize(e, t);
2667
+ }
2668
+ setDepthTexture(e, t) {
2669
+ this.currentMaterial.depthBuffer = e, this.currentMaterial.depthPacking = t ?? 0;
2670
+ }
2671
+ get outputBuffer() {
2672
+ return this.historyRenderTarget.texture;
2673
+ }
2674
+ get shadowBuffer() {
2675
+ return this.currentMaterial.uniforms.shadowBuffer.value;
2676
+ }
2677
+ set shadowBuffer(e) {
2678
+ this.currentMaterial.uniforms.shadowBuffer.value = e;
2679
+ }
2680
+ get shadowLengthBuffer() {
2681
+ return this.historyRenderTarget.shadowLength;
2682
+ }
2683
+ get temporalUpscale() {
2684
+ return this.currentMaterial.temporalUpscale;
2685
+ }
2686
+ set temporalUpscale(e) {
2687
+ e !== this.temporalUpscale && (this.currentMaterial.temporalUpscale = e, this.resolveMaterial.temporalUpscale = e, this.setSize(this.width, this.height));
2688
+ }
2689
+ get lightShafts() {
2690
+ return this.currentMaterial.shadowLength;
2691
+ }
2692
+ set lightShafts(e) {
2693
+ e !== this.lightShafts && (this.currentMaterial.shadowLength = e, this.resolveMaterial.shadowLength = e, this.initRenderTargets({
2694
+ depthVelocity: !0,
2695
+ shadowLength: e
2696
+ }), this.setSize(this.width, this.height));
2697
+ }
2698
+ }
2699
+ function Tt(o, e) {
2700
+ const t = o.properties.get(e.texture).__webglTexture, n = o.getContext();
2701
+ A(n instanceof WebGL2RenderingContext), o.setRenderTarget(e);
2702
+ const a = [];
2703
+ if (t != null)
2704
+ for (let i = 0; i < e.depth; ++i)
2705
+ n.framebufferTextureLayer(
2706
+ n.FRAMEBUFFER,
2707
+ n.COLOR_ATTACHMENT0 + i,
2708
+ t,
2709
+ 0,
2710
+ i
2711
+ ), a.push(n.COLOR_ATTACHMENT0 + i);
2712
+ n.drawBuffers(a);
2713
+ }
2714
+ class ye extends Z {
2715
+ render(e, t, n, a, i) {
2716
+ const s = this.fullscreenMaterial.uniforms;
2717
+ t !== null && (s == null ? void 0 : s[this.input]) != null && (s[this.input].value = t.texture), Tt(e, n), e.render(this.scene, this.camera);
2718
+ }
2719
+ }
2720
+ const Dt = `precision highp float;
2721
+ precision highp sampler3D;
2722
+
2723
+ #include <common>
2724
+
2725
+ #include "core/math"
2726
+ #include "core/raySphereIntersection"
2727
+ #include "types"
2728
+ #include "parameters"
2729
+ #include "structuredSampling"
2730
+ #include "clouds"
2731
+
2732
+ uniform mat4 inverseShadowMatrices[CASCADE_COUNT];
2733
+ uniform mat4 reprojectionMatrices[CASCADE_COUNT];
2734
+
2735
+ // Primary raymarch
2736
+ uniform int maxIterationCount;
2737
+ uniform float minStepSize;
2738
+ uniform float maxStepSize;
2739
+ uniform float opticalDepthTailScale;
2740
+
2741
+ in vec2 vUv;
2742
+ in vec3 vEllipsoidCenter;
2743
+
2744
+ layout(location = 0) out vec4 outputColor[CASCADE_COUNT];
2745
+
2746
+ // Redundant notation for prettier.
2747
+ #if CASCADE_COUNT == 1
2748
+ layout(location = 1) out vec3 outputDepthVelocity[CASCADE_COUNT];
2749
+ #elif CASCADE_COUNT == 2
2750
+ layout(location = 2) out vec3 outputDepthVelocity[CASCADE_COUNT];
2751
+ #elif CASCADE_COUNT == 3
2752
+ layout(location = 3) out vec3 outputDepthVelocity[CASCADE_COUNT];
2753
+ #elif CASCADE_COUNT == 4
2754
+ layout(location = 4) out vec3 outputDepthVelocity[CASCADE_COUNT];
2755
+ #endif // CASCADE_COUNT
2756
+
2757
+ vec4 marchClouds(
2758
+ const vec3 rayOrigin,
2759
+ const vec3 rayDirection,
2760
+ const float maxRayDistance,
2761
+ const float jitter,
2762
+ const float mipLevel
2763
+ ) {
2764
+ // Setup structured volume sampling (SVS).
2765
+ // While SVS introduces spatial aliasing, it is indeed temporally stable,
2766
+ // which is important for lower-resolution shadow maps where a flickering
2767
+ // single pixel can be highly noticeable.
2768
+ vec3 normal = getStructureNormal(rayDirection, jitter);
2769
+ float rayDistance;
2770
+ float stepSize;
2771
+ intersectStructuredPlanes(
2772
+ normal,
2773
+ rayOrigin,
2774
+ rayDirection,
2775
+ clamp(maxRayDistance / float(maxIterationCount), minStepSize, maxStepSize),
2776
+ rayDistance,
2777
+ stepSize
2778
+ );
2779
+
2780
+ #ifdef TEMPORAL_JITTER
2781
+ rayDistance -= stepSize * jitter;
2782
+ #endif // TEMPORAL_JITTER
2783
+
2784
+ float extinctionSum = 0.0;
2785
+ float maxOpticalDepth = 0.0;
2786
+ float maxOpticalDepthTail = 0.0;
2787
+ float transmittanceIntegral = 1.0;
2788
+ float weightedDistanceSum = 0.0;
2789
+ float transmittanceSum = 0.0;
2790
+
2791
+ int sampleCount = 0;
2792
+ for (int i = 0; i < maxIterationCount; ++i) {
2793
+ if (rayDistance > maxRayDistance) {
2794
+ break; // Termination
2795
+ }
2796
+
2797
+ vec3 position = rayDistance * rayDirection + rayOrigin;
2798
+ float height = length(position) - bottomRadius;
2799
+
2800
+ #if !defined(DEBUG_MARCH_INTERVALS)
2801
+ if (insideLayerIntervals(height)) {
2802
+ rayDistance += stepSize;
2803
+ continue;
2804
+ }
2805
+ #endif // !defined(DEBUG_MARCH_INTERVALS)
2806
+
2807
+ // Sample rough weather.
2808
+ vec2 uv = getGlobeUv(position);
2809
+ WeatherSample weather = sampleWeather(uv, height, mipLevel);
2810
+
2811
+ if (any(greaterThan(weather.density, vec4(minDensity)))) {
2812
+ // Sample detailed participating media.
2813
+ // Note this assumes an homogeneous medium.
2814
+ MediaSample media = sampleMedia(weather, position, uv, mipLevel, jitter);
2815
+ if (media.extinction > minExtinction) {
2816
+ extinctionSum += media.extinction;
2817
+ maxOpticalDepth += media.extinction * stepSize;
2818
+ transmittanceIntegral *= exp(-media.extinction * stepSize);
2819
+ weightedDistanceSum += rayDistance * transmittanceIntegral;
2820
+ transmittanceSum += transmittanceIntegral;
2821
+ ++sampleCount;
2822
+ }
2823
+ }
2824
+
2825
+ if (transmittanceIntegral <= minTransmittance) {
2826
+ // A large amount of optical depth accumulates in the tail, beyond the
2827
+ // point of minimum transmittance. The expected optical depth seems to
2828
+ // decrease exponentially with the number of samples taken before reaching
2829
+ // the minimum transmittance.
2830
+ // See the discussion here: https://x.com/shotamatsuda/status/1886259549931520437
2831
+ maxOpticalDepthTail = min(
2832
+ opticalDepthTailScale * stepSize * exp(float(1 - sampleCount)),
2833
+ stepSize * 0.5 // Excessive optical depth only introduces aliasing.
2834
+ );
2835
+ break; // Early termination
2836
+ }
2837
+ rayDistance += stepSize;
2838
+ }
2839
+
2840
+ if (sampleCount == 0) {
2841
+ return vec4(maxRayDistance, 0.0, 0.0, 0.0);
2842
+ }
2843
+ float frontDepth = min(weightedDistanceSum / transmittanceSum, maxRayDistance);
2844
+ float meanExtinction = extinctionSum / float(sampleCount);
2845
+ return vec4(frontDepth, meanExtinction, maxOpticalDepth, maxOpticalDepthTail);
2846
+ }
2847
+
2848
+ void getRayNearFar(
2849
+ const vec3 sunPosition,
2850
+ const vec3 rayDirection,
2851
+ out float rayNear,
2852
+ out float rayFar
2853
+ ) {
2854
+ vec4 firstIntersections = raySphereFirstIntersection(
2855
+ sunPosition,
2856
+ rayDirection,
2857
+ vec3(0.0),
2858
+ bottomRadius + vec4(shadowTopHeight, shadowBottomHeight, 0.0, 0.0)
2859
+ );
2860
+ rayNear = max(0.0, firstIntersections.x);
2861
+ rayFar = firstIntersections.y;
2862
+ if (rayFar < 0.0) {
2863
+ rayFar = 1e6;
2864
+ }
2865
+ }
2866
+
2867
+ void cascade(
2868
+ const int cascadeIndex,
2869
+ const float mipLevel,
2870
+ out vec4 outputColor,
2871
+ out vec3 outputDepthVelocity
2872
+ ) {
2873
+ vec2 clip = vUv * 2.0 - 1.0;
2874
+ vec4 point = inverseShadowMatrices[cascadeIndex] * vec4(clip.xy, -1.0, 1.0);
2875
+ point /= point.w;
2876
+ vec3 sunPosition = mat3(inverseEllipsoidMatrix) * point.xyz - vEllipsoidCenter;
2877
+
2878
+ // The sun direction is in ECEF. Since the view matrix is constructed with the
2879
+ // ellipsoid matrix already applied, there's no need to apply the inverse
2880
+ // matrix here.
2881
+ vec3 rayDirection = normalize(-sunDirection);
2882
+ float rayNear;
2883
+ float rayFar;
2884
+ getRayNearFar(sunPosition, rayDirection, rayNear, rayFar);
2885
+
2886
+ vec3 rayOrigin = rayNear * rayDirection + sunPosition;
2887
+ float stbn = getSTBN();
2888
+ vec4 color = marchClouds(rayOrigin, rayDirection, rayFar - rayNear, stbn, mipLevel);
2889
+ outputColor = color;
2890
+
2891
+ // Velocity for temporal resolution.
2892
+ #ifdef TEMPORAL_PASS
2893
+ vec3 frontPosition = color.x * rayDirection + rayOrigin;
2894
+ vec3 frontPositionWorld = mat3(ellipsoidMatrix) * (frontPosition + vEllipsoidCenter);
2895
+ vec4 prevClip = reprojectionMatrices[cascadeIndex] * vec4(frontPositionWorld, 1.0);
2896
+ prevClip /= prevClip.w;
2897
+ vec2 prevUv = prevClip.xy * 0.5 + 0.5;
2898
+ vec2 velocity = (vUv - prevUv) * resolution;
2899
+ outputDepthVelocity = vec3(color.x, velocity);
2900
+ #else // TEMPORAL_PASS
2901
+ outputDepthVelocity = vec3(0.0);
2902
+ #endif // TEMPORAL_PASS
2903
+ }
2904
+
2905
+ // TODO: Calculate from the main camera frustum perhaps?
2906
+ const float mipLevels[4] = float[4](0.0, 0.5, 1.0, 2.0);
2907
+
2908
+ void main() {
2909
+ #pragma unroll_loop_start
2910
+ for (int i = 0; i < 4; ++i) {
2911
+ #if UNROLLED_LOOP_INDEX < CASCADE_COUNT
2912
+ cascade(UNROLLED_LOOP_INDEX, mipLevels[i], outputColor[i], outputDepthVelocity[i]);
2913
+ #endif // UNROLLED_LOOP_INDEX < CASCADE_COUNT
2914
+ }
2915
+ #pragma unroll_loop_end
2916
+ }
2917
+ `, Et = `precision highp float;
2918
+
2919
+ uniform vec3 ellipsoidCenter;
2920
+ uniform vec3 altitudeCorrection;
2921
+
2922
+ layout(location = 0) in vec3 position;
2923
+
2924
+ out vec2 vUv;
2925
+ out vec3 vEllipsoidCenter;
2926
+
2927
+ void main() {
2928
+ vUv = position.xy * 0.5 + 0.5;
2929
+ vEllipsoidCenter = ellipsoidCenter + altitudeCorrection;
2930
+
2931
+ gl_Position = vec4(position.xy, 1.0, 1.0);
2932
+ }
2933
+ `, At = `// Implements Structured Volume Sampling in fragment shader:
2934
+ // https://github.com/huwb/volsample
2935
+ // Implementation reference:
2936
+ // https://www.shadertoy.com/view/ttVfDc
2937
+
2938
+ void getIcosahedralVertices(const vec3 direction, out vec3 v1, out vec3 v2, out vec3 v3) {
2939
+ // Normalization scalers to fit dodecahedron to unit sphere.
2940
+ const float a = 0.85065080835204; // phi / sqrt(2 + phi)
2941
+ const float b = 0.5257311121191336; // 1 / sqrt(2 + phi)
2942
+
2943
+ // Derive the vertices of icosahedron where triangle intersects the direction.
2944
+ // See: https://www.ppsloan.org/publications/AmbientDice.pdf
2945
+ const float kT = 0.6180339887498948; // 1 / phi
2946
+ const float kT2 = 0.38196601125010515; // 1 / phi^2
2947
+ vec3 absD = abs(direction);
2948
+ float selector1 = dot(absD, vec3(1.0, kT2, -kT));
2949
+ float selector2 = dot(absD, vec3(-kT, 1.0, kT2));
2950
+ float selector3 = dot(absD, vec3(kT2, -kT, 1.0));
2951
+ v1 = selector1 > 0.0 ? vec3(a, b, 0.0) : vec3(-b, 0.0, a);
2952
+ v2 = selector2 > 0.0 ? vec3(0.0, a, b) : vec3(a, -b, 0.0);
2953
+ v3 = selector3 > 0.0 ? vec3(b, 0.0, a) : vec3(0.0, a, -b);
2954
+ vec3 octantSign = sign(direction);
2955
+ v1 *= octantSign;
2956
+ v2 *= octantSign;
2957
+ v3 *= octantSign;
2958
+ }
2959
+
2960
+ void swapIfBigger(inout vec4 a, inout vec4 b) {
2961
+ if (a.w > b.w) {
2962
+ vec4 t = a;
2963
+ a = b;
2964
+ b = t;
2965
+ }
2966
+ }
2967
+
2968
+ void sortVertices(inout vec3 a, inout vec3 b, inout vec3 c) {
2969
+ const vec3 base = vec3(0.5, 0.5, 1.0);
2970
+ vec4 aw = vec4(a, dot(a, base));
2971
+ vec4 bw = vec4(b, dot(b, base));
2972
+ vec4 cw = vec4(c, dot(c, base));
2973
+ swapIfBigger(aw, bw);
2974
+ swapIfBigger(bw, cw);
2975
+ swapIfBigger(aw, bw);
2976
+ a = aw.xyz;
2977
+ b = bw.xyz;
2978
+ c = cw.xyz;
2979
+ }
2980
+
2981
+ vec3 getPentagonalWeights(const vec3 direction, const vec3 v1, const vec3 v2, const vec3 v3) {
2982
+ float d1 = dot(v1, direction);
2983
+ float d2 = dot(v2, direction);
2984
+ float d3 = dot(v3, direction);
2985
+ vec3 w = exp(vec3(d1, d2, d3) * 40.0);
2986
+ return w / (w.x + w.y + w.z);
2987
+ }
2988
+
2989
+ vec3 getStructureNormal(
2990
+ const vec3 direction,
2991
+ const float jitter,
2992
+ out vec3 a,
2993
+ out vec3 b,
2994
+ out vec3 c,
2995
+ out vec3 weights
2996
+ ) {
2997
+ getIcosahedralVertices(direction, a, b, c);
2998
+ sortVertices(a, b, c);
2999
+ weights = getPentagonalWeights(direction, a, b, c);
3000
+ return jitter < weights.x
3001
+ ? a
3002
+ : jitter < weights.x + weights.y
3003
+ ? b
3004
+ : c;
3005
+ }
3006
+
3007
+ vec3 getStructureNormal(const vec3 direction, const float jitter) {
3008
+ vec3 a, b, c, weights;
3009
+ return getStructureNormal(direction, jitter, a, b, c, weights);
3010
+ }
3011
+
3012
+ // Reference: https://github.com/huwb/volsample/blob/master/src/unity/Assets/Shaders/RayMarchCore.cginc
3013
+ void intersectStructuredPlanes(
3014
+ const vec3 normal,
3015
+ const vec3 rayOrigin,
3016
+ const vec3 rayDirection,
3017
+ const float samplePeriod,
3018
+ out float stepOffset,
3019
+ out float stepSize
3020
+ ) {
3021
+ float NoD = dot(rayDirection, normal);
3022
+ stepSize = samplePeriod / abs(NoD);
3023
+
3024
+ // Skips leftover bit to get from rayOrigin to first strata plane.
3025
+ stepOffset = -mod(dot(rayOrigin, normal), samplePeriod) / NoD;
3026
+
3027
+ // mod() gives different results depending on if the arg is negative or
3028
+ // positive. This line makes it consistent, and ensures the first sample is in
3029
+ // front of the viewer.
3030
+ if (stepOffset < 0.0) {
3031
+ stepOffset += stepSize;
3032
+ }
3033
+ }
3034
+ `;
3035
+ var _t = Object.defineProperty, R = (o, e, t, n) => {
3036
+ for (var a = void 0, i = o.length - 1, s; i >= 0; i--)
3037
+ (s = o[i]) && (a = s(e, t, a) || a);
3038
+ return a && _t(e, t, a), a;
3039
+ };
3040
+ class _ extends X {
3041
+ constructor({
3042
+ parameterUniforms: e,
3043
+ layerUniforms: t,
3044
+ atmosphereUniforms: n
3045
+ }) {
3046
+ super({
3047
+ name: "ShadowMaterial",
3048
+ glslVersion: W,
3049
+ vertexShader: Et,
3050
+ fragmentShader: G(
3051
+ M(Dt, {
3052
+ core: {
3053
+ math: _e,
3054
+ raySphereIntersection: Ee
3055
+ },
3056
+ types: $,
3057
+ parameters: Re,
3058
+ structuredSampling: At,
3059
+ clouds: Oe
3060
+ })
3061
+ ),
3062
+ uniforms: {
3063
+ ...e,
3064
+ ...t,
3065
+ ...n,
3066
+ inverseShadowMatrices: new r(
3067
+ Array.from({ length: 4 }, () => new v())
3068
+ // Populate the max number of elements
3069
+ ),
3070
+ reprojectionMatrices: new r(
3071
+ Array.from({ length: 4 }, () => new v())
3072
+ // Populate the max number of elements
3073
+ ),
3074
+ resolution: new r(new m()),
3075
+ frame: new r(0),
3076
+ stbnTexture: new r(null),
3077
+ // Primary raymarch
3078
+ maxIterationCount: new r(l.shadow.maxIterationCount),
3079
+ minStepSize: new r(l.shadow.minStepSize),
3080
+ maxStepSize: new r(l.shadow.maxStepSize),
3081
+ minDensity: new r(l.shadow.minDensity),
3082
+ minExtinction: new r(l.shadow.minExtinction),
3083
+ minTransmittance: new r(l.shadow.minTransmittance),
3084
+ opticalDepthTailScale: new r(2)
3085
+ },
3086
+ defines: {
3087
+ SHADOW: "1",
3088
+ TEMPORAL_PASS: "1",
3089
+ TEMPORAL_JITTER: "1"
3090
+ }
3091
+ }), this.localWeatherChannels = "rgba", this.cascadeCount = l.shadow.cascadeCount, this.temporalPass = !0, this.temporalJitter = !0, this.shapeDetail = l.shapeDetail, this.turbulence = l.turbulence, this.cascadeCount = l.shadow.cascadeCount;
3092
+ }
3093
+ setSize(e, t) {
3094
+ this.uniforms.resolution.value.set(e, t);
3095
+ }
3096
+ }
3097
+ R([
3098
+ De("LOCAL_WEATHER_CHANNELS", {
3099
+ validate: (o) => /^[rgba]{4}$/.test(o)
3100
+ })
3101
+ ], _.prototype, "localWeatherChannels");
3102
+ R([
3103
+ O("CASCADE_COUNT", { min: 1, max: 4 })
3104
+ ], _.prototype, "cascadeCount");
3105
+ R([
3106
+ C("TEMPORAL_PASS")
3107
+ ], _.prototype, "temporalPass");
3108
+ R([
3109
+ C("TEMPORAL_JITTER")
3110
+ ], _.prototype, "temporalJitter");
3111
+ R([
3112
+ C("SHAPE_DETAIL")
3113
+ ], _.prototype, "shapeDetail");
3114
+ R([
3115
+ C("TURBULENCE")
3116
+ ], _.prototype, "turbulence");
3117
+ const Pt = `precision highp float;
3118
+ precision highp sampler2DArray;
3119
+
3120
+ #define VARIANCE_9_SAMPLES (1)
3121
+ #define VARIANCE_SAMPLER_ARRAY (1)
3122
+
3123
+ #include "varianceClipping"
3124
+
3125
+ uniform sampler2DArray inputBuffer;
3126
+ uniform sampler2DArray historyBuffer;
3127
+
3128
+ uniform vec2 texelSize;
3129
+ uniform float varianceGamma;
3130
+ uniform float temporalAlpha;
3131
+
3132
+ in vec2 vUv;
3133
+
3134
+ layout(location = 0) out vec4 outputColor[CASCADE_COUNT];
3135
+
3136
+ const ivec2 neighborOffsets[9] = ivec2[9](
3137
+ ivec2(-1, -1),
3138
+ ivec2(-1, 0),
3139
+ ivec2(-1, 1),
3140
+ ivec2(0, -1),
3141
+ ivec2(0, 0),
3142
+ ivec2(0, 1),
3143
+ ivec2(1, -1),
3144
+ ivec2(1, 0),
3145
+ ivec2(1, 1)
3146
+ );
3147
+
3148
+ vec4 getClosestFragment(const ivec3 coord) {
3149
+ vec4 result = vec4(1e7, 0.0, 0.0, 0.0);
3150
+ vec4 neighbor;
3151
+ #pragma unroll_loop_start
3152
+ for (int i = 0; i < 9; ++i) {
3153
+ neighbor = texelFetchOffset(
3154
+ inputBuffer,
3155
+ coord + ivec3(0, 0, CASCADE_COUNT),
3156
+ 0,
3157
+ neighborOffsets[i]
3158
+ );
3159
+ if (neighbor.r < result.r) {
3160
+ result = neighbor;
3161
+ }
3162
+ }
3163
+ #pragma unroll_loop_end
3164
+ return result;
3165
+ }
3166
+
3167
+ void cascade(const int cascadeIndex, out vec4 outputColor) {
3168
+ ivec3 coord = ivec3(gl_FragCoord.xy, cascadeIndex);
3169
+ vec4 current = texelFetch(inputBuffer, coord, 0);
3170
+
3171
+ vec4 depthVelocity = getClosestFragment(coord);
3172
+ vec2 velocity = depthVelocity.gb * texelSize;
3173
+ vec2 prevUv = vUv - velocity;
3174
+ if (prevUv.x < 0.0 || prevUv.x > 1.0 || prevUv.y < 0.0 || prevUv.y > 1.0) {
3175
+ outputColor = current;
3176
+ return; // Rejection
3177
+ }
3178
+
3179
+ vec4 history = texture(historyBuffer, vec3(prevUv, float(cascadeIndex)));
3180
+ vec4 clippedHistory = varianceClipping(inputBuffer, coord, current, history, varianceGamma);
3181
+ outputColor = mix(clippedHistory, current, temporalAlpha);
3182
+ }
3183
+
3184
+ void main() {
3185
+ #pragma unroll_loop_start
3186
+ for (int i = 0; i < 4; ++i) {
3187
+ #if UNROLLED_LOOP_INDEX < CASCADE_COUNT
3188
+ cascade(UNROLLED_LOOP_INDEX, outputColor[i]);
3189
+ #endif // UNROLLED_LOOP_INDEX < CASCADE_COUNT
3190
+ }
3191
+ #pragma unroll_loop_end
3192
+ }
3193
+ `, Ot = `precision highp float;
3194
+
3195
+ layout(location = 0) in vec3 position;
3196
+
3197
+ out vec2 vUv;
3198
+
3199
+ void main() {
3200
+ vUv = position.xy * 0.5 + 0.5;
3201
+ gl_Position = vec4(position.xy, 1.0, 1.0);
3202
+ }
3203
+ `;
3204
+ var Rt = Object.defineProperty, Lt = (o, e, t, n) => {
3205
+ for (var a = void 0, i = o.length - 1, s; i >= 0; i--)
3206
+ (s = o[i]) && (a = s(e, t, a) || a);
3207
+ return a && Rt(e, t, a), a;
3208
+ };
3209
+ class be extends X {
3210
+ constructor({
3211
+ inputBuffer: e = null,
3212
+ historyBuffer: t = null
3213
+ } = {}) {
3214
+ super({
3215
+ name: "ShadowResolveMaterial",
3216
+ glslVersion: W,
3217
+ vertexShader: Ot,
3218
+ fragmentShader: G(
3219
+ M(Pt, {
3220
+ varianceClipping: Le
3221
+ })
3222
+ ),
3223
+ uniforms: {
3224
+ inputBuffer: new r(e),
3225
+ historyBuffer: new r(t),
3226
+ texelSize: new r(new m()),
3227
+ varianceGamma: new r(1),
3228
+ // Use a very slow alpha because a single flickering pixel can be highly
3229
+ // noticeable in shadow maps. This value can be increased if temporal
3230
+ // jitter is turned off in the shadows rendering, but it will suffer
3231
+ // from spatial aliasing.
3232
+ temporalAlpha: new r(0.01)
3233
+ },
3234
+ defines: {}
3235
+ }), this.cascadeCount = l.shadow.cascadeCount;
3236
+ }
3237
+ setSize(e, t) {
3238
+ this.uniforms.texelSize.value.set(1 / e, 1 / t);
3239
+ }
3240
+ }
3241
+ Lt([
3242
+ O("CASCADE_COUNT", { min: 1, max: 4 })
3243
+ ], be.prototype, "cascadeCount");
3244
+ function j(o) {
3245
+ const e = new Ve(1, 1, 1, {
3246
+ depthBuffer: !1,
3247
+ stencilBuffer: !1
3248
+ });
3249
+ return e.texture.type = we, e.texture.minFilter = z, e.texture.magFilter = z, e.texture.name = o, e;
3250
+ }
3251
+ class It extends Ne {
3252
+ constructor({
3253
+ parameterUniforms: e,
3254
+ layerUniforms: t,
3255
+ atmosphereUniforms: n,
3256
+ ...a
3257
+ }) {
3258
+ super("ShadowPass", a), this.width = 0, this.height = 0, this.currentMaterial = new _({
3259
+ parameterUniforms: e,
3260
+ layerUniforms: t,
3261
+ atmosphereUniforms: n
3262
+ }), this.currentPass = new ye(this.currentMaterial), this.resolveMaterial = new be(), this.resolvePass = new ye(this.resolveMaterial), this.initRenderTargets();
3263
+ }
3264
+ initialize(e, t, n) {
3265
+ this.currentPass.initialize(e, t, n), this.resolvePass.initialize(e, t, n);
3266
+ }
3267
+ initRenderTargets() {
3268
+ var i, s, c;
3269
+ (i = this.currentRenderTarget) == null || i.dispose(), (s = this.resolveRenderTarget) == null || s.dispose(), (c = this.historyRenderTarget) == null || c.dispose();
3270
+ const e = j("Shadow"), t = this.temporalPass ? j("Shadow.A") : null, n = this.temporalPass ? j("Shadow.B") : null;
3271
+ this.currentRenderTarget = e, this.resolveRenderTarget = t, this.historyRenderTarget = n;
3272
+ const a = this.resolveMaterial.uniforms;
3273
+ a.inputBuffer.value = e.texture, a.historyBuffer.value = (n == null ? void 0 : n.texture) ?? null;
3274
+ }
3275
+ copyShadow() {
3276
+ const e = this.shadow, t = this.currentMaterial.uniforms;
3277
+ for (let n = 0; n < e.cascadeCount; ++n) {
3278
+ const a = e.cascades[n];
3279
+ t.inverseShadowMatrices.value[n].copy(a.inverseMatrix);
3280
+ }
3281
+ }
3282
+ copyReprojection() {
3283
+ const e = this.shadow, t = this.currentMaterial.uniforms;
3284
+ for (let n = 0; n < e.cascadeCount; ++n) {
3285
+ const a = e.cascades[n];
3286
+ t.reprojectionMatrices.value[n].copy(a.matrix);
3287
+ }
3288
+ }
3289
+ swapBuffers() {
3290
+ A(this.historyRenderTarget != null), A(this.resolveRenderTarget != null);
3291
+ const e = this.historyRenderTarget, t = this.resolveRenderTarget;
3292
+ this.resolveRenderTarget = e, this.historyRenderTarget = t, this.resolveMaterial.uniforms.historyBuffer.value = t.texture;
3293
+ }
3294
+ update(e, t, n) {
3295
+ this.currentMaterial.uniforms.frame.value = t, this.copyShadow(), this.currentPass.render(e, null, this.currentRenderTarget), this.temporalPass && (A(this.resolveRenderTarget != null), this.resolvePass.render(e, null, this.resolveRenderTarget), this.copyReprojection(), this.swapBuffers());
3296
+ }
3297
+ setSize(e, t, n = this.shadow.cascadeCount) {
3298
+ var a, i;
3299
+ this.width = e, this.height = t, this.currentMaterial.cascadeCount = n, this.resolveMaterial.cascadeCount = n, this.currentMaterial.setSize(e, t), this.resolveMaterial.setSize(e, t), this.currentRenderTarget.setSize(
3300
+ e,
3301
+ t,
3302
+ this.temporalPass ? n * 2 : n
3303
+ // For depth velocity
3304
+ ), (a = this.resolveRenderTarget) == null || a.setSize(e, t, n), (i = this.historyRenderTarget) == null || i.setSize(e, t, n);
3305
+ }
3306
+ get outputBuffer() {
3307
+ return this.temporalPass ? (A(this.historyRenderTarget != null), this.historyRenderTarget.texture) : this.currentRenderTarget.texture;
3308
+ }
3309
+ get temporalPass() {
3310
+ return this.currentMaterial.temporalPass;
3311
+ }
3312
+ set temporalPass(e) {
3313
+ e !== this.temporalPass && (this.currentMaterial.temporalPass = e, this.initRenderTargets(), this.setSize(this.width, this.height));
3314
+ }
3315
+ }
3316
+ function Nt(o) {
3317
+ return {
3318
+ // Participating medium
3319
+ scatteringCoefficient: new r(1),
3320
+ absorptionCoefficient: new r(0),
3321
+ // Weather and shape
3322
+ coverage: new r(0.3),
3323
+ localWeatherTexture: new r(o.localWeatherTexture),
3324
+ localWeatherRepeat: new r(o.localWeatherRepeat),
3325
+ localWeatherOffset: new r(o.localWeatherOffset),
3326
+ shapeTexture: new r(o.shapeTexture),
3327
+ shapeRepeat: new r(o.shapeRepeat),
3328
+ shapeOffset: new r(o.shapeOffset),
3329
+ shapeDetailTexture: new r(o.shapeDetailTexture),
3330
+ shapeDetailRepeat: new r(o.shapeDetailRepeat),
3331
+ shapeDetailOffset: new r(o.shapeDetailOffset),
3332
+ turbulenceTexture: new r(o.turbulenceTexture),
3333
+ turbulenceRepeat: new r(o.turbulenceRepeat),
3334
+ turbulenceDisplacement: new r(350)
3335
+ };
3336
+ }
3337
+ function bt() {
3338
+ return {
3339
+ minLayerHeights: new r(new x()),
3340
+ maxLayerHeights: new r(new x()),
3341
+ minIntervalHeights: new r(new f()),
3342
+ maxIntervalHeights: new r(new f()),
3343
+ densityScales: new r(new x()),
3344
+ shapeAmounts: new r(new x()),
3345
+ shapeDetailAmounts: new r(new x()),
3346
+ weatherExponents: new r(new x()),
3347
+ shapeAlteringBiases: new r(new x()),
3348
+ coverageFilterWidths: new r(new x()),
3349
+ minHeight: new r(0),
3350
+ maxHeight: new r(0),
3351
+ shadowTopHeight: new r(0),
3352
+ shadowBottomHeight: new r(0),
3353
+ shadowLayerMask: new r(new x()),
3354
+ densityProfile: new r({
3355
+ expTerms: new x(),
3356
+ exponents: new x(),
3357
+ linearTerms: new x(),
3358
+ constantTerms: new x()
3359
+ })
3360
+ };
3361
+ }
3362
+ const Y = [0, 0, 0, 0];
3363
+ function Mt(o, e) {
3364
+ e.packValues("altitude", o.minLayerHeights.value), e.packSums("altitude", "height", o.maxLayerHeights.value), e.packIntervalHeights(
3365
+ o.minIntervalHeights.value,
3366
+ o.maxIntervalHeights.value
3367
+ ), e.packValues("densityScale", o.densityScales.value), e.packValues("shapeAmount", o.shapeAmounts.value), e.packValues("shapeDetailAmount", o.shapeDetailAmounts.value), e.packValues("weatherExponent", o.weatherExponents.value), e.packValues("shapeAlteringBias", o.shapeAlteringBiases.value), e.packValues("coverageFilterWidth", o.coverageFilterWidths.value);
3368
+ const t = o.densityProfile.value;
3369
+ e.packDensityProfiles("expTerm", t.expTerms), e.packDensityProfiles("exponent", t.exponents), e.packDensityProfiles("linearTerm", t.linearTerms), e.packDensityProfiles("constantTerm", t.constantTerms);
3370
+ let n = 1 / 0, a = 0, i = 1 / 0, s = 0;
3371
+ Y.fill(0);
3372
+ for (let c = 0; c < e.length; ++c) {
3373
+ const { altitude: u, height: h, shadow: d } = e[c], p = u + h;
3374
+ h > 0 && (u < n && (n = u), d && u < i && (i = u), p > a && (a = p), d && p > s && (s = p)), Y[c] = d ? 1 : 0;
3375
+ }
3376
+ n !== 1 / 0 ? (o.minHeight.value = n, o.maxHeight.value = a) : (A(a === 0), o.minHeight.value = 0), i !== 1 / 0 ? (o.shadowBottomHeight.value = i, o.shadowTopHeight.value = s) : (A(s === 0), o.shadowBottomHeight.value = 0), o.shadowLayerMask.value.fromArray(Y);
3377
+ }
3378
+ function Ut(o, e) {
3379
+ return {
3380
+ bottomRadius: new r(o.bottomRadius),
3381
+ topRadius: new r(o.topRadius),
3382
+ ellipsoidCenter: new r(e.ellipsoidCenter),
3383
+ ellipsoidMatrix: new r(e.ellipsoidMatrix),
3384
+ inverseEllipsoidMatrix: new r(e.inverseEllipsoidMatrix),
3385
+ altitudeCorrection: new r(e.altitudeCorrection),
3386
+ sunDirection: new r(e.sunDirection)
3387
+ };
3388
+ }
3389
+ const Ht = `uniform sampler2D cloudsBuffer;
3390
+
3391
+ void mainImage(const vec4 inputColor, const vec2 uv, out vec4 outputColor) {
3392
+ #ifdef SKIP_RENDERING
3393
+ outputColor = inputColor;
3394
+ #else // SKIP_RENDERING
3395
+ vec4 clouds = texture(cloudsBuffer, uv);
3396
+ outputColor.rgb = inputColor.rgb * (1.0 - clouds.a) + clouds.rgb;
3397
+ outputColor.a = inputColor.a * (1.0 - clouds.a) + clouds.a;
3398
+ #endif // SKIP_RENDERING
3399
+ }
3400
+ `;
3401
+ var Ft = Object.defineProperty, zt = (o, e, t, n) => {
3402
+ for (var a = void 0, i = o.length - 1, s; i >= 0; i--)
3403
+ (s = o[i]) && (a = s(e, t, a) || a);
3404
+ return a && Ft(e, t, a), a;
3405
+ };
3406
+ const I = /* @__PURE__ */ new f(), Wt = /* @__PURE__ */ new m(), Gt = [
3407
+ "maxIterationCount",
3408
+ "minStepSize",
3409
+ "maxStepSize",
3410
+ "maxRayDistance",
3411
+ "perspectiveStepScale",
3412
+ "minDensity",
3413
+ "minExtinction",
3414
+ "minTransmittance",
3415
+ "maxIterationCountToSun",
3416
+ "maxIterationCountToGround",
3417
+ "minSecondaryStepSize",
3418
+ "secondaryStepScale",
3419
+ "maxShadowFilterRadius",
3420
+ "maxShadowLengthIterationCount",
3421
+ "minShadowLengthStepSize",
3422
+ "maxShadowLengthRayDistance",
3423
+ "hazeDensityScale",
3424
+ "hazeExponent"
3425
+ ], Bt = [
3426
+ "multiScatteringOctaves",
3427
+ "accurateSunSkyIrradiance",
3428
+ "accuratePhaseFunction"
3429
+ ], Vt = [
3430
+ "maxIterationCount",
3431
+ "minStepSize",
3432
+ "maxStepSize",
3433
+ "minDensity",
3434
+ "minExtinction",
3435
+ "minTransmittance",
3436
+ "opticalDepthTailScale"
3437
+ ], kt = [
3438
+ "temporalJitter"
3439
+ ], jt = [
3440
+ "temporalPass"
3441
+ ], Yt = [
3442
+ "cascadeCount",
3443
+ "mapSize",
3444
+ "maxFar",
3445
+ "farScale",
3446
+ "splitMode",
3447
+ "splitLambda"
3448
+ ], D = {
3449
+ type: "change"
3450
+ }, Zt = {
3451
+ resolutionScale: l.resolutionScale,
3452
+ width: q.AUTO_SIZE,
3453
+ height: q.AUTO_SIZE
3454
+ };
3455
+ class qt extends Fe {
3456
+ constructor(e = new xe(), t, n = Ce.DEFAULT) {
3457
+ var d, p, g, S;
3458
+ super("CloudsEffect", Ht, {
3459
+ attributes: ze.DEPTH,
3460
+ uniforms: /* @__PURE__ */ new Map([["cloudsBuffer", new r(null)]])
3461
+ }), this.camera = e, this.atmosphere = n, this.cloudLayers = K.DEFAULT.clone(), this.correctAltitude = !0, this.localWeatherRepeat = new m().setScalar(100), this.localWeatherOffset = new m(), this.shapeRepeat = new f().setScalar(3e-4), this.shapeOffset = new f(), this.shapeDetailRepeat = new f().setScalar(6e-3), this.shapeDetailOffset = new f(), this.turbulenceRepeat = new m().setScalar(20), this.ellipsoidCenter = new f(), this.ellipsoidMatrix = new v(), this.inverseEllipsoidMatrix = new v(), this.altitudeCorrection = new f(), this.sunDirection = new f(), this.localWeatherVelocity = new m(), this.shapeVelocity = new f(), this.shapeDetailVelocity = new f(), this._atmosphereOverlay = null, this._atmosphereShadow = null, this._atmosphereShadowLength = null, this.events = new ke(), this.frame = 0, this.shadowCascadeCount = 0, this.shadowMapSize = new m(), this.onResolutionChange = () => {
3462
+ this.setSize(this.resolution.baseWidth, this.resolution.baseHeight);
3463
+ }, this.skipRendering = !0;
3464
+ const {
3465
+ resolutionScale: a,
3466
+ width: i,
3467
+ height: s,
3468
+ resolutionX: c = i,
3469
+ resolutionY: u = s
3470
+ } = {
3471
+ ...Zt,
3472
+ ...t
3473
+ };
3474
+ this.shadowMaps = new ut({
3475
+ cascadeCount: l.shadow.cascadeCount,
3476
+ mapSize: l.shadow.mapSize,
3477
+ splitLambda: 0.6
3478
+ }), this.parameterUniforms = Nt({
3479
+ localWeatherTexture: ((d = this.proceduralLocalWeather) == null ? void 0 : d.texture) ?? null,
3480
+ localWeatherRepeat: this.localWeatherRepeat,
3481
+ localWeatherOffset: this.localWeatherOffset,
3482
+ shapeTexture: ((p = this.proceduralShape) == null ? void 0 : p.texture) ?? null,
3483
+ shapeRepeat: this.shapeRepeat,
3484
+ shapeOffset: this.shapeOffset,
3485
+ shapeDetailTexture: ((g = this.proceduralShapeDetail) == null ? void 0 : g.texture) ?? null,
3486
+ shapeDetailRepeat: this.shapeDetailRepeat,
3487
+ shapeDetailOffset: this.shapeDetailOffset,
3488
+ turbulenceTexture: ((S = this.proceduralTurbulence) == null ? void 0 : S.texture) ?? null,
3489
+ turbulenceRepeat: this.turbulenceRepeat
3490
+ }), this.layerUniforms = bt(), this.atmosphereUniforms = Ut(n, {
3491
+ ellipsoidCenter: this.ellipsoidCenter,
3492
+ ellipsoidMatrix: this.ellipsoidMatrix,
3493
+ inverseEllipsoidMatrix: this.inverseEllipsoidMatrix,
3494
+ altitudeCorrection: this.altitudeCorrection,
3495
+ sunDirection: this.sunDirection
3496
+ });
3497
+ const h = {
3498
+ shadow: this.shadowMaps,
3499
+ parameterUniforms: this.parameterUniforms,
3500
+ layerUniforms: this.layerUniforms,
3501
+ atmosphereUniforms: this.atmosphereUniforms
3502
+ };
3503
+ this.shadowPass = new It(h), this.cloudsPass = new Ct(h, n), this.clouds = ue(
3504
+ he(
3505
+ {},
3506
+ this.cloudsPass.currentMaterial,
3507
+ Gt
3508
+ ),
3509
+ this.cloudsPass.currentMaterial,
3510
+ Bt
3511
+ ), this.shadow = ue(
3512
+ he(
3513
+ {},
3514
+ this.shadowPass.currentMaterial,
3515
+ Vt
3516
+ ),
3517
+ this.shadowPass.currentMaterial,
3518
+ kt,
3519
+ this.shadowPass,
3520
+ jt,
3521
+ this.shadowMaps,
3522
+ Yt
3523
+ ), this.resolution = new q(
3524
+ this,
3525
+ c,
3526
+ u,
3527
+ a
3528
+ ), this.resolution.addEventListener("change", this.onResolutionChange);
3529
+ }
3530
+ get mainCamera() {
3531
+ return this.camera;
3532
+ }
3533
+ set mainCamera(e) {
3534
+ this.camera = e, this.shadowPass.mainCamera = e, this.cloudsPass.mainCamera = e;
3535
+ }
3536
+ initialize(e, t, n) {
3537
+ this.shadowPass.initialize(e, t, n), this.cloudsPass.initialize(e, t, n);
3538
+ }
3539
+ updateSharedUniforms(e) {
3540
+ Mt(this.layerUniforms, this.cloudLayers);
3541
+ const { parameterUniforms: t } = this;
3542
+ t.localWeatherOffset.value.add(
3543
+ Wt.copy(this.localWeatherVelocity).multiplyScalar(e)
3544
+ ), t.shapeOffset.value.add(
3545
+ I.copy(this.shapeVelocity).multiplyScalar(e)
3546
+ ), t.shapeDetailOffset.value.add(
3547
+ I.copy(this.shapeDetailVelocity).multiplyScalar(e)
3548
+ );
3549
+ const n = this.inverseEllipsoidMatrix.copy(this.ellipsoidMatrix).invert(), a = this.camera.getWorldPosition(I).applyMatrix4(n).sub(this.ellipsoidCenter), i = this.altitudeCorrection;
3550
+ this.correctAltitude ? Ye(
3551
+ a,
3552
+ this.atmosphere.bottomRadius,
3553
+ this.ellipsoid,
3554
+ i,
3555
+ !1
3556
+ ) : i.setScalar(0);
3557
+ const s = this.ellipsoid.getSurfaceNormal(
3558
+ a,
3559
+ I
3560
+ ), c = this.sunDirection.dot(s), u = Te(1e6, 1e3, c);
3561
+ this.shadowMaps.update(
3562
+ this.camera,
3563
+ // The sun direction must be rotated with the ellipsoid to ensure the
3564
+ // frusta are constructed correctly. Note this affects the transformation
3565
+ // in the shadow shader.
3566
+ I.copy(this.sunDirection).applyMatrix4(this.ellipsoidMatrix),
3567
+ u
3568
+ );
3569
+ }
3570
+ updateWeatherTextureChannels() {
3571
+ const e = this.cloudLayers.localWeatherChannels;
3572
+ this.cloudsPass.currentMaterial.localWeatherChannels = e, this.shadowPass.currentMaterial.localWeatherChannels = e;
3573
+ }
3574
+ updateAtmosphereComposition() {
3575
+ const { shadowMaps: e, shadowPass: t, cloudsPass: n } = this, a = t.currentMaterial.uniforms, i = n.currentMaterial.uniforms, s = this._atmosphereOverlay, c = Object.assign(this._atmosphereOverlay ?? {}, {
3576
+ map: n.outputBuffer
3577
+ });
3578
+ s !== c && (this._atmosphereOverlay = c, D.target = this, D.property = "atmosphereOverlay", this.events.dispatchEvent(D));
3579
+ const u = this._atmosphereShadow, h = Object.assign(this._atmosphereShadow ?? {}, {
3580
+ map: t.outputBuffer,
3581
+ mapSize: e.mapSize,
3582
+ cascadeCount: e.cascadeCount,
3583
+ intervals: i.shadowIntervals.value,
3584
+ matrices: i.shadowMatrices.value,
3585
+ inverseMatrices: a.inverseShadowMatrices.value,
3586
+ far: e.far,
3587
+ topHeight: i.shadowTopHeight.value
3588
+ });
3589
+ u !== h && (this._atmosphereShadow = h, D.target = this, D.property = "atmosphereShadow", this.events.dispatchEvent(D));
3590
+ const d = this._atmosphereShadowLength, p = n.shadowLengthBuffer != null ? Object.assign(this._atmosphereShadowLength ?? {}, {
3591
+ map: n.shadowLengthBuffer
3592
+ }) : null;
3593
+ d !== p && (this._atmosphereShadowLength = p, D.target = this, D.property = "atmosphereShadowLength", this.events.dispatchEvent(D));
3594
+ }
3595
+ update(e, t, n = 0) {
3596
+ var c, u, h, d;
3597
+ const { shadowMaps: a, shadowPass: i, cloudsPass: s } = this;
3598
+ if (a.cascadeCount !== this.shadowCascadeCount || !a.mapSize.equals(this.shadowMapSize)) {
3599
+ const { width: p, height: g } = a.mapSize, S = a.cascadeCount;
3600
+ this.shadowMapSize.set(p, g), this.shadowCascadeCount = S, i.setSize(p, g, S), s.setShadowSize(p, g, S);
3601
+ }
3602
+ (c = this.proceduralLocalWeather) == null || c.render(e, n), (u = this.proceduralShape) == null || u.render(e, n), (h = this.proceduralShapeDetail) == null || h.render(e, n), (d = this.proceduralTurbulence) == null || d.render(e, n), ++this.frame, this.updateSharedUniforms(n), this.updateWeatherTextureChannels(), i.update(e, this.frame, n), s.shadowBuffer = i.outputBuffer, s.update(e, this.frame, n), this.updateAtmosphereComposition(), this.uniforms.get("cloudsBuffer").value = this.cloudsPass.outputBuffer;
3603
+ }
3604
+ setSize(e, t) {
3605
+ const { resolution: n } = this;
3606
+ n.setBaseSize(e, t);
3607
+ const { width: a, height: i } = n;
3608
+ this.cloudsPass.setSize(a, i);
3609
+ }
3610
+ setDepthTexture(e, t) {
3611
+ this.shadowPass.setDepthTexture(e, t), this.cloudsPass.setDepthTexture(e, t);
3612
+ }
3613
+ // eslint-disable-next-line accessor-pairs
3614
+ set qualityPreset(e) {
3615
+ const { clouds: t, shadow: n, ...a } = dt[e];
3616
+ Object.assign(this, a), Object.assign(this.clouds, t), Object.assign(this.shadow, n);
3617
+ }
3618
+ // Textures
3619
+ get localWeatherTexture() {
3620
+ return this.proceduralLocalWeather ?? this.parameterUniforms.localWeatherTexture.value;
3621
+ }
3622
+ set localWeatherTexture(e) {
3623
+ e instanceof ce || e == null ? (this.proceduralLocalWeather = void 0, this.parameterUniforms.localWeatherTexture.value = e) : (this.proceduralLocalWeather = e, this.parameterUniforms.localWeatherTexture.value = e.texture);
3624
+ }
3625
+ get shapeTexture() {
3626
+ return this.proceduralShape ?? this.parameterUniforms.shapeTexture.value;
3627
+ }
3628
+ set shapeTexture(e) {
3629
+ e instanceof le || e == null ? (this.proceduralShape = void 0, this.parameterUniforms.shapeTexture.value = e) : (this.proceduralShape = e, this.parameterUniforms.shapeTexture.value = e.texture);
3630
+ }
3631
+ get shapeDetailTexture() {
3632
+ return this.proceduralShapeDetail ?? this.parameterUniforms.shapeDetailTexture.value;
3633
+ }
3634
+ set shapeDetailTexture(e) {
3635
+ e instanceof le || e == null ? (this.proceduralShapeDetail = void 0, this.parameterUniforms.shapeDetailTexture.value = e) : (this.proceduralShapeDetail = e, this.parameterUniforms.shapeDetailTexture.value = e.texture);
3636
+ }
3637
+ get turbulenceTexture() {
3638
+ return this.proceduralTurbulence ?? this.parameterUniforms.turbulenceTexture.value;
3639
+ }
3640
+ set turbulenceTexture(e) {
3641
+ e instanceof ce || e == null ? (this.proceduralTurbulence = void 0, this.parameterUniforms.turbulenceTexture.value = e) : (this.proceduralTurbulence = e, this.parameterUniforms.turbulenceTexture.value = e.texture);
3642
+ }
3643
+ get stbnTexture() {
3644
+ return this.cloudsPass.currentMaterial.uniforms.stbnTexture.value;
3645
+ }
3646
+ set stbnTexture(e) {
3647
+ this.cloudsPass.currentMaterial.uniforms.stbnTexture.value = e, this.shadowPass.currentMaterial.uniforms.stbnTexture.value = e;
3648
+ }
3649
+ // Rendering controls
3650
+ get resolutionScale() {
3651
+ return this.resolution.scale;
3652
+ }
3653
+ set resolutionScale(e) {
3654
+ this.resolution.scale = e;
3655
+ }
3656
+ get temporalUpscale() {
3657
+ return this.cloudsPass.temporalUpscale;
3658
+ }
3659
+ set temporalUpscale(e) {
3660
+ this.cloudsPass.temporalUpscale = e;
3661
+ }
3662
+ get lightShafts() {
3663
+ return this.cloudsPass.lightShafts;
3664
+ }
3665
+ set lightShafts(e) {
3666
+ this.cloudsPass.lightShafts = e;
3667
+ }
3668
+ get shapeDetail() {
3669
+ return this.cloudsPass.currentMaterial.shapeDetail;
3670
+ }
3671
+ set shapeDetail(e) {
3672
+ this.cloudsPass.currentMaterial.shapeDetail = e, this.shadowPass.currentMaterial.shapeDetail = e;
3673
+ }
3674
+ get turbulence() {
3675
+ return this.cloudsPass.currentMaterial.turbulence;
3676
+ }
3677
+ set turbulence(e) {
3678
+ this.cloudsPass.currentMaterial.turbulence = e, this.shadowPass.currentMaterial.turbulence = e;
3679
+ }
3680
+ get haze() {
3681
+ return this.cloudsPass.currentMaterial.haze;
3682
+ }
3683
+ set haze(e) {
3684
+ this.cloudsPass.currentMaterial.haze = e;
3685
+ }
3686
+ // Cloud parameter primitives
3687
+ get scatteringCoefficient() {
3688
+ return this.parameterUniforms.scatteringCoefficient.value;
3689
+ }
3690
+ set scatteringCoefficient(e) {
3691
+ this.parameterUniforms.scatteringCoefficient.value = e;
3692
+ }
3693
+ get absorptionCoefficient() {
3694
+ return this.parameterUniforms.absorptionCoefficient.value;
3695
+ }
3696
+ set absorptionCoefficient(e) {
3697
+ this.parameterUniforms.absorptionCoefficient.value = e;
3698
+ }
3699
+ get coverage() {
3700
+ return this.parameterUniforms.coverage.value;
3701
+ }
3702
+ set coverage(e) {
3703
+ this.parameterUniforms.coverage.value = e;
3704
+ }
3705
+ get turbulenceDisplacement() {
3706
+ return this.parameterUniforms.turbulenceDisplacement.value;
3707
+ }
3708
+ set turbulenceDisplacement(e) {
3709
+ this.parameterUniforms.turbulenceDisplacement.value = e;
3710
+ }
3711
+ // Scattering parameters
3712
+ get scatterAnisotropy1() {
3713
+ return this.cloudsPass.currentMaterial.scatterAnisotropy1;
3714
+ }
3715
+ set scatterAnisotropy1(e) {
3716
+ this.cloudsPass.currentMaterial.scatterAnisotropy1 = e;
3717
+ }
3718
+ get scatterAnisotropy2() {
3719
+ return this.cloudsPass.currentMaterial.scatterAnisotropy2;
3720
+ }
3721
+ set scatterAnisotropy2(e) {
3722
+ this.cloudsPass.currentMaterial.scatterAnisotropy2 = e;
3723
+ }
3724
+ get scatterAnisotropyMix() {
3725
+ return this.cloudsPass.currentMaterial.scatterAnisotropyMix;
3726
+ }
3727
+ set scatterAnisotropyMix(e) {
3728
+ this.cloudsPass.currentMaterial.scatterAnisotropyMix = e;
3729
+ }
3730
+ get skyIrradianceScale() {
3731
+ return this.cloudsPass.currentMaterial.uniforms.skyIrradianceScale.value;
3732
+ }
3733
+ set skyIrradianceScale(e) {
3734
+ this.cloudsPass.currentMaterial.uniforms.skyIrradianceScale.value = e;
3735
+ }
3736
+ get groundIrradianceScale() {
3737
+ return this.cloudsPass.currentMaterial.uniforms.groundIrradianceScale.value;
3738
+ }
3739
+ set groundIrradianceScale(e) {
3740
+ this.cloudsPass.currentMaterial.uniforms.groundIrradianceScale.value = e;
3741
+ }
3742
+ get powderScale() {
3743
+ return this.cloudsPass.currentMaterial.uniforms.powderScale.value;
3744
+ }
3745
+ set powderScale(e) {
3746
+ this.cloudsPass.currentMaterial.uniforms.powderScale.value = e;
3747
+ }
3748
+ get powderExponent() {
3749
+ return this.cloudsPass.currentMaterial.uniforms.powderExponent.value;
3750
+ }
3751
+ set powderExponent(e) {
3752
+ this.cloudsPass.currentMaterial.uniforms.powderExponent.value = e;
3753
+ }
3754
+ // Atmosphere composition
3755
+ get atmosphereOverlay() {
3756
+ return this._atmosphereOverlay;
3757
+ }
3758
+ get atmosphereShadow() {
3759
+ return this._atmosphereShadow;
3760
+ }
3761
+ get atmosphereShadowLength() {
3762
+ return this._atmosphereShadowLength;
3763
+ }
3764
+ // Atmosphere parameters
3765
+ get irradianceTexture() {
3766
+ return this.cloudsPass.currentMaterial.irradianceTexture;
3767
+ }
3768
+ set irradianceTexture(e) {
3769
+ this.cloudsPass.currentMaterial.irradianceTexture = e;
3770
+ }
3771
+ get scatteringTexture() {
3772
+ return this.cloudsPass.currentMaterial.scatteringTexture;
3773
+ }
3774
+ set scatteringTexture(e) {
3775
+ this.cloudsPass.currentMaterial.scatteringTexture = e;
3776
+ }
3777
+ get transmittanceTexture() {
3778
+ return this.cloudsPass.currentMaterial.transmittanceTexture;
3779
+ }
3780
+ set transmittanceTexture(e) {
3781
+ this.cloudsPass.currentMaterial.transmittanceTexture = e;
3782
+ }
3783
+ get useHalfFloat() {
3784
+ return this.cloudsPass.currentMaterial.useHalfFloat;
3785
+ }
3786
+ set useHalfFloat(e) {
3787
+ this.cloudsPass.currentMaterial.useHalfFloat = e;
3788
+ }
3789
+ get ellipsoid() {
3790
+ return this.cloudsPass.currentMaterial.ellipsoid;
3791
+ }
3792
+ set ellipsoid(e) {
3793
+ this.cloudsPass.currentMaterial.ellipsoid = e;
3794
+ }
3795
+ get photometric() {
3796
+ return this.cloudsPass.currentMaterial.photometric;
3797
+ }
3798
+ set photometric(e) {
3799
+ this.cloudsPass.currentMaterial.photometric = e;
3800
+ }
3801
+ get sunAngularRadius() {
3802
+ return this.cloudsPass.currentMaterial.sunAngularRadius;
3803
+ }
3804
+ set sunAngularRadius(e) {
3805
+ this.cloudsPass.currentMaterial.sunAngularRadius = e;
3806
+ }
3807
+ }
3808
+ zt([
3809
+ C("SKIP_RENDERING")
3810
+ ], qt.prototype, "skipRendering");
3811
+ const tn = 128, nn = 32, V = "45a1c6c1bb9fd38b3680fd120795ff4c32df68ff", an = `https://media.githubusercontent.com/media/takram-design-engineering/three-geospatial/${V}/packages/clouds/assets/local_weather.png`, rn = `https://media.githubusercontent.com/media/takram-design-engineering/three-geospatial/${V}/packages/clouds/assets/shape.bin`, on = `https://media.githubusercontent.com/media/takram-design-engineering/three-geospatial/${V}/packages/clouds/assets/shape_detail.bin`, sn = `https://media.githubusercontent.com/media/takram-design-engineering/three-geospatial/${V}/packages/clouds/assets/turbulence.png`;
3812
+ export {
3813
+ tn as C,
3814
+ an as D,
3815
+ nn as a,
3816
+ T as b,
3817
+ K as c,
3818
+ Zt as d,
3819
+ qt as e,
3820
+ rn as f,
3821
+ on as g,
3822
+ sn as h,
3823
+ B as i
3824
+ };
3825
+ //# sourceMappingURL=shared.js.map