@takram/three-clouds 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.md +1130 -0
- package/assets/local_weather.png +0 -0
- package/assets/shape.bin +1 -0
- package/assets/shape_detail.bin +1 -0
- package/assets/turbulence.png +0 -0
- package/build/index.cjs +583 -0
- package/build/index.cjs.map +1 -0
- package/build/index.js +728 -0
- package/build/index.js.map +1 -0
- package/build/r3f.cjs +2 -0
- package/build/r3f.cjs.map +1 -0
- package/build/r3f.js +205 -0
- package/build/r3f.js.map +1 -0
- package/build/shared.cjs +2189 -0
- package/build/shared.cjs.map +1 -0
- package/build/shared.js +3825 -0
- package/build/shared.js.map +1 -0
- package/package.json +77 -0
- package/src/CascadedShadowMaps.ts +288 -0
- package/src/CloudLayer.ts +85 -0
- package/src/CloudLayers.test.ts +61 -0
- package/src/CloudLayers.ts +181 -0
- package/src/CloudShape.ts +22 -0
- package/src/CloudShapeDetail.ts +22 -0
- package/src/CloudsEffect.ts +810 -0
- package/src/CloudsMaterial.ts +467 -0
- package/src/CloudsPass.ts +285 -0
- package/src/CloudsResolveMaterial.ts +108 -0
- package/src/DensityProfile.ts +38 -0
- package/src/LocalWeather.ts +21 -0
- package/src/PassBase.ts +28 -0
- package/src/Procedural3DTexture.ts +94 -0
- package/src/ProceduralTexture.ts +94 -0
- package/src/ShaderArrayPass.ts +32 -0
- package/src/ShadowMaterial.ts +141 -0
- package/src/ShadowPass.ts +185 -0
- package/src/ShadowResolveMaterial.ts +72 -0
- package/src/Turbulence.ts +21 -0
- package/src/bayer.ts +23 -0
- package/src/constants.ts +8 -0
- package/src/helpers/FrustumCorners.ts +138 -0
- package/src/helpers/setArrayRenderTargetLayers.ts +32 -0
- package/src/helpers/splitFrustum.ts +59 -0
- package/src/index.ts +14 -0
- package/src/qualityPresets.ts +117 -0
- package/src/r3f/CloudLayer.tsx +95 -0
- package/src/r3f/CloudLayers.tsx +54 -0
- package/src/r3f/Clouds.tsx +278 -0
- package/src/r3f/index.ts +2 -0
- package/src/shaders/catmullRomSampling.glsl +113 -0
- package/src/shaders/cloudShape.frag +78 -0
- package/src/shaders/cloudShapeDetail.frag +56 -0
- package/src/shaders/clouds.frag +996 -0
- package/src/shaders/clouds.glsl +190 -0
- package/src/shaders/clouds.vert +69 -0
- package/src/shaders/cloudsEffect.frag +11 -0
- package/src/shaders/cloudsResolve.frag +202 -0
- package/src/shaders/cloudsResolve.vert +10 -0
- package/src/shaders/localWeather.frag +83 -0
- package/src/shaders/parameters.glsl +64 -0
- package/src/shaders/perlin.glsl +211 -0
- package/src/shaders/shadow.frag +197 -0
- package/src/shaders/shadow.vert +16 -0
- package/src/shaders/shadowResolve.frag +76 -0
- package/src/shaders/shadowResolve.vert +10 -0
- package/src/shaders/structuredSampling.glsl +101 -0
- package/src/shaders/tileableNoise.glsl +88 -0
- package/src/shaders/turbulence.frag +51 -0
- package/src/shaders/types.glsl +18 -0
- package/src/shaders/varianceClipping.glsl +114 -0
- package/src/uniforms.ts +218 -0
- package/types/CascadedShadowMaps.d.ts +52 -0
- package/types/CloudLayer.d.ts +26 -0
- package/types/CloudLayers.d.ts +21 -0
- package/types/CloudShape.d.ts +5 -0
- package/types/CloudShapeDetail.d.ts +5 -0
- package/types/CloudsEffect.d.ts +170 -0
- package/types/CloudsMaterial.d.ts +86 -0
- package/types/CloudsPass.d.ts +44 -0
- package/types/CloudsResolveMaterial.d.ts +30 -0
- package/types/DensityProfile.d.ts +12 -0
- package/types/LocalWeather.d.ts +5 -0
- package/types/PassBase.d.ts +14 -0
- package/types/Procedural3DTexture.d.ts +20 -0
- package/types/ProceduralTexture.d.ts +24 -0
- package/types/ShaderArrayPass.d.ts +7 -0
- package/types/ShadowMaterial.d.ts +34 -0
- package/types/ShadowPass.d.ts +34 -0
- package/types/ShadowResolveMaterial.d.ts +20 -0
- package/types/Turbulence.d.ts +5 -0
- package/types/bayer.d.ts +4 -0
- package/types/constants.d.ts +6 -0
- package/types/helpers/FrustumCorners.d.ts +18 -0
- package/types/helpers/setArrayRenderTargetLayers.d.ts +3 -0
- package/types/helpers/splitFrustum.d.ts +9 -0
- package/types/index.d.ts +13 -0
- package/types/qualityPresets.d.ts +46 -0
- package/types/r3f/CloudLayer.d.ts +7 -0
- package/types/r3f/CloudLayers.d.ts +15 -0
- package/types/r3f/Clouds.d.ts +16 -0
- package/types/r3f/index.d.ts +2 -0
- package/types/uniforms.d.ts +66 -0
package/build/shared.js
ADDED
@@ -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
|