@woosh/meep-engine 2.162.0 → 2.163.1
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/README.md +1 -1
- package/package.json +1 -1
- package/src/core/color/xyz/rgb_to_xyz.d.ts +3 -3
- package/src/core/color/xyz/rgb_to_xyz.d.ts.map +1 -1
- package/src/core/color/xyz/rgb_to_xyz.js +2 -2
- package/src/core/color/xyz/xyz_to_rgb.d.ts +3 -3
- package/src/core/color/xyz/xyz_to_rgb.d.ts.map +1 -1
- package/src/core/color/xyz/xyz_to_rgb.js +2 -2
- package/src/core/geom/vec3/v3_array_scale.d.ts +11 -0
- package/src/core/geom/vec3/v3_array_scale.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_array_scale.js +18 -0
- package/src/engine/EngineHarness.js +3 -3
- package/src/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +218 -202
- package/src/engine/graphics/ecs/path/tube/build/make_cap.d.ts.map +1 -1
- package/src/engine/graphics/ecs/path/tube/build/make_cap.js +11 -2
- package/src/engine/graphics/sh3/path_tracer/make_sky_hosek.d.ts.map +1 -1
- package/src/engine/graphics/sh3/path_tracer/make_sky_hosek.js +45 -44
- package/src/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.d.ts +5 -7
- package/src/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.d.ts.map +1 -1
- package/src/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +388 -395
|
@@ -1,395 +1,388 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
+ w[
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
*
|
|
36
|
-
* @param {number
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
+ w[
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
w[
|
|
72
|
-
w[
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*
|
|
82
|
-
* @param {number[]|Float32Array}
|
|
83
|
-
* @param {number} turbidity
|
|
84
|
-
* @param {number}
|
|
85
|
-
* @param {number}
|
|
86
|
-
* @param {number
|
|
87
|
-
* @param {number}
|
|
88
|
-
* @
|
|
89
|
-
* @
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
FindQuinticWeights(s, quinticWeights);
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
EvalQuintic_3(quinticWeights, dataset9,
|
|
115
|
-
EvalQuintic_3(quinticWeights, dataset9, size_set9_0 +
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* @
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
coeffs
|
|
177
|
-
coeffs_offset
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
* @
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
sun_direction
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
+
|
|
321
|
-
+
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
mCoeffsXYZ
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
out[0] = x * mRadXYZ[0];
|
|
391
|
-
out[1] = y * mRadXYZ[1];
|
|
392
|
-
out[2] = z * mRadXYZ[2];
|
|
393
|
-
|
|
394
|
-
xyz_to_rgb(out, out);
|
|
395
|
-
}
|
|
1
|
+
import { rgb_to_xyz } from "../../../../../core/color/xyz/rgb_to_xyz.js";
|
|
2
|
+
import { xyz_to_rgb } from "../../../../../core/color/xyz/xyz_to_rgb.js";
|
|
3
|
+
import { v3_angle_cos_between } from "../../../../../core/geom/vec3/v3_angle_cos_between.js";
|
|
4
|
+
import { v3_array_scale } from "../../../../../core/geom/vec3/v3_array_scale.js";
|
|
5
|
+
import { v3_length } from "../../../../../core/geom/vec3/v3_length.js";
|
|
6
|
+
import { clamp } from "../../../../../core/math/clamp.js";
|
|
7
|
+
import { clamp01 } from "../../../../../core/math/clamp01.js";
|
|
8
|
+
import { lerp } from "../../../../../core/math/lerp.js";
|
|
9
|
+
import { kHosekCoeffsX, kHosekCoeffsY, kHosekCoeffsZ, kHosekRadX, kHosekRadY, kHosekRadZ } from "./data.js";
|
|
10
|
+
|
|
11
|
+
const HALF_PI = Math.PI * 0.5;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param {number[]|Float32Array} w float[6]
|
|
17
|
+
* @param {number[]|Float32Array} data float[6]
|
|
18
|
+
* @param {number} data_offset
|
|
19
|
+
* @return {number}
|
|
20
|
+
*/
|
|
21
|
+
function EvalQuintic_2(w, data, data_offset) {
|
|
22
|
+
return w[0] * data[data_offset + 0]
|
|
23
|
+
+ w[1] * data[data_offset + 1]
|
|
24
|
+
+ w[2] * data[data_offset + 2]
|
|
25
|
+
+ w[3] * data[data_offset + 3]
|
|
26
|
+
+ w[4] * data[data_offset + 4]
|
|
27
|
+
+ w[5] * data[data_offset + 5];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* @param {number[]|Float32Array} w float[6]
|
|
33
|
+
* @param {number[]|Float32Array} data float[6][9] flattened
|
|
34
|
+
* @param {number} data_offset
|
|
35
|
+
* @param {number[]|Float32Array} coeffs float[9] output is written here
|
|
36
|
+
* @param {number} coeffs_offset
|
|
37
|
+
*/
|
|
38
|
+
function EvalQuintic_3(w, data, data_offset, coeffs, coeffs_offset) {
|
|
39
|
+
for (let i = 0; i < 9; i++) {
|
|
40
|
+
coeffs[coeffs_offset + i] = w[0] * data[data_offset + 0 * 9 + i]
|
|
41
|
+
+ w[1] * data[data_offset + 1 * 9 + i]
|
|
42
|
+
+ w[2] * data[data_offset + 2 * 9 + i]
|
|
43
|
+
+ w[3] * data[data_offset + 3 * 9 + i]
|
|
44
|
+
+ w[4] * data[data_offset + 4 * 9 + i]
|
|
45
|
+
+ w[5] * data[data_offset + 5 * 9 + i];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
*
|
|
51
|
+
* @param {number} s
|
|
52
|
+
* @param {number[]|ArrayLike<number>|Float32Array} w
|
|
53
|
+
*/
|
|
54
|
+
function FindQuinticWeights(s, w) {
|
|
55
|
+
const s1 = s;
|
|
56
|
+
const s2 = s1 * s1;
|
|
57
|
+
const s3 = s1 * s2;
|
|
58
|
+
const s4 = s2 * s2;
|
|
59
|
+
const s5 = s2 * s3;
|
|
60
|
+
|
|
61
|
+
const is1 = 1.0 - s1;
|
|
62
|
+
const is2 = is1 * is1;
|
|
63
|
+
const is3 = is1 * is2;
|
|
64
|
+
const is4 = is2 * is2;
|
|
65
|
+
const is5 = is2 * is3;
|
|
66
|
+
|
|
67
|
+
w[0] = is5;
|
|
68
|
+
w[1] = is4 * s1 * 5.0;
|
|
69
|
+
w[2] = is3 * s2 * 10.0;
|
|
70
|
+
w[3] = is2 * s3 * 10.0;
|
|
71
|
+
w[4] = is1 * s4 * 5.0;
|
|
72
|
+
w[5] = s5;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Reusable scratch buffers for FindHosekCoeffs. Safe to share: evaluation is
|
|
76
|
+
// synchronous and non-reentrant, and each call fully overwrites them.
|
|
77
|
+
const quinticWeights = new Float32Array(6);
|
|
78
|
+
const ic = new Float32Array(4 * 9);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
*
|
|
82
|
+
* @param {number[]|Float32Array} dataset9 albedo x 2, turbidity x 10, quintics x 6, weights x 9
|
|
83
|
+
* @param {number[]|Float32Array} datasetR albedo x 2, turbidity x 10, quintics x 6
|
|
84
|
+
* @param {number} turbidity
|
|
85
|
+
* @param {number} albedo
|
|
86
|
+
* @param {number} solarElevation
|
|
87
|
+
* @param {number[]|Float32Array} coeffs result is written here
|
|
88
|
+
* @param {number} coeffs_offset
|
|
89
|
+
* @return {number}
|
|
90
|
+
* @constructor
|
|
91
|
+
*/
|
|
92
|
+
function FindHosekCoeffs
|
|
93
|
+
(
|
|
94
|
+
dataset9,
|
|
95
|
+
datasetR,
|
|
96
|
+
turbidity,
|
|
97
|
+
albedo,
|
|
98
|
+
solarElevation,
|
|
99
|
+
coeffs,
|
|
100
|
+
coeffs_offset
|
|
101
|
+
) {
|
|
102
|
+
const tbi = clamp(Math.floor(turbidity), 1, 9);
|
|
103
|
+
|
|
104
|
+
const tbf = turbidity - tbi;
|
|
105
|
+
|
|
106
|
+
const s = Math.cbrt(solarElevation / HALF_PI);
|
|
107
|
+
|
|
108
|
+
FindQuinticWeights(s, quinticWeights);
|
|
109
|
+
|
|
110
|
+
const size_set9_1 = 6 * 9;
|
|
111
|
+
const size_set9_0 = 10 * size_set9_1;
|
|
112
|
+
EvalQuintic_3(quinticWeights, dataset9, (tbi - 1) * size_set9_1, ic, 0);
|
|
113
|
+
EvalQuintic_3(quinticWeights, dataset9, size_set9_0 + (tbi - 1) * size_set9_1, ic, 9);
|
|
114
|
+
EvalQuintic_3(quinticWeights, dataset9, tbi * size_set9_1, ic, 18);
|
|
115
|
+
EvalQuintic_3(quinticWeights, dataset9, size_set9_0 + tbi * size_set9_1, ic, 27);
|
|
116
|
+
|
|
117
|
+
const size_set_R_1 = 6;
|
|
118
|
+
const size_set_R_0 = 10 * size_set_R_1;
|
|
119
|
+
const ir_0 = EvalQuintic_2(quinticWeights, datasetR, (tbi - 1) * size_set_R_1);
|
|
120
|
+
const ir_1 = EvalQuintic_2(quinticWeights, datasetR, size_set_R_0 + (tbi - 1) * size_set_R_1);
|
|
121
|
+
const ir_2 = EvalQuintic_2(quinticWeights, datasetR, tbi * size_set_R_1);
|
|
122
|
+
const ir_3 = EvalQuintic_2(quinticWeights, datasetR, size_set_R_0 + tbi * size_set_R_1);
|
|
123
|
+
|
|
124
|
+
const cw_0 = (1.0 - albedo) * (1.0 - tbf);
|
|
125
|
+
const cw_1 = albedo * (1.0 - tbf);
|
|
126
|
+
const cw_2 = (1.0 - albedo) * tbf;
|
|
127
|
+
const cw_3 = albedo * tbf;
|
|
128
|
+
|
|
129
|
+
for (let i = 0; i < 9; i++) {
|
|
130
|
+
coeffs[coeffs_offset + i] = cw_0 * ic[i]
|
|
131
|
+
+ cw_1 * ic[9 + i]
|
|
132
|
+
+ cw_2 * ic[2 * 9 + i]
|
|
133
|
+
+ cw_3 * ic[3 * 9 + i];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return cw_0 * ir_0 + cw_1 * ir_1 + cw_2 * ir_2 + cw_3 * ir_3;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
*
|
|
141
|
+
* Hosek:
|
|
142
|
+
* (1 + A e ^ (B / cos(t))) (1 + C e ^ (D g) + E cos(g) ^ 2 + F mieM(g, G) + H cos(t)^1/2 + (I - 1))
|
|
143
|
+
*
|
|
144
|
+
* These bits are the same as Preetham, but do different jobs in some cases
|
|
145
|
+
* A: sky gradient, carries white -> blue gradient
|
|
146
|
+
* B: sky tightness
|
|
147
|
+
* C: sun, carries most of sun-centred blue term
|
|
148
|
+
* D: sun tightness, higher = tighter
|
|
149
|
+
* E: rosy hue around sun
|
|
150
|
+
*
|
|
151
|
+
* Hosek-specific:
|
|
152
|
+
* F: mie term, does most of the heavy lifting for sunset glow
|
|
153
|
+
* G: mie tuning
|
|
154
|
+
* H: zenith gradient
|
|
155
|
+
* I: constant term balanced with H
|
|
156
|
+
*
|
|
157
|
+
* Notes:
|
|
158
|
+
* A/B still carries some of the "blue" base of sky, but much comes from C/D
|
|
159
|
+
* C/E minimal effect in sunset situations, carry bulk of sun halo in sun-overhead
|
|
160
|
+
* F/G sunset glow, but also takes sun halo from yellowish to white overhead
|
|
161
|
+
* @param {number[]|Float32Array} coeffs
|
|
162
|
+
* @param {number} coeffs_offset
|
|
163
|
+
* @param {number} cosTheta
|
|
164
|
+
* @param {number} gamma
|
|
165
|
+
* @param {number} cosGamma
|
|
166
|
+
* @return {number}
|
|
167
|
+
* @constructor
|
|
168
|
+
*/
|
|
169
|
+
function EvalHosekCoeffs(
|
|
170
|
+
coeffs,
|
|
171
|
+
coeffs_offset,
|
|
172
|
+
cosTheta,
|
|
173
|
+
gamma,
|
|
174
|
+
cosGamma
|
|
175
|
+
) {
|
|
176
|
+
const c_0 = coeffs[coeffs_offset + 0];
|
|
177
|
+
const c_1 = coeffs[coeffs_offset + 1];
|
|
178
|
+
const c_2 = coeffs[coeffs_offset + 2];
|
|
179
|
+
const c_3 = coeffs[coeffs_offset + 3];
|
|
180
|
+
const c_4 = coeffs[coeffs_offset + 4];
|
|
181
|
+
const c_5 = coeffs[coeffs_offset + 5];
|
|
182
|
+
const c_6 = coeffs[coeffs_offset + 6];
|
|
183
|
+
const c_7 = coeffs[coeffs_offset + 7];
|
|
184
|
+
const c_8 = coeffs[coeffs_offset + 8];
|
|
185
|
+
|
|
186
|
+
// Current coeffs ordering is AB I CDEF HG
|
|
187
|
+
// 01 2 3456 78
|
|
188
|
+
const expM = Math.exp(c_4 * gamma); // D g
|
|
189
|
+
const rayM = cosGamma * cosGamma; // Rayleigh scattering
|
|
190
|
+
const mieM = (1.0 + rayM) / Math.pow((1.0 + c_8 * c_8 - 2.0 * c_8 * cosGamma), 1.5); // G
|
|
191
|
+
const zenith = Math.sqrt(cosTheta); // vertical zenith gradient
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
1.0 + c_0 * Math.exp(c_1 / (cosTheta + 0.01)) // A, B
|
|
195
|
+
)
|
|
196
|
+
* (1.0
|
|
197
|
+
+ c_3 * expM // C
|
|
198
|
+
+ c_5 * rayM // E
|
|
199
|
+
+ c_6 * mieM // F
|
|
200
|
+
+ c_7 * zenith // H
|
|
201
|
+
+ (c_2 - 1.0) // I
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
*
|
|
207
|
+
* @param {number} thetaS
|
|
208
|
+
* @param {number} T
|
|
209
|
+
* @return {number}
|
|
210
|
+
*/
|
|
211
|
+
function ZenithLuminance(thetaS, T) {
|
|
212
|
+
const chi = (4.0 / 9.0 - T / 120.0) * (Math.PI - 2.0 * thetaS);
|
|
213
|
+
let Lz = (4.0453 * T - 4.9710) * Math.tan(chi) - 0.2155 * T + 2.4192;
|
|
214
|
+
Lz *= 1000.0; // conversion from kcd/m^2 to cd/m^2
|
|
215
|
+
return Lz;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @see https://github.com/andrewwillmott/sun-sky/blob/master/SunSky.cpp
|
|
220
|
+
* @param {number[]} mCoeffsXYZ output, float[3][9], Hosek 9-term distribution coefficients
|
|
221
|
+
* @param {number[]} mRadXYZ output, vec3, Overall average radiance
|
|
222
|
+
* @param {number[]|Float32Array} sun_direction direction to the sun, engine frame (+Y up)
|
|
223
|
+
* @param {number} turbidity should be between 1 and 10
|
|
224
|
+
* @param {number[]|Float32Array} rgbAlbedo albedo in linear color space (make sure it's not sRGB)
|
|
225
|
+
* @param {number} overcast
|
|
226
|
+
*/
|
|
227
|
+
export function sky_hosek_precompute(
|
|
228
|
+
mCoeffsXYZ,
|
|
229
|
+
mRadXYZ,
|
|
230
|
+
sun_direction,
|
|
231
|
+
turbidity,
|
|
232
|
+
rgbAlbedo,
|
|
233
|
+
overcast
|
|
234
|
+
) {
|
|
235
|
+
|
|
236
|
+
const mToSun = sun_direction;
|
|
237
|
+
|
|
238
|
+
const sun_x = mToSun[0];
|
|
239
|
+
const sun_y = mToSun[1];
|
|
240
|
+
const sun_z = mToSun[2];
|
|
241
|
+
|
|
242
|
+
// Y is up (engine convention): solar elevation is measured from the Y component.
|
|
243
|
+
const solarElevation = sun_y > 0 ? Math.asin(sun_y) : 0;
|
|
244
|
+
|
|
245
|
+
const albedo = [];
|
|
246
|
+
rgb_to_xyz(albedo, rgbAlbedo);
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
// Note that the hosek coefficients change with time of day, vs. Preetham where the 'upper' coefficients stay the same,
|
|
250
|
+
// and only the scaler mPerezInvDen, consisting of time-dependent normalisation and zenith luminnce factors, changes.
|
|
251
|
+
mRadXYZ[0] = FindHosekCoeffs(kHosekCoeffsX, kHosekRadX, turbidity, albedo[0], solarElevation, mCoeffsXYZ, 0);
|
|
252
|
+
mRadXYZ[1] = FindHosekCoeffs(kHosekCoeffsY, kHosekRadY, turbidity, albedo[1], solarElevation, mCoeffsXYZ, 9);
|
|
253
|
+
mRadXYZ[2] = FindHosekCoeffs(kHosekCoeffsZ, kHosekRadZ, turbidity, albedo[2], solarElevation, mCoeffsXYZ, 18);
|
|
254
|
+
|
|
255
|
+
v3_array_scale(mRadXYZ, 0, mRadXYZ, 0, 683); // convert to luminance in lumens
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
if (sun_y < 0.0) // sun below horizon?
|
|
259
|
+
{
|
|
260
|
+
const s = clamp01(1.0 + sun_y * 50.0); // goes from 1 to 0 as the sun sets
|
|
261
|
+
const is = 1.0 - s;
|
|
262
|
+
|
|
263
|
+
// Emulate Preetham's zenith darkening
|
|
264
|
+
const darken = ZenithLuminance(Math.acos(sun_y), turbidity) / ZenithLuminance(HALF_PI, turbidity);
|
|
265
|
+
|
|
266
|
+
// Take C/E/F which control sun term to zero
|
|
267
|
+
for (let j = 0; j < 3; j++) {
|
|
268
|
+
mCoeffsXYZ[j * 9 + 3] *= s;
|
|
269
|
+
mCoeffsXYZ[j * 9 + 5] *= s;
|
|
270
|
+
mCoeffsXYZ[j * 9 + 6] *= s;
|
|
271
|
+
|
|
272
|
+
// Take horizon term H to zero, as it's an orange glow at this point
|
|
273
|
+
mCoeffsXYZ[j * 9 + 7] *= s;
|
|
274
|
+
|
|
275
|
+
// Take I term back to 1
|
|
276
|
+
mCoeffsXYZ[j * 9 + 2] *= s;
|
|
277
|
+
mCoeffsXYZ[j * 9 + 2] += is;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
v3_array_scale(mRadXYZ, 0, mRadXYZ, 0, darken);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (overcast !== 0.0) // Handle overcast term
|
|
284
|
+
{
|
|
285
|
+
const is = overcast;
|
|
286
|
+
const s = 1.0 - overcast; // goes to 0 as we go to overcast
|
|
287
|
+
|
|
288
|
+
// Hosek isn't self-normalising, unlike Preetham/CIE, which divides by PreethamLower().
|
|
289
|
+
// Thus when we lerp to the CIE overcast model, we get some non-linearities.
|
|
290
|
+
// We deal with this by using ratios of normalisation terms to balance.
|
|
291
|
+
// Another difference is that Hosek is relative to the average radiance,
|
|
292
|
+
// whereas CIE is the zenith radiance, so rather than taking the zenith
|
|
293
|
+
// as normalising as in CIE, we average over the zenith and two horizon
|
|
294
|
+
// points.
|
|
295
|
+
const cosGammaZ = sun_y;
|
|
296
|
+
const gammaZ = Math.acos(cosGammaZ);
|
|
297
|
+
const cosGammaH = sun_z;
|
|
298
|
+
const gammaHP = Math.acos(sun_z);
|
|
299
|
+
const gammaHN = Math.PI - gammaHP;
|
|
300
|
+
|
|
301
|
+
const sc0 = EvalHosekCoeffs(mCoeffsXYZ, 1 * 9, 1.0, gammaZ, cosGammaZ) * 2.0
|
|
302
|
+
+ EvalHosekCoeffs(mCoeffsXYZ, 1 * 9, 0.0, gammaHP, +cosGammaH)
|
|
303
|
+
+ EvalHosekCoeffs(mCoeffsXYZ, 1 * 9, 0.0, gammaHN, -cosGammaH);
|
|
304
|
+
|
|
305
|
+
for (let j = 0; j < 3; j++) {
|
|
306
|
+
// sun flare -> 0 strength/base chroma
|
|
307
|
+
// Take C/E/F which control sun term to zero
|
|
308
|
+
mCoeffsXYZ[j * 9 + 3] *= s;
|
|
309
|
+
mCoeffsXYZ[j * 9 + 5] *= s;
|
|
310
|
+
mCoeffsXYZ[j * 9 + 6] *= s;
|
|
311
|
+
|
|
312
|
+
// Take H back to 0
|
|
313
|
+
mCoeffsXYZ[j * 9 + 7] *= s;
|
|
314
|
+
|
|
315
|
+
// Take I term back to 1
|
|
316
|
+
mCoeffsXYZ[j * 9 + 2] *= s;
|
|
317
|
+
mCoeffsXYZ[j * 9 + 2] += is;
|
|
318
|
+
|
|
319
|
+
// Take A/B to CIE cloudy sky model: 4, -0.7
|
|
320
|
+
mCoeffsXYZ[j * 9 + 0] = lerp(mCoeffsXYZ[j * 9 + 0], 4.0, is);
|
|
321
|
+
mCoeffsXYZ[j * 9 + 1] = lerp(mCoeffsXYZ[j * 9 + 1], -0.7, is);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const sc1 = EvalHosekCoeffs(mCoeffsXYZ, 1 * 9, 1.0, gammaZ, cosGammaZ) * 2.0
|
|
325
|
+
+ EvalHosekCoeffs(mCoeffsXYZ, 1 * 9, 0.0, gammaHP, +cosGammaH)
|
|
326
|
+
+ EvalHosekCoeffs(mCoeffsXYZ, 1 * 9, 0.0, gammaHN, -cosGammaH);
|
|
327
|
+
|
|
328
|
+
const rescale = sc0 / sc1;
|
|
329
|
+
v3_array_scale(mRadXYZ, 0, mRadXYZ, 0, rescale);
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
// move back to white point
|
|
333
|
+
mRadXYZ[0] = lerp(mRadXYZ[0], mRadXYZ[1], is);
|
|
334
|
+
mRadXYZ[2] = lerp(mRadXYZ[2], mRadXYZ[1], is);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
*
|
|
342
|
+
* @param {number[]|Float32Array} out vec3 in RGB color space
|
|
343
|
+
* @param {number[]|Float32Array} mCoeffsXYZ float[3][9]
|
|
344
|
+
* @param {number[]|Float32Array} mRadXYZ vec3
|
|
345
|
+
* @param {number[]|Float32Array} mToSun vec3, direction to the sun, engine frame (+Y up)
|
|
346
|
+
* @param {number} direction_x
|
|
347
|
+
* @param {number} direction_y up axis (engine +Y up)
|
|
348
|
+
* @param {number} direction_z
|
|
349
|
+
*/
|
|
350
|
+
export function sky_hosek_compute_irradiance_by_direction(
|
|
351
|
+
out,
|
|
352
|
+
mCoeffsXYZ,
|
|
353
|
+
mRadXYZ,
|
|
354
|
+
mToSun,
|
|
355
|
+
direction_x,
|
|
356
|
+
direction_y,
|
|
357
|
+
direction_z,
|
|
358
|
+
) {
|
|
359
|
+
// Normalise the view direction so cosTheta stays a true cosine even when the
|
|
360
|
+
// caller passes a non-unit direction.
|
|
361
|
+
const direction_length = v3_length(direction_x, direction_y, direction_z);
|
|
362
|
+
const inv_direction_length = direction_length !== 0.0 ? 1.0 / direction_length : 0.0;
|
|
363
|
+
|
|
364
|
+
let cosTheta = direction_y * inv_direction_length;
|
|
365
|
+
|
|
366
|
+
// v3_angle_cos_between divides by both magnitudes and clamps to [-1, 1], so it
|
|
367
|
+
// is robust to non-unit inputs and gamma = acos(cosGamma) can never be NaN.
|
|
368
|
+
const cosGamma = v3_angle_cos_between(
|
|
369
|
+
mToSun[0], mToSun[1], mToSun[2],
|
|
370
|
+
direction_x, direction_y, direction_z
|
|
371
|
+
);
|
|
372
|
+
const gamma = Math.acos(cosGamma);
|
|
373
|
+
|
|
374
|
+
if (cosTheta < 0.0) {
|
|
375
|
+
// clamp angle
|
|
376
|
+
cosTheta = 0.0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const x = EvalHosekCoeffs(mCoeffsXYZ, 0, cosTheta, gamma, cosGamma);
|
|
380
|
+
const y = EvalHosekCoeffs(mCoeffsXYZ, 9, cosTheta, gamma, cosGamma);
|
|
381
|
+
const z = EvalHosekCoeffs(mCoeffsXYZ, 18, cosTheta, gamma, cosGamma);
|
|
382
|
+
|
|
383
|
+
out[0] = x * mRadXYZ[0];
|
|
384
|
+
out[1] = y * mRadXYZ[1];
|
|
385
|
+
out[2] = z * mRadXYZ[2];
|
|
386
|
+
|
|
387
|
+
xyz_to_rgb(out, out);
|
|
388
|
+
}
|