@woosh/meep-engine 3.64.0 → 3.64.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/package.json +1 -1
- package/src/engine/graphics/texture/sampler/exr_to_sampler2d.d.ts +27 -0
- package/src/engine/graphics/texture/sampler/exr_to_sampler2d.d.ts.map +1 -0
- package/src/engine/graphics/texture/sampler/exr_to_sampler2d.js +91 -0
- package/src/engine/graphics/texture/sampler/sampler2d_to_exr.d.ts +23 -0
- package/src/engine/graphics/texture/sampler/sampler2d_to_exr.d.ts.map +1 -0
- package/src/engine/graphics/texture/sampler/sampler2d_to_exr.js +64 -0
- package/src/format/image/exr/ExrCompression.d.ts +22 -0
- package/src/format/image/exr/ExrCompression.d.ts.map +1 -0
- package/src/format/image/exr/ExrCompression.js +27 -0
- package/src/format/image/exr/ExrImage.d.ts +32 -0
- package/src/format/image/exr/ExrImage.d.ts.map +1 -0
- package/src/format/image/exr/ExrImage.js +32 -0
- package/src/format/image/exr/ExrPixelType.d.ts +13 -0
- package/src/format/image/exr/ExrPixelType.d.ts.map +1 -0
- package/src/format/image/exr/ExrPixelType.js +18 -0
- package/src/format/image/exr/exr_compare_names.d.ts +16 -0
- package/src/format/image/exr/exr_compare_names.d.ts.map +1 -0
- package/src/format/image/exr/exr_compare_names.js +25 -0
- package/src/format/image/exr/exr_constants.d.ts +49 -0
- package/src/format/image/exr/exr_constants.d.ts.map +1 -0
- package/src/format/image/exr/exr_constants.js +56 -0
- package/src/format/image/exr/exr_read.d.ts +32 -0
- package/src/format/image/exr/exr_read.d.ts.map +1 -0
- package/src/format/image/exr/exr_read.js +531 -0
- package/src/format/image/exr/exr_sample_bits.d.ts +16 -0
- package/src/format/image/exr/exr_sample_bits.d.ts.map +1 -0
- package/src/format/image/exr/exr_sample_bits.js +21 -0
- package/src/format/image/exr/exr_scanlines_per_chunk.d.ts +17 -0
- package/src/format/image/exr/exr_scanlines_per_chunk.d.ts.map +1 -0
- package/src/format/image/exr/exr_scanlines_per_chunk.js +31 -0
- package/src/format/image/exr/exr_write.d.ts +27 -0
- package/src/format/image/exr/exr_write.d.ts.map +1 -0
- package/src/format/image/exr/exr_write.js +378 -0
- package/src/shade/playground/probe_clipmap/index.html +5 -0
- package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
- package/src/shade/playground/probe_clipmap/main.js +34 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +3 -15
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +4 -17
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +3 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +15 -13
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +7 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +28 -17
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts +18 -18
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.js +44 -48
- package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts +8 -0
- package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.js +21 -3
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
|
|
2
|
+
import { compress_bytes } from "../../../core/binary/compression/compress_bytes.js";
|
|
3
|
+
import { EndianType } from "../../../core/binary/EndianType.js";
|
|
4
|
+
import { utf8_encode_into } from "../../../core/binary/utf8/utf8_encode_into.js";
|
|
5
|
+
import { utf8_encoded_length } from "../../../core/binary/utf8/utf8_encoded_length.js";
|
|
6
|
+
import {
|
|
7
|
+
EXR_FLAG_LONG_NAMES,
|
|
8
|
+
EXR_LONG_NAME_BYTES,
|
|
9
|
+
EXR_MAGIC,
|
|
10
|
+
EXR_SHORT_NAME_BYTES,
|
|
11
|
+
EXR_VERSION
|
|
12
|
+
} from "./exr_constants.js";
|
|
13
|
+
import { exr_compare_names } from "./exr_compare_names.js";
|
|
14
|
+
import { exr_sample_bits } from "./exr_sample_bits.js";
|
|
15
|
+
import { exr_scanlines_per_chunk } from "./exr_scanlines_per_chunk.js";
|
|
16
|
+
import { ExrCompression } from "./ExrCompression.js";
|
|
17
|
+
import { ExrPixelType } from "./ExrPixelType.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The largest value of the format's signed 32-bit fields: a chunk's size, and the data window's corners.
|
|
21
|
+
*
|
|
22
|
+
* @readonly
|
|
23
|
+
* @type {number}
|
|
24
|
+
*/
|
|
25
|
+
const INT32_MAX = 0x7FFFFFFF;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Write an image as a scanline OpenEXR file.
|
|
29
|
+
*
|
|
30
|
+
* Each channel is written in the pixel type its array holds — a `Float16Array` as HALF, a `Float32Array`
|
|
31
|
+
* as FLOAT, a `Uint32Array` as UINT — and its samples go into the file as the bits they are. The header
|
|
32
|
+
* carries OpenEXR's required attributes and nothing else: both windows at the origin exactly covering the
|
|
33
|
+
* pixels, increasing line order, square pixels and the default screen window.
|
|
34
|
+
*
|
|
35
|
+
* Under {@link ExrCompression.ZIP} and {@link ExrCompression.ZIPS} a chunk that deflate does not make
|
|
36
|
+
* smaller is stored as it is, as OpenEXR's own writer does and every reader expects.
|
|
37
|
+
*
|
|
38
|
+
* The channels' arrays are read chunk by chunk as earlier chunks are compressed, so keep them unchanged
|
|
39
|
+
* until the promise settles.
|
|
40
|
+
*
|
|
41
|
+
* @param {ExrImage} image channels in any order, with distinct names of 1 to 255 bytes in UTF-8
|
|
42
|
+
* @param {ExrCompression} compression {@link ExrCompression.None}, {@link ExrCompression.ZIPS} or
|
|
43
|
+
* {@link ExrCompression.ZIP}
|
|
44
|
+
* @returns {Promise<ArrayBuffer>} the whole file
|
|
45
|
+
* @throws {Error} naming what about the image or the compression method cannot be written
|
|
46
|
+
*
|
|
47
|
+
* @see https://openexr.com/en/latest/OpenEXRFileLayout.html
|
|
48
|
+
* @author Alex Goldring
|
|
49
|
+
* @copyright Company Named Limited (c) 2026
|
|
50
|
+
*/
|
|
51
|
+
export async function exr_write(image, compression) {
|
|
52
|
+
const width = image.width;
|
|
53
|
+
const height = image.height;
|
|
54
|
+
|
|
55
|
+
// the data window's corners are signed 32-bit integers
|
|
56
|
+
if (!Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1
|
|
57
|
+
|| width - 1 > INT32_MAX || height - 1 > INT32_MAX) {
|
|
58
|
+
throw new Error(`EXR: cannot write a ${width}x${height} image; a side is 1 to 2^31 pixels`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const scanlines_per_chunk = exr_scanlines_per_chunk(compression);
|
|
62
|
+
|
|
63
|
+
const channels = sort_channels(image.channels, width * height);
|
|
64
|
+
const channel_count = channels.length;
|
|
65
|
+
|
|
66
|
+
let pixel_bytes = 0;
|
|
67
|
+
let long_names = false;
|
|
68
|
+
|
|
69
|
+
const plane_bits = new Array(channel_count);
|
|
70
|
+
|
|
71
|
+
for (let c = 0; c < channel_count; c++) {
|
|
72
|
+
const channel = channels[c];
|
|
73
|
+
|
|
74
|
+
pixel_bytes += channel.data.BYTES_PER_ELEMENT;
|
|
75
|
+
long_names ||= channel.name_bytes.length > EXR_SHORT_NAME_BYTES;
|
|
76
|
+
|
|
77
|
+
plane_bits[c] = exr_sample_bits(channel.data);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const line_bytes = width * pixel_bytes;
|
|
81
|
+
const chunk_bytes = Math.min(scanlines_per_chunk, height) * line_bytes;
|
|
82
|
+
|
|
83
|
+
if (chunk_bytes > INT32_MAX) {
|
|
84
|
+
throw new Error(`EXR: a chunk of this image would be ${chunk_bytes} bytes, more than a chunk's 32-bit size holds`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const chunk_count = Math.ceil(height / scanlines_per_chunk);
|
|
88
|
+
|
|
89
|
+
const buffer = new BinaryBuffer();
|
|
90
|
+
|
|
91
|
+
// OpenEXR is little-endian throughout
|
|
92
|
+
buffer.endianness = EndianType.LittleEndian;
|
|
93
|
+
|
|
94
|
+
buffer.writeUint32(EXR_MAGIC);
|
|
95
|
+
buffer.writeUint32(long_names ? EXR_VERSION | EXR_FLAG_LONG_NAMES : EXR_VERSION);
|
|
96
|
+
|
|
97
|
+
write_header(buffer, channels, compression, width, height);
|
|
98
|
+
|
|
99
|
+
// No chunk is stored larger than its scanlines, so this bounds the file: the output is allocated
|
|
100
|
+
// once, and uncompressed it is exactly the file.
|
|
101
|
+
buffer.setCapacity(buffer.position + chunk_count * 16 + height * line_bytes);
|
|
102
|
+
|
|
103
|
+
// each entry is known once its chunk has been written; reserve the table and fill it in as they are
|
|
104
|
+
const table_offset = buffer.position;
|
|
105
|
+
|
|
106
|
+
for (let i = 0; i < chunk_count; i++) {
|
|
107
|
+
buffer.writeUint64(0n);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const raw = new BinaryBuffer();
|
|
111
|
+
|
|
112
|
+
raw.endianness = EndianType.LittleEndian;
|
|
113
|
+
raw.setCapacity(chunk_bytes);
|
|
114
|
+
|
|
115
|
+
const prepared = compression === ExrCompression.None ? null : new Uint8Array(chunk_bytes);
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < chunk_count; i++) {
|
|
118
|
+
const first_line = i * scanlines_per_chunk;
|
|
119
|
+
const line_count = Math.min(scanlines_per_chunk, height - first_line);
|
|
120
|
+
const unpacked_size = line_count * line_bytes;
|
|
121
|
+
|
|
122
|
+
raw.position = 0;
|
|
123
|
+
|
|
124
|
+
write_scanlines(raw, plane_bits, width, first_line, line_count);
|
|
125
|
+
|
|
126
|
+
let payload = raw.raw_bytes;
|
|
127
|
+
let payload_size = unpacked_size;
|
|
128
|
+
|
|
129
|
+
if (prepared !== null) {
|
|
130
|
+
zip_prepare(raw.raw_bytes, prepared, unpacked_size);
|
|
131
|
+
|
|
132
|
+
const compressed = await compress_bytes([prepared.subarray(0, unpacked_size)], "deflate");
|
|
133
|
+
|
|
134
|
+
if (compressed.length < unpacked_size) {
|
|
135
|
+
payload = compressed;
|
|
136
|
+
payload_size = compressed.length;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const offset = buffer.position;
|
|
141
|
+
|
|
142
|
+
buffer.position = table_offset + i * 8;
|
|
143
|
+
buffer.writeUint64(BigInt(offset));
|
|
144
|
+
buffer.position = offset;
|
|
145
|
+
|
|
146
|
+
// the data window starts at the origin, so a chunk's scanline is its first row
|
|
147
|
+
buffer.writeInt32(first_line);
|
|
148
|
+
buffer.writeInt32(payload_size);
|
|
149
|
+
buffer.writeBytes(payload, 0, payload_size);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
buffer.trim();
|
|
153
|
+
|
|
154
|
+
return buffer.data;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Check the channels and sort them by {@link exr_compare_names}, which is the order a file lists them in
|
|
159
|
+
* and lays out their samples in.
|
|
160
|
+
*
|
|
161
|
+
* @param {ExrChannel[]} channels
|
|
162
|
+
* @param {number} sample_count every channel's
|
|
163
|
+
* @returns {{name: string, name_bytes: Uint8Array, data: (Float16Array|Float32Array|Uint32Array), pixel_type: ExrPixelType}[]}
|
|
164
|
+
*/
|
|
165
|
+
function sort_channels(channels, sample_count) {
|
|
166
|
+
const channel_count = channels.length;
|
|
167
|
+
|
|
168
|
+
if (channel_count === 0) {
|
|
169
|
+
throw new Error("EXR: the image has no channels");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const result = new Array(channel_count);
|
|
173
|
+
|
|
174
|
+
for (let c = 0; c < channel_count; c++) {
|
|
175
|
+
const { name, data } = channels[c];
|
|
176
|
+
|
|
177
|
+
if (typeof name !== "string" || name.length === 0 || name.includes("\0")) {
|
|
178
|
+
throw new Error(`EXR: channel name '${name}' is not a non-empty string without a null`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const name_bytes = new Uint8Array(utf8_encoded_length(name));
|
|
182
|
+
|
|
183
|
+
utf8_encode_into(name, name_bytes, 0);
|
|
184
|
+
|
|
185
|
+
if (name_bytes.length > EXR_LONG_NAME_BYTES) {
|
|
186
|
+
throw new Error(`EXR: channel name '${name}' is ${name_bytes.length} bytes in UTF-8, more than ${EXR_LONG_NAME_BYTES}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let pixel_type;
|
|
190
|
+
|
|
191
|
+
if (data instanceof Float16Array) {
|
|
192
|
+
pixel_type = ExrPixelType.HALF;
|
|
193
|
+
} else if (data instanceof Float32Array) {
|
|
194
|
+
pixel_type = ExrPixelType.FLOAT;
|
|
195
|
+
} else if (data instanceof Uint32Array) {
|
|
196
|
+
pixel_type = ExrPixelType.UINT;
|
|
197
|
+
} else {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`EXR: channel '${name}' holds ${data?.constructor?.name};`
|
|
200
|
+
+ ` samples are Float16Array (HALF), Float32Array (FLOAT) or Uint32Array (UINT)`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (data.length !== sample_count) {
|
|
205
|
+
throw new Error(`EXR: channel '${name}' holds ${data.length} samples, where the image has ${sample_count}`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
result[c] = { name, name_bytes, data, pixel_type };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
result.sort((a, b) => exr_compare_names(a.name_bytes, b.name_bytes));
|
|
212
|
+
|
|
213
|
+
for (let c = 1; c < channel_count; c++) {
|
|
214
|
+
if (exr_compare_names(result[c - 1].name_bytes, result[c].name_bytes) === 0) {
|
|
215
|
+
throw new Error(`EXR: there are two channels named '${result[c].name}'`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Write the header: OpenEXR's required attributes, in its order, then the terminator.
|
|
224
|
+
*
|
|
225
|
+
* @param {BinaryBuffer} buffer
|
|
226
|
+
* @param {{name_bytes: Uint8Array, pixel_type: ExrPixelType}[]} channels sorted by name
|
|
227
|
+
* @param {ExrCompression} compression
|
|
228
|
+
* @param {number} width
|
|
229
|
+
* @param {number} height
|
|
230
|
+
*/
|
|
231
|
+
function write_header(buffer, channels, compression, width, height) {
|
|
232
|
+
let channel_list_bytes = 1;
|
|
233
|
+
|
|
234
|
+
for (let c = 0; c < channels.length; c++) {
|
|
235
|
+
// name and terminator; pixel type; pLinear and three reserved bytes; x and y sampling
|
|
236
|
+
channel_list_bytes += channels[c].name_bytes.length + 1 + 4 + 4 + 8;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
write_attribute(buffer, "channels", "chlist", channel_list_bytes);
|
|
240
|
+
|
|
241
|
+
for (let c = 0; c < channels.length; c++) {
|
|
242
|
+
const name_bytes = channels[c].name_bytes;
|
|
243
|
+
|
|
244
|
+
buffer.writeBytes(name_bytes, 0, name_bytes.length);
|
|
245
|
+
buffer.writeUint8(0);
|
|
246
|
+
|
|
247
|
+
buffer.writeInt32(channels[c].pixel_type);
|
|
248
|
+
buffer.writeUint32(0);
|
|
249
|
+
buffer.writeInt32(1);
|
|
250
|
+
buffer.writeInt32(1);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
buffer.writeUint8(0);
|
|
254
|
+
|
|
255
|
+
write_attribute(buffer, "compression", "compression", 1);
|
|
256
|
+
buffer.writeUint8(compression);
|
|
257
|
+
|
|
258
|
+
write_attribute(buffer, "dataWindow", "box2i", 16);
|
|
259
|
+
write_window(buffer, width, height);
|
|
260
|
+
|
|
261
|
+
write_attribute(buffer, "displayWindow", "box2i", 16);
|
|
262
|
+
write_window(buffer, width, height);
|
|
263
|
+
|
|
264
|
+
// increasing Y
|
|
265
|
+
write_attribute(buffer, "lineOrder", "lineOrder", 1);
|
|
266
|
+
buffer.writeUint8(0);
|
|
267
|
+
|
|
268
|
+
write_attribute(buffer, "pixelAspectRatio", "float", 4);
|
|
269
|
+
buffer.writeFloat32(1);
|
|
270
|
+
|
|
271
|
+
write_attribute(buffer, "screenWindowCenter", "v2f", 8);
|
|
272
|
+
buffer.writeFloat32(0);
|
|
273
|
+
buffer.writeFloat32(0);
|
|
274
|
+
|
|
275
|
+
write_attribute(buffer, "screenWindowWidth", "float", 4);
|
|
276
|
+
buffer.writeFloat32(1);
|
|
277
|
+
|
|
278
|
+
buffer.writeUint8(0);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* @param {BinaryBuffer} buffer
|
|
283
|
+
* @param {string} name
|
|
284
|
+
* @param {string} type
|
|
285
|
+
* @param {number} size bytes of the value that follows
|
|
286
|
+
*/
|
|
287
|
+
function write_attribute(buffer, name, type, size) {
|
|
288
|
+
write_name(buffer, name);
|
|
289
|
+
write_name(buffer, type);
|
|
290
|
+
|
|
291
|
+
buffer.writeInt32(size);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* @param {BinaryBuffer} buffer
|
|
296
|
+
* @param {string} name ASCII
|
|
297
|
+
*/
|
|
298
|
+
function write_name(buffer, name) {
|
|
299
|
+
buffer.writeASCIIString(name);
|
|
300
|
+
buffer.writeUint8(0);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* A `box2i` from the origin to the last pixel.
|
|
305
|
+
*
|
|
306
|
+
* @param {BinaryBuffer} buffer
|
|
307
|
+
* @param {number} width
|
|
308
|
+
* @param {number} height
|
|
309
|
+
*/
|
|
310
|
+
function write_window(buffer, width, height) {
|
|
311
|
+
buffer.writeInt32(0);
|
|
312
|
+
buffer.writeInt32(0);
|
|
313
|
+
buffer.writeInt32(width - 1);
|
|
314
|
+
buffer.writeInt32(height - 1);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Gather a chunk's scanlines from the channel planes — the inverse of `read_scanlines` in `exr_read`.
|
|
319
|
+
*
|
|
320
|
+
* @param {BinaryBuffer} destination
|
|
321
|
+
* @param {(Uint16Array|Uint32Array)[]} plane_bits in channel list order
|
|
322
|
+
* @param {number} width
|
|
323
|
+
* @param {number} first_line
|
|
324
|
+
* @param {number} line_count
|
|
325
|
+
*/
|
|
326
|
+
function write_scanlines(destination, plane_bits, width, first_line, line_count) {
|
|
327
|
+
const channel_count = plane_bits.length;
|
|
328
|
+
|
|
329
|
+
for (let line = first_line; line < first_line + line_count; line++) {
|
|
330
|
+
const row = line * width;
|
|
331
|
+
|
|
332
|
+
for (let c = 0; c < channel_count; c++) {
|
|
333
|
+
const bits = plane_bits[c];
|
|
334
|
+
|
|
335
|
+
if (bits.BYTES_PER_ELEMENT === 2) {
|
|
336
|
+
destination.writeUint16Array(bits, row, width);
|
|
337
|
+
} else {
|
|
338
|
+
destination.writeUint32Array(bits, row, width);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Transform a chunk the way ZIP compression does before deflating it — the inverse of
|
|
346
|
+
* `zip_reconstruct` in `exr_read`.
|
|
347
|
+
*
|
|
348
|
+
* The bytes at even positions are moved ahead of those at odd ones — for half floats, the low bytes of
|
|
349
|
+
* the samples ahead of their high bytes — and the result is delta-coded byte by byte with a bias of
|
|
350
|
+
* 128, which turns smooth image data into the small, repetitive values deflate codes cheaply.
|
|
351
|
+
*
|
|
352
|
+
* @param {Uint8Array} source the chunk's pixel data
|
|
353
|
+
* @param {Uint8Array} destination
|
|
354
|
+
* @param {number} length bytes, at least one
|
|
355
|
+
*/
|
|
356
|
+
function zip_prepare(source, destination, length) {
|
|
357
|
+
const half = (length + 1) >> 1;
|
|
358
|
+
|
|
359
|
+
let previous = source[0];
|
|
360
|
+
|
|
361
|
+
destination[0] = previous;
|
|
362
|
+
|
|
363
|
+
for (let i = 1; i < half; i++) {
|
|
364
|
+
const value = source[2 * i];
|
|
365
|
+
|
|
366
|
+
destination[i] = value - previous + 128;
|
|
367
|
+
|
|
368
|
+
previous = value;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
for (let i = half; i < length; i++) {
|
|
372
|
+
const value = source[2 * (i - half) + 1];
|
|
373
|
+
|
|
374
|
+
destination[i] = value - previous + 128;
|
|
375
|
+
|
|
376
|
+
previous = value;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
@@ -124,6 +124,11 @@
|
|
|
124
124
|
|
|
125
125
|
<hr>
|
|
126
126
|
|
|
127
|
+
<label>
|
|
128
|
+
<input type="checkbox" id="sun" checked>
|
|
129
|
+
<span>Sun <span class="hint">— the directional light; L flips it</span></span>
|
|
130
|
+
</label>
|
|
131
|
+
|
|
127
132
|
<label>
|
|
128
133
|
<span>Sun height</span>
|
|
129
134
|
<input type="range" id="sun_elevation" min="2" max="88" value="62">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/probe_clipmap/main.js"],"names":[],"mappings":"AAyFA;;;;;GAKG;AACH,yBAFa,QAAQ,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/probe_clipmap/main.js"],"names":[],"mappings":"AAyFA;;;;;GAKG;AACH,yBAFa,QAAQ,IAAI,CAAC,CAuUzB"}
|
|
@@ -178,7 +178,11 @@ export async function start() {
|
|
|
178
178
|
// a sun the sliders turn, whatever lights the file brought
|
|
179
179
|
const sun = make_sunlight({ intensity: 7.2 });
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
/**
|
|
182
|
+
* The sun's entity while it is in the scene, -1 while the toggle has it out.
|
|
183
|
+
* @type {number}
|
|
184
|
+
*/
|
|
185
|
+
let sun_entity = place_light(scene, sun);
|
|
182
186
|
|
|
183
187
|
const rotation = new Quaternion();
|
|
184
188
|
const sun_direction = new Vector3();
|
|
@@ -229,6 +233,33 @@ export async function start() {
|
|
|
229
233
|
element('sun_elevation').addEventListener('input', on_sun_input);
|
|
230
234
|
element('sun_azimuth').addEventListener('input', on_sun_input);
|
|
231
235
|
|
|
236
|
+
const sun_toggle = element('sun');
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Take the sun out of the scene, or put it back; out, it is gone from the lights, its shadows and the
|
|
240
|
+
* probes' hits alike, rather than dimmed to nothing.
|
|
241
|
+
* @param {boolean} enabled
|
|
242
|
+
*/
|
|
243
|
+
function set_sun(enabled) {
|
|
244
|
+
sun_toggle.checked = enabled;
|
|
245
|
+
|
|
246
|
+
if (enabled === (sun_entity !== -1)) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (enabled) {
|
|
251
|
+
sun_entity = place_light(scene, sun);
|
|
252
|
+
} else {
|
|
253
|
+
scene.lights.remove(sun);
|
|
254
|
+
scene.dataset.removeEntity(sun_entity);
|
|
255
|
+
sun_entity = -1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
lights_changed();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
sun_toggle.addEventListener('change', event => set_sun(event.target.checked));
|
|
262
|
+
|
|
232
263
|
load_default_environment(scene);
|
|
233
264
|
|
|
234
265
|
// ---- controls -------------------------------------------------------------------------
|
|
@@ -311,6 +342,8 @@ export async function start() {
|
|
|
311
342
|
} else if (event.code === 'KeyT') {
|
|
312
343
|
ray_resampling_toggle.checked = !ray_resampling_toggle.checked;
|
|
313
344
|
renderer.probe_clipmap.ray_resampling = ray_resampling_toggle.checked;
|
|
345
|
+
} else if (event.code === 'KeyL') {
|
|
346
|
+
set_sun(sun_entity === -1);
|
|
314
347
|
}
|
|
315
348
|
});
|
|
316
349
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Words of a listed probe's maps, as accumulation writes them for projection and publish: two a
|
|
3
3
|
* radiance texel, rgba as half floats; one a radiance texel, its rays this update, resolved and back
|
|
4
|
-
* faces; one a depth texel, its moments as half floats; then one a radiance texel,
|
|
5
|
-
*
|
|
6
|
-
* request reads (`PROBE_CHANGE_SIGNIFICANCE`).
|
|
4
|
+
* faces; one a depth texel, its moments as half floats; then one a radiance texel, its estimator's short
|
|
5
|
+
* mean less its mean and its deviation, luminance, as half floats, which the probe's L0 estimator sums.
|
|
7
6
|
*
|
|
8
7
|
* @param {number} radiance_resolution n_r
|
|
9
8
|
* @param {number} depth_resolution n_d
|
|
@@ -276,16 +275,6 @@ export const PROBE_DEAD_RAYS: number;
|
|
|
276
275
|
* @type {number}
|
|
277
276
|
*/
|
|
278
277
|
export const PROBE_RAYS_PER_TEXEL_MAX: number;
|
|
279
|
-
/**
|
|
280
|
-
* How far a probe's change score may go from its own running mean, in its own running deviation, before
|
|
281
|
-
* the probe's light counts as moved (§8.6); and how far a texel's sample may fall from its mean, in its
|
|
282
|
-
* own deviation, before it counts at that. The score sums the texels' falls over the root of their
|
|
283
|
-
* count, each held to this, which the central limit makes near normal however a texel's own fall is
|
|
284
|
-
* spread. A normal score passes it on all but 0.27% of updates; Sponza's probes past 64 updates, the light
|
|
285
|
-
* still, asked for the most on 1.1–1.3% (measured, §8.6), each such update costing one at the most rays.
|
|
286
|
-
* @type {number}
|
|
287
|
-
*/
|
|
288
|
-
export const PROBE_CHANGE_SIGNIFICANCE: number;
|
|
289
278
|
/**
|
|
290
279
|
* Rays a guided update gives one radiance texel at most, the same gather budget as a depth texel's
|
|
291
280
|
* ({@link PROBE_DEPTH_TEXEL_RAYS}). The guide holds each texel's weight down to it and spreads what it
|
|
@@ -366,8 +355,7 @@ export const PROBE_WANT_WORKGROUP_TILES: number;
|
|
|
366
355
|
*/
|
|
367
356
|
export const PROBE_FILL_WORKGROUP_SIZE: number;
|
|
368
357
|
/**
|
|
369
|
-
* Lattice cells a cascade's blend skirt is deep:
|
|
370
|
-
* which the spacing doubles (§8.1).
|
|
358
|
+
* Lattice cells a cascade's blend skirt is deep: two cells of the coarser cascade (§8.1).
|
|
371
359
|
* @type {number}
|
|
372
360
|
*/
|
|
373
361
|
export const PROBE_BLEND_SKIRT: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PROBE_CLIPMAP.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js"],"names":[],"mappings":"AAkSA
|
|
1
|
+
{"version":3,"file":"PROBE_CLIPMAP.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js"],"names":[],"mappings":"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"}
|
|
@@ -291,9 +291,8 @@ export const PROBE_DEPTH_TEXEL_RAYS = 64;
|
|
|
291
291
|
/**
|
|
292
292
|
* Words of a listed probe's maps, as accumulation writes them for projection and publish: two a
|
|
293
293
|
* radiance texel, rgba as half floats; one a radiance texel, its rays this update, resolved and back
|
|
294
|
-
* faces; one a depth texel, its moments as half floats; then one a radiance texel,
|
|
295
|
-
*
|
|
296
|
-
* request reads (`PROBE_CHANGE_SIGNIFICANCE`).
|
|
294
|
+
* faces; one a depth texel, its moments as half floats; then one a radiance texel, its estimator's short
|
|
295
|
+
* mean less its mean and its deviation, luminance, as half floats, which the probe's L0 estimator sums.
|
|
297
296
|
*
|
|
298
297
|
* @param {number} radiance_resolution n_r
|
|
299
298
|
* @param {number} depth_resolution n_d
|
|
@@ -337,17 +336,6 @@ export const PROBE_DEAD_RAYS = 16;
|
|
|
337
336
|
*/
|
|
338
337
|
export const PROBE_RAYS_PER_TEXEL_MAX = 4;
|
|
339
338
|
|
|
340
|
-
/**
|
|
341
|
-
* How far a probe's change score may go from its own running mean, in its own running deviation, before
|
|
342
|
-
* the probe's light counts as moved (§8.6); and how far a texel's sample may fall from its mean, in its
|
|
343
|
-
* own deviation, before it counts at that. The score sums the texels' falls over the root of their
|
|
344
|
-
* count, each held to this, which the central limit makes near normal however a texel's own fall is
|
|
345
|
-
* spread. A normal score passes it on all but 0.27% of updates; Sponza's probes past 64 updates, the light
|
|
346
|
-
* still, asked for the most on 1.1–1.3% (measured, §8.6), each such update costing one at the most rays.
|
|
347
|
-
* @type {number}
|
|
348
|
-
*/
|
|
349
|
-
export const PROBE_CHANGE_SIGNIFICANCE = 3;
|
|
350
|
-
|
|
351
339
|
/**
|
|
352
340
|
* Rays a guided update gives one radiance texel at most, the same gather budget as a depth texel's
|
|
353
341
|
* ({@link PROBE_DEPTH_TEXEL_RAYS}). The guide holds each texel's weight down to it and spreads what it
|
|
@@ -469,11 +457,10 @@ export const PROBE_WANT_WORKGROUP_TILES = 8;
|
|
|
469
457
|
export const PROBE_FILL_WORKGROUP_SIZE = 64;
|
|
470
458
|
|
|
471
459
|
/**
|
|
472
|
-
* Lattice cells a cascade's blend skirt is deep:
|
|
473
|
-
* which the spacing doubles (§8.1).
|
|
460
|
+
* Lattice cells a cascade's blend skirt is deep: two cells of the coarser cascade (§8.1).
|
|
474
461
|
* @type {number}
|
|
475
462
|
*/
|
|
476
|
-
export const PROBE_BLEND_SKIRT =
|
|
463
|
+
export const PROBE_BLEND_SKIRT = 4;
|
|
477
464
|
|
|
478
465
|
/**
|
|
479
466
|
* The receiver bias's length, in lattice cells of the cascade picked (§9.1).
|
|
@@ -189,8 +189,8 @@ export const PROBE_RAY_SUMS_WRITE_RESOURCE: ShaderResourceDescriptor;
|
|
|
189
189
|
*/
|
|
190
190
|
export const PROBE_RAY_SUMS_READ_RESOURCE: ShaderResourceDescriptor;
|
|
191
191
|
/**
|
|
192
|
-
* A slot's
|
|
193
|
-
*
|
|
192
|
+
* A slot's request (§8.6, §8.8), as projection writes it after each update: what the probe's next update takes,
|
|
193
|
+
* by its L0 estimator, a multi-scale mean estimator over the probe's whole light (`shader_probe_clipmap_project`).
|
|
194
194
|
*
|
|
195
195
|
* @type {WebGPUStruct}
|
|
196
196
|
*/
|
|
@@ -201,7 +201,7 @@ export const PROBE_REQUEST_STRUCT: WebGPUStruct;
|
|
|
201
201
|
*/
|
|
202
202
|
export const PROBE_REQUESTS_WRITE_RESOURCE: ShaderResourceDescriptor;
|
|
203
203
|
/**
|
|
204
|
-
* Each slot's
|
|
204
|
+
* Each slot's request, as the ray counts and accumulation read it.
|
|
205
205
|
* @type {ShaderResourceDescriptor}
|
|
206
206
|
*/
|
|
207
207
|
export const PROBE_REQUESTS_READ_RESOURCE: ShaderResourceDescriptor;
|
package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk_probe_clipmap.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js"],"names":[],"mappings":"AAyEA;;;;;;GAMG;AACH,kCAFU,YAAY,CAsBG;AAEzB;;;;;GAKG;AACH,+BAFU,YAAY,CAuBA;AAEtB;;;;;;GAMG;AACH,oCAFU,wBAAwB,CAEyE;AAE3G;;;;GAIG;AACH,+CAFU,wBAAwB,CAE0G;AAE5I;;;;GAIG;AACH,0CAFU,wBAAwB,CAE+F;AAEjI;;;GAGG;AACH,yCAFU,wBAAwB,CAEwF;AAE1H;;;GAGG;AACH,2CAFU,wBAAwB,CAE+G;AAEjJ;;;GAGG;AACH,0CAFU,wBAAwB,CAEwG;AAE1I;;;GAGG;AACH,sCAFU,wBAAwB,CAEkG;AAEpI;;;;GAIG;AACH,2CAFU,wBAAwB,CAEoF;AAEtH;;;GAGG;AACH,kCAFU,wBAAwB,CAE6E;AAE/G;;;;;GAKG;AACH,oCAFU,YAAY,CAKK;AAE3B;;;GAGG;AACH,2CAFU,wBAAwB,CAE0F;AAE5H;;;GAGG;AACH,4CAFU,wBAAwB,CAEiG;AAEnI;;;GAGG;AACH,yCAFU,wBAAwB,CAEgG;AAElI;;;GAGG;AACH,0CAFU,wBAAwB,CAEyG;AAE3I;;;GAGG;AACH,4CAFU,wBAAwB,CAEoG;AAEtI;;;GAGG;AACH,yCAFU,wBAAwB,CAEwG;AAE1I;;;GAGG;AACH,yCAFU,wBAAwB,CAEiG;AAEnI;;;GAGG;AACH,wCAFU,wBAAwB,CAEyF;AAE3H;;;;GAIG;AACH,yCAFU,wBAAwB,CAEgG;AAElI;;;GAGG;AACH,0CAFU,wBAAwB,CAEmG;AAErI;;;GAGG;AACH,yCAFU,wBAAwB,CAE2F;AAE7H;;;GAGG;AACH,sCAFU,wBAAwB,CAEqF;AAEvH;;;;GAIG;AACH,2CAFU,wBAAwB,CAEiG;AAEnI;;;GAGG;AACH,0CAFU,wBAAwB,CAE0F;AAE5H;;;GAGG;AACH,wCAFU,wBAAwB,CAEsG;AAExI;;;GAGG;AACH,uCAFU,wBAAwB,CAE+F;AAEjI;;;;;GAKG;AACH,8CAFU,wBAAwB,CAE0G;AAE5I;;;;GAIG;AACH,gDAFU,wBAAwB,CAE8G;AAEhJ;;;GAGG;AACH,iDAFU,wBAAwB,CAE2G;AAE7I;;;GAGG;AACH,mDAFU,wBAAwB,CAE+G;AAEjJ;;;;;GAKG;AACH,4CAFU,wBAAwB,CAEsG;AAExI;;;GAGG;AACH,2CAFU,wBAAwB,CAE+F;AAEjI;;;;;GAKG;AACH,mCAFU,YAAY,CAWI;AAE1B;;;GAGG;AACH,4CAFU,wBAAwB,CAEkH;AAEpJ;;;GAGG;AACH,2CAFU,wBAAwB,CAE2G;AAE7I;;;;GAIG;AACH,wCAFU,wBAAwB,CAEmF;AAErH;;;GAGG;AACH,uCAFU,wBAAwB,CAE4E;AAE9G;;;;;;GAMG;AACH,wCAFU,wBAAwB,CAEiG;AAEnI;;;GAGG;AACH,6CAFU,wBAAwB,CAEgG;AAElI;;;;;GAKG;AACH,6CAFU,wBAAwB,CAEqG;AAEvI;;;GAGG;AACH,4CAFU,wBAAwB,CAE8F;AAEhI;;;;GAIG;AACH,2CAFU,wBAAwB,CAEwG;AAE1I;;;;GAIG;AACH,0CAFU,wBAAwB,CAEuF;AAEzH;;;GAGG;AACH,yCAFU,wBAAwB,CAEgF;AAElH;;;GAGG;AACH,qCAFU,YAAY,CAOD;AAErB;;;GAGG;AACH,8CAFU,wBAAwB,CAEwH;AAE1J;;;GAGG;AACH,6CAFU,wBAAwB,CAEiH;AAEnJ;;;GAGG;AACH,0CAFU,YAAY,
|
|
1
|
+
{"version":3,"file":"chunk_probe_clipmap.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js"],"names":[],"mappings":"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"}
|
|
@@ -340,20 +340,20 @@ export const PROBE_RAY_SUMS_WRITE_RESOURCE = ShaderResourceDescriptor.storageBuf
|
|
|
340
340
|
export const PROBE_RAY_SUMS_READ_RESOURCE = ShaderResourceDescriptor.storageBuffer("probe_ray_sums", COLLECTION_U32_STRUCT_READ);
|
|
341
341
|
|
|
342
342
|
/**
|
|
343
|
-
* A slot's
|
|
344
|
-
*
|
|
343
|
+
* A slot's request (§8.6, §8.8), as projection writes it after each update: what the probe's next update takes,
|
|
344
|
+
* by its L0 estimator, a multi-scale mean estimator over the probe's whole light (`shader_probe_clipmap_project`).
|
|
345
345
|
*
|
|
346
346
|
* @type {WebGPUStruct}
|
|
347
347
|
*/
|
|
348
348
|
export const PROBE_REQUEST_STRUCT = WebGPUStruct.from({
|
|
349
349
|
/** the rays the probe's next guided update asks for */
|
|
350
350
|
rays: u32,
|
|
351
|
-
/** the
|
|
352
|
-
|
|
353
|
-
/** the
|
|
354
|
-
|
|
355
|
-
/** the
|
|
356
|
-
|
|
351
|
+
/** the least blend of every texel's long mean on the next update: the L0 estimator's catch-up */
|
|
352
|
+
blend: f32,
|
|
353
|
+
/** the L0 estimator's inconsistency, the smoothed history of its means' gap over its deviation */
|
|
354
|
+
inconsistency: f32,
|
|
355
|
+
/** the L0 estimator's blend reduction */
|
|
356
|
+
reduction: f32,
|
|
357
357
|
}, "ProbeRequest").pack();
|
|
358
358
|
|
|
359
359
|
/**
|
|
@@ -363,7 +363,7 @@ export const PROBE_REQUEST_STRUCT = WebGPUStruct.from({
|
|
|
363
363
|
export const PROBE_REQUESTS_WRITE_RESOURCE = ShaderResourceDescriptor.storageBuffer("probe_requests", WebGPUArray.from(PROBE_REQUEST_STRUCT), true);
|
|
364
364
|
|
|
365
365
|
/**
|
|
366
|
-
* Each slot's
|
|
366
|
+
* Each slot's request, as the ray counts and accumulation read it.
|
|
367
367
|
* @type {ShaderResourceDescriptor}
|
|
368
368
|
*/
|
|
369
369
|
export const PROBE_REQUESTS_READ_RESOURCE = ShaderResourceDescriptor.storageBuffer("probe_requests", WebGPUArray.from(PROBE_REQUEST_STRUCT));
|
|
@@ -463,10 +463,12 @@ export const PROBE_PROJECTION_ROW_STRUCT = WebGPUStruct.from({
|
|
|
463
463
|
resolved: u32,
|
|
464
464
|
/** of them, the back faces */
|
|
465
465
|
back_faces: u32,
|
|
466
|
-
/**
|
|
467
|
-
|
|
468
|
-
/** the texels
|
|
469
|
-
|
|
466
|
+
/** the texels' short means less their means, luminance, each by its solid angle, summed: L0's gap */
|
|
467
|
+
gap: f32,
|
|
468
|
+
/** the texels' deviations squared, luminance, each by its solid angle squared, summed: L0's variance */
|
|
469
|
+
variance: f32,
|
|
470
|
+
/** the texels' short means, luminance, each by its solid angle, summed: L0's short mean */
|
|
471
|
+
level: f32,
|
|
470
472
|
}, "ProbeProjectionRow").pack();
|
|
471
473
|
|
|
472
474
|
/**
|
|
@@ -9,9 +9,9 @@ export const PROBE_ACCUMULATE_SETTINGS: WebGPUStruct;
|
|
|
9
9
|
* `probe_maps` (`probe_clipmap_maps_words`): two words a radiance texel, rgba as half floats; then a
|
|
10
10
|
* word a radiance texel, its rays this update, resolved in the low half and back faces in the high,
|
|
11
11
|
* which classification sums; then a word a depth texel, its moments as half floats; then a word a
|
|
12
|
-
* radiance texel,
|
|
13
|
-
* `
|
|
14
|
-
*
|
|
12
|
+
* radiance texel, its estimator after the update as the probe's L0 estimator sums it
|
|
13
|
+
* (`shader_probe_clipmap_project`): its short mean less its mean and its deviation (the estimator's own,
|
|
14
|
+
* floored at the display's exposure), luminance, as half floats; nothing for a texel no ray has reached.
|
|
15
15
|
*
|
|
16
16
|
* - **A texel's rays**: on a uniform update (`PROBE_UNIFORM_PERIOD`), `probe_clipmap_texel_ray`'s. On a
|
|
17
17
|
* guided one, the run that begins at its walk position in the probe's guide, at most
|
|
@@ -29,6 +29,10 @@ export const PROBE_ACCUMULATE_SETTINGS: WebGPUStruct;
|
|
|
29
29
|
* - The mean blends by at least 1/(a+1) until the count reaches its limit: a young texel is its running
|
|
30
30
|
* mean, which the estimator's blend reduction, nothing at its seed, would otherwise hold back. Past it
|
|
31
31
|
* the estimator alone, down to 1/8192 an update on a noisy texel.
|
|
32
|
+
* - And by at least the probe's L0 blend (`PROBE_REQUEST_STRUCT`'s `blend`), its L0 estimator's catch-up
|
|
33
|
+
* after its last update: a change in the probe's whole light, which the texels' own tests, a one-ray
|
|
34
|
+
* sample's noise apiece, do not see. The texel's own catch-up stays, for light that moves without
|
|
35
|
+
* changing L0.
|
|
32
36
|
* - Samples are clipped once the variance's window has passed, when 1/(a+1) is no longer its blend:
|
|
33
37
|
* before that it holds no spread to clip to.
|
|
34
38
|
* - A texel's first sample seeds both means. A texel no ray reached keeps its history.
|