@aethervtt/shard-render 0.1.0 → 0.2.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/dist/ablation.d.ts +74 -0
- package/dist/ablation.js +117 -0
- package/dist/atmosphere-nodes.js +100 -12
- package/dist/atmosphere-shaders.js +17 -5
- package/dist/baseline/exposure.d.ts +1 -1
- package/dist/baseline/exposure.js +18 -5
- package/dist/crowd.d.ts +21 -0
- package/dist/crowd.js +121 -0
- package/dist/decals-plugin.d.ts +5 -0
- package/dist/decals-plugin.js +54 -0
- package/dist/decals.d.ts +69 -0
- package/dist/decals.js +326 -0
- package/dist/deferred.js +4 -1
- package/dist/display-nodes.js +24 -11
- package/dist/foliage-budget.d.ts +64 -0
- package/dist/foliage-budget.js +108 -0
- package/dist/foliage-path.d.ts +19 -0
- package/dist/foliage-path.js +7 -0
- package/dist/foliage-shaders.d.ts +11 -0
- package/dist/foliage-shaders.js +504 -0
- package/dist/foliage.d.ts +194 -0
- package/dist/foliage.js +1027 -0
- package/dist/forward.js +26 -2
- package/dist/graph.d.ts +34 -0
- package/dist/graph.js +139 -14
- package/dist/index.d.ts +11 -1
- package/dist/index.js +57 -0
- package/dist/overlay-registry.d.ts +3 -1
- package/dist/overlay-registry.js +3 -1
- package/dist/overlays.js +5 -2
- package/dist/perf-overlay.d.ts +9 -0
- package/dist/perf-overlay.js +125 -0
- package/dist/plugin.d.ts +11 -1
- package/dist/plugin.js +33 -2
- package/dist/post-common.d.ts +20 -2
- package/dist/post-common.js +79 -2
- package/dist/post-nodes.d.ts +5 -0
- package/dist/post-nodes.js +136 -104
- package/dist/post-scene.d.ts +130 -0
- package/dist/post-scene.js +164 -0
- package/dist/post-shaders.js +41 -63
- package/dist/post.js +11 -1
- package/dist/ribbons-plugin.d.ts +5 -0
- package/dist/ribbons-plugin.js +59 -0
- package/dist/ribbons.d.ts +44 -0
- package/dist/ribbons.js +288 -0
- package/dist/scenario.d.ts +70 -0
- package/dist/scenario.js +125 -0
- package/dist/shaders.js +58 -5
- package/dist/shadows.d.ts +2 -1
- package/dist/shadows.js +3 -1
- package/dist/standard.js +6 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.js +6 -0
- package/dist/timer.d.ts +16 -1
- package/dist/timer.js +66 -19
- package/dist/vfx-draw.d.ts +37 -0
- package/dist/vfx-draw.js +291 -0
- package/dist/vfx-state.d.ts +42 -0
- package/dist/vfx-state.js +81 -0
- package/dist/vfx.d.ts +69 -0
- package/dist/vfx.js +103 -0
- package/package.json +19 -11
package/dist/post-nodes.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Exposure } from "./camera.js";
|
|
|
9
9
|
import { addRenderFeatures } from "./features.js";
|
|
10
10
|
import { drawMaterials, ForwardStateResource, PASS_PREPASS, viewBindGroup } from "./forward.js";
|
|
11
11
|
import { RenderPhase } from "./graph.js";
|
|
12
|
-
import { Graph, Views } from "./plugin.js";
|
|
12
|
+
import { Gpu, Graph, Views } from "./plugin.js";
|
|
13
13
|
import { AutoExposure, cocParams, hasEffect, needsPrepass, PostEffect } from "./post.js";
|
|
14
14
|
import {
|
|
15
15
|
beginPass,
|
|
@@ -18,8 +18,11 @@ import {
|
|
|
18
18
|
HDR,
|
|
19
19
|
idOf,
|
|
20
20
|
PostCache,
|
|
21
|
+
postFormat,
|
|
22
|
+
roundsRg11,
|
|
21
23
|
sampler,
|
|
22
24
|
scratch,
|
|
25
|
+
targetsFor,
|
|
23
26
|
tex,
|
|
24
27
|
uniform
|
|
25
28
|
} from "./post-common.js";
|
|
@@ -44,7 +47,16 @@ const prepassNode = {
|
|
|
44
47
|
if (v.cam.deferred) drawMaterials(ctx, state, v.pv, v.cam, v.cam.forwardOnly, PASS_PREPASS);
|
|
45
48
|
}
|
|
46
49
|
};
|
|
47
|
-
const FOG_KEYS = [
|
|
50
|
+
const FOG_KEYS = [
|
|
51
|
+
"post/fog",
|
|
52
|
+
"post/fog/alpha",
|
|
53
|
+
"post/fog/interior",
|
|
54
|
+
"post/fog/alpha/interior",
|
|
55
|
+
"post/fog/rg11",
|
|
56
|
+
"post/fog/alpha/rg11",
|
|
57
|
+
"post/fog/interior/rg11",
|
|
58
|
+
"post/fog/alpha/interior/rg11"
|
|
59
|
+
];
|
|
48
60
|
const SSAO_STEPS = [
|
|
49
61
|
[1, 4],
|
|
50
62
|
[2, 4],
|
|
@@ -53,7 +65,7 @@ const SSAO_STEPS = [
|
|
|
53
65
|
const invProj = mat4.create();
|
|
54
66
|
function ssaoNode(phase, deferred) {
|
|
55
67
|
const cache = new PostCache();
|
|
56
|
-
let
|
|
68
|
+
let layout;
|
|
57
69
|
let layoutGen = -1;
|
|
58
70
|
return {
|
|
59
71
|
kind: "raw",
|
|
@@ -63,41 +75,27 @@ function ssaoNode(phase, deferred) {
|
|
|
63
75
|
return cam !== void 0 && cam.deferred === deferred && (cam.post.effects & PostEffect.Ssao) !== 0;
|
|
64
76
|
},
|
|
65
77
|
reads: ["ssao-normal", "ssao-depth"],
|
|
66
|
-
writes: ["ssao"
|
|
78
|
+
writes: ["ssao"],
|
|
67
79
|
run: (ctx) => {
|
|
68
80
|
const v = forwardView(ctx);
|
|
69
81
|
if (!v) return;
|
|
70
82
|
const gpu = ctx.gpu;
|
|
71
|
-
if (!
|
|
83
|
+
if (!layout || layoutGen !== gpu.generation) {
|
|
72
84
|
layoutGen = gpu.generation;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}),
|
|
78
|
-
up: gpu.layouts.bindGroupLayout({
|
|
79
|
-
label: "ssao/upsample",
|
|
80
|
-
entries: [uniform(0), depthTex(gpu, 1), uniform(3), tex(4, "unfilterable-float")]
|
|
81
|
-
})
|
|
82
|
-
};
|
|
85
|
+
layout = gpu.layouts.bindGroupLayout({
|
|
86
|
+
label: "ssao/gtao",
|
|
87
|
+
entries: [uniform(0), depthTex(gpu, 1), tex(2, "unfilterable-float"), uniform(3)]
|
|
88
|
+
});
|
|
83
89
|
}
|
|
84
90
|
const gtao = cache.render(
|
|
85
91
|
ctx,
|
|
86
92
|
"ssao/gtao",
|
|
87
93
|
"shard::post::ssao",
|
|
88
94
|
"gtao",
|
|
89
|
-
[
|
|
95
|
+
[layout],
|
|
90
96
|
[{ format: "rg16float" }]
|
|
91
97
|
);
|
|
92
|
-
|
|
93
|
-
ctx,
|
|
94
|
-
"ssao/upsample",
|
|
95
|
-
"shard::post::ssao",
|
|
96
|
-
"upsample",
|
|
97
|
-
[layouts.up],
|
|
98
|
-
[{ format: "r8unorm" }]
|
|
99
|
-
);
|
|
100
|
-
if (!gtao || !up) return;
|
|
98
|
+
if (!gtao) return;
|
|
101
99
|
const { cam, pv } = v;
|
|
102
100
|
const s = cam.post.ssao;
|
|
103
101
|
const steps = SSAO_STEPS[s.quality] ?? SSAO_STEPS[1];
|
|
@@ -113,13 +111,12 @@ function ssaoNode(phase, deferred) {
|
|
|
113
111
|
params.write(scratch, 0, 0, 24);
|
|
114
112
|
const depth = ctx.texture("ssao-depth");
|
|
115
113
|
const normal = ctx.texture("ssao-normal");
|
|
116
|
-
const half = ctx.texture("ssao-half");
|
|
117
114
|
const out = ctx.texture("ssao");
|
|
118
|
-
const
|
|
115
|
+
const group = cache.group(
|
|
119
116
|
gpu,
|
|
120
117
|
`${ctx.view.name}/gtao`,
|
|
121
118
|
`${idOf(depth)}/${idOf(normal)}/${pv.uniform.version}/${params.version}`,
|
|
122
|
-
|
|
119
|
+
layout,
|
|
123
120
|
() => [
|
|
124
121
|
{ binding: 0, resource: { buffer: pv.uniform.buffer } },
|
|
125
122
|
{ binding: 1, resource: depth.createView() },
|
|
@@ -127,26 +124,9 @@ function ssaoNode(phase, deferred) {
|
|
|
127
124
|
{ binding: 3, resource: { buffer: params.buffer } }
|
|
128
125
|
]
|
|
129
126
|
);
|
|
130
|
-
|
|
127
|
+
const pass = beginPass(ctx, "ssao", out.createView());
|
|
131
128
|
pass.setPipeline(gtao);
|
|
132
|
-
pass.setBindGroup(0,
|
|
133
|
-
pass.draw(3);
|
|
134
|
-
pass.end();
|
|
135
|
-
const g2 = cache.group(
|
|
136
|
-
gpu,
|
|
137
|
-
`${ctx.view.name}/ssao-up`,
|
|
138
|
-
`${idOf(depth)}/${idOf(half)}/${pv.uniform.version}/${params.version}`,
|
|
139
|
-
layouts.up,
|
|
140
|
-
() => [
|
|
141
|
-
{ binding: 0, resource: { buffer: pv.uniform.buffer } },
|
|
142
|
-
{ binding: 1, resource: depth.createView() },
|
|
143
|
-
{ binding: 3, resource: { buffer: params.buffer } },
|
|
144
|
-
{ binding: 4, resource: half.createView() }
|
|
145
|
-
]
|
|
146
|
-
);
|
|
147
|
-
pass = beginPass(ctx, "ssao/upsample", out.createView());
|
|
148
|
-
pass.setPipeline(up);
|
|
149
|
-
pass.setBindGroup(0, g2);
|
|
129
|
+
pass.setBindGroup(0, group);
|
|
150
130
|
pass.draw(3);
|
|
151
131
|
pass.end();
|
|
152
132
|
pv.ao = out;
|
|
@@ -160,7 +140,8 @@ function fogNode() {
|
|
|
160
140
|
return {
|
|
161
141
|
kind: "render",
|
|
162
142
|
phase: RenderPhase.Post,
|
|
163
|
-
|
|
143
|
+
// With aerial perspective, the atmosphere's composite applies fog in its own pass.
|
|
144
|
+
enabled: (view) => ((cameraOf(view)?.post.effects ?? 0) & (PostEffect.Fog | PostEffect.Atmosphere)) === PostEffect.Fog,
|
|
164
145
|
reads: ["fog-in", "depth", "environment", "clusters"],
|
|
165
146
|
writes: ["fog-out"],
|
|
166
147
|
color: [{ resource: "fog-out", clear: { r: 0, g: 0, b: 0, a: 1 } }],
|
|
@@ -177,14 +158,16 @@ function fogNode() {
|
|
|
177
158
|
});
|
|
178
159
|
}
|
|
179
160
|
const alpha = v.cam.alphaOutput;
|
|
161
|
+
const out = ctx.texture("fog-out");
|
|
162
|
+
const rg11 = out.format === "rg11b10ufloat";
|
|
180
163
|
const pipeline = cache.render(
|
|
181
164
|
ctx,
|
|
182
|
-
FOG_KEYS[(alpha ? 1 : 0) + (state.interiorMode !== 0 ? 2 : 0)],
|
|
165
|
+
FOG_KEYS[(alpha ? 1 : 0) + (state.interiorMode !== 0 ? 2 : 0) + (rg11 ? 4 : 0)],
|
|
183
166
|
"shard::post::fog",
|
|
184
167
|
"fs",
|
|
185
168
|
[state.layouts.view, layout],
|
|
186
|
-
|
|
187
|
-
{ TRANSPARENT: alpha }
|
|
169
|
+
targetsFor(out),
|
|
170
|
+
{ TRANSPARENT: alpha, RG11_ROUND: roundsRg11(gpu, out) }
|
|
188
171
|
);
|
|
189
172
|
if (!pipeline) return;
|
|
190
173
|
const f = v.cam.post.fog;
|
|
@@ -254,12 +237,11 @@ function taaNode() {
|
|
|
254
237
|
"shard::post::taa",
|
|
255
238
|
"fs",
|
|
256
239
|
[layout],
|
|
257
|
-
|
|
240
|
+
HDR,
|
|
258
241
|
{ TRANSPARENT: alpha }
|
|
259
242
|
);
|
|
260
243
|
if (!pipeline) return;
|
|
261
244
|
const input = ctx.texture("taa-in");
|
|
262
|
-
const out = ctx.texture("taa-out");
|
|
263
245
|
let h = histories.get(ctx.view.name);
|
|
264
246
|
if (!h || h.generation !== gpu.generation || h.textures[0].width !== input.width || h.textures[0].height !== input.height) {
|
|
265
247
|
for (const t of h?.textures ?? []) t.destroy();
|
|
@@ -267,7 +249,7 @@ function taaNode() {
|
|
|
267
249
|
label: `${ctx.view.name}/taa-history-${i}`,
|
|
268
250
|
size: [input.width, input.height],
|
|
269
251
|
format: "rgba16float",
|
|
270
|
-
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
252
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC
|
|
271
253
|
});
|
|
272
254
|
h = { textures: [make(0), make(1)], index: 0, valid: false, generation: gpu.generation };
|
|
273
255
|
histories.set(ctx.view.name, h);
|
|
@@ -301,7 +283,6 @@ function taaNode() {
|
|
|
301
283
|
const pass = ctx.encoder.beginRenderPass({
|
|
302
284
|
label: `${ctx.view.name}/taa`,
|
|
303
285
|
colorAttachments: [
|
|
304
|
-
{ view: out.createView(), loadOp: "clear", storeOp: "store", clearValue: [0, 0, 0, 1] },
|
|
305
286
|
{ view: write.createView(), loadOp: "clear", storeOp: "store", clearValue: [0, 0, 0, 1] }
|
|
306
287
|
],
|
|
307
288
|
timestampWrites: ctx.timestamps("post/taa")
|
|
@@ -310,6 +291,7 @@ function taaNode() {
|
|
|
310
291
|
pass.setBindGroup(0, group);
|
|
311
292
|
pass.draw(3);
|
|
312
293
|
pass.end();
|
|
294
|
+
ctx.provide("taa-out", write);
|
|
313
295
|
h.index = 1 - h.index;
|
|
314
296
|
h.valid = true;
|
|
315
297
|
}
|
|
@@ -344,13 +326,15 @@ function motionBlurNode() {
|
|
|
344
326
|
]
|
|
345
327
|
});
|
|
346
328
|
}
|
|
329
|
+
const out = ctx.texture("motion-blur-out");
|
|
347
330
|
const pipeline = cache.render(
|
|
348
331
|
ctx,
|
|
349
|
-
"post/motion-blur",
|
|
332
|
+
out.format === "rg11b10ufloat" ? "post/motion-blur/rg11" : "post/motion-blur",
|
|
350
333
|
"shard::post::motion_blur",
|
|
351
334
|
"fs",
|
|
352
335
|
[layout],
|
|
353
|
-
|
|
336
|
+
targetsFor(out),
|
|
337
|
+
{ RG11_ROUND: roundsRg11(gpu, out) }
|
|
354
338
|
);
|
|
355
339
|
if (!pipeline) return;
|
|
356
340
|
const m = v.cam.post.motionBlur;
|
|
@@ -407,14 +391,24 @@ function dofNode() {
|
|
|
407
391
|
});
|
|
408
392
|
}
|
|
409
393
|
const prepare = cache.render(ctx, "dof/prepare", "shard::post::dof", "prepare", [layout], HDR);
|
|
410
|
-
const
|
|
394
|
+
const blur = ctx.texture("dof-blur");
|
|
395
|
+
const out = ctx.texture("dof-out");
|
|
396
|
+
const gather = cache.render(
|
|
397
|
+
ctx,
|
|
398
|
+
blur.format === "rg11b10ufloat" ? "dof/gather/rg11" : "dof/gather",
|
|
399
|
+
"shard::post::dof",
|
|
400
|
+
"gather",
|
|
401
|
+
[layout],
|
|
402
|
+
targetsFor(blur)
|
|
403
|
+
);
|
|
411
404
|
const composite = cache.render(
|
|
412
405
|
ctx,
|
|
413
|
-
"dof/composite",
|
|
406
|
+
out.format === "rg11b10ufloat" ? "dof/composite/rg11" : "dof/composite",
|
|
414
407
|
"shard::post::dof",
|
|
415
408
|
"composite",
|
|
416
409
|
[layout],
|
|
417
|
-
|
|
410
|
+
targetsFor(out),
|
|
411
|
+
{ RG11_ROUND: roundsRg11(gpu, out) }
|
|
418
412
|
);
|
|
419
413
|
if (!prepare || !gather || !composite) return;
|
|
420
414
|
const params = cache.buffer(gpu, `${ctx.view.name}/dof`, 16);
|
|
@@ -422,8 +416,6 @@ function dofNode() {
|
|
|
422
416
|
const input = ctx.texture("dof-in");
|
|
423
417
|
const depth = ctx.texture("depth");
|
|
424
418
|
const half = ctx.texture("dof-half");
|
|
425
|
-
const blur = ctx.texture("dof-blur");
|
|
426
|
-
const out = ctx.texture("dof-out");
|
|
427
419
|
const group = (slot, secondary) => cache.group(
|
|
428
420
|
gpu,
|
|
429
421
|
`${ctx.view.name}/dof/${slot}`,
|
|
@@ -465,7 +457,8 @@ function bloomNode() {
|
|
|
465
457
|
phase: RenderPhase.Post + 40,
|
|
466
458
|
enabled: hasEffect(PostEffect.Bloom),
|
|
467
459
|
reads: ["bloom-in"],
|
|
468
|
-
|
|
460
|
+
// The tonemap composites level 0 over the image (and auto exposure meters the same mix).
|
|
461
|
+
writes: ["bloom"],
|
|
469
462
|
run: (ctx) => {
|
|
470
463
|
const v = forwardView(ctx);
|
|
471
464
|
if (!v) return;
|
|
@@ -474,48 +467,40 @@ function bloomNode() {
|
|
|
474
467
|
layoutGen = gpu.generation;
|
|
475
468
|
layout = gpu.layouts.bindGroupLayout({
|
|
476
469
|
label: "bloom",
|
|
477
|
-
entries: [tex(0), sampler(1), uniform(2)
|
|
470
|
+
entries: [tex(0), sampler(1), uniform(2)]
|
|
478
471
|
});
|
|
479
472
|
}
|
|
480
473
|
const add = { srcFactor: "one", dstFactor: "one", operation: "add" };
|
|
474
|
+
const chain = ctx.texture("bloom");
|
|
475
|
+
const rg11 = chain.format === "rg11b10ufloat";
|
|
481
476
|
const prefilter = cache.render(
|
|
482
477
|
ctx,
|
|
483
|
-
"bloom/prefilter",
|
|
478
|
+
rg11 ? "bloom/prefilter/rg11" : "bloom/prefilter",
|
|
484
479
|
"shard::post::bloom",
|
|
485
480
|
"prefilter",
|
|
486
481
|
[layout],
|
|
487
|
-
|
|
482
|
+
targetsFor(chain)
|
|
488
483
|
);
|
|
489
484
|
const down = cache.render(
|
|
490
485
|
ctx,
|
|
491
|
-
"bloom/downsample",
|
|
486
|
+
rg11 ? "bloom/downsample/rg11" : "bloom/downsample",
|
|
492
487
|
"shard::post::bloom",
|
|
493
488
|
"downsample",
|
|
494
489
|
[layout],
|
|
495
|
-
|
|
490
|
+
targetsFor(chain)
|
|
496
491
|
);
|
|
497
492
|
const up = cache.render(
|
|
498
493
|
ctx,
|
|
499
|
-
"bloom/upsample",
|
|
494
|
+
rg11 ? "bloom/upsample/rg11" : "bloom/upsample",
|
|
500
495
|
"shard::post::bloom",
|
|
501
496
|
"upsample",
|
|
502
497
|
[layout],
|
|
503
|
-
[{ format:
|
|
504
|
-
);
|
|
505
|
-
const composite = cache.render(
|
|
506
|
-
ctx,
|
|
507
|
-
"bloom/composite",
|
|
508
|
-
"shard::post::bloom",
|
|
509
|
-
"composite",
|
|
510
|
-
[layout],
|
|
511
|
-
HDR
|
|
498
|
+
[{ format: chain.format, blend: { color: add, alpha: add } }]
|
|
512
499
|
);
|
|
513
|
-
if (!prefilter || !down || !up
|
|
500
|
+
if (!prefilter || !down || !up) return;
|
|
514
501
|
const { cam } = v;
|
|
515
502
|
const b = cam.post.bloom;
|
|
516
503
|
const input = ctx.texture("bloom-in");
|
|
517
|
-
const chain = ctx.texture("bloom");
|
|
518
|
-
const out = ctx.texture("bloom-out");
|
|
519
504
|
const levels = Math.min(chain.mipLevelCount, bloomLevels(cam));
|
|
520
505
|
const buffer = cache.buffer(gpu, `${ctx.view.name}/bloom`, 256 * 20);
|
|
521
506
|
const slot = (i2, w, h) => {
|
|
@@ -534,7 +519,6 @@ function bloomNode() {
|
|
|
534
519
|
slot(n++, input.width, input.height);
|
|
535
520
|
for (let k = 1; k < levels; k++) slot(n++, ...mipSize(k - 1));
|
|
536
521
|
for (let k = levels - 2; k >= 0; k--) slot(n++, ...mipSize(k + 1));
|
|
537
|
-
slot(n++, chain.width, chain.height);
|
|
538
522
|
buffer.write(params, 0, 0, n * 64);
|
|
539
523
|
const view = (k) => chain.createView({ baseMipLevel: k, mipLevelCount: 1 });
|
|
540
524
|
const group = (i2, source, mip) => cache.group(
|
|
@@ -545,8 +529,7 @@ function bloomNode() {
|
|
|
545
529
|
() => [
|
|
546
530
|
{ binding: 0, resource: source === chain ? view(mip) : source.createView() },
|
|
547
531
|
{ binding: 1, resource: cache.sampler(gpu) },
|
|
548
|
-
{ binding: 2, resource: { buffer: buffer.buffer, offset: i2 * 256, size: 32 } }
|
|
549
|
-
{ binding: 3, resource: input.createView() }
|
|
532
|
+
{ binding: 2, resource: { buffer: buffer.buffer, offset: i2 * 256, size: 32 } }
|
|
550
533
|
]
|
|
551
534
|
);
|
|
552
535
|
let i = 0;
|
|
@@ -560,10 +543,31 @@ function bloomNode() {
|
|
|
560
543
|
draw("post/bloom", prefilter, view(0), input, 0);
|
|
561
544
|
for (let k = 1; k < levels; k++) draw("post/bloom-down", down, view(k), chain, k - 1);
|
|
562
545
|
for (let k = levels - 2; k >= 0; k--) draw("post/bloom-up", up, view(k), chain, k + 1, true);
|
|
563
|
-
draw("post/bloom-composite", composite, out.createView(), chain, 0);
|
|
564
546
|
}
|
|
565
547
|
};
|
|
566
548
|
}
|
|
549
|
+
function bloomGlow(cam, chain, out, at) {
|
|
550
|
+
const b = cam.post.bloom;
|
|
551
|
+
out[at] = chain ? b.intensity : 0;
|
|
552
|
+
out[at + 1] = chain ? chain.mipLevelCount : 1;
|
|
553
|
+
out[at + 2] = chain && b.threshold <= 0 ? 1 : 0;
|
|
554
|
+
out[at + 3] = 0;
|
|
555
|
+
}
|
|
556
|
+
const whites = /* @__PURE__ */ new WeakMap();
|
|
557
|
+
function whiteTexture(gpu) {
|
|
558
|
+
let t = whites.get(gpu.device);
|
|
559
|
+
if (!t) {
|
|
560
|
+
t = gpu.device.createTexture({
|
|
561
|
+
label: "post/white",
|
|
562
|
+
size: [1, 1],
|
|
563
|
+
format: "rgba8unorm",
|
|
564
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
565
|
+
});
|
|
566
|
+
gpu.device.queue.writeTexture({ texture: t }, new Uint8Array([255, 255, 255, 255]), {}, [1, 1]);
|
|
567
|
+
whites.set(gpu.device, t);
|
|
568
|
+
}
|
|
569
|
+
return t;
|
|
570
|
+
}
|
|
567
571
|
const METER_READBACKS = 3;
|
|
568
572
|
const ExposureMeters = defineResource("render/ExposureMeters", {
|
|
569
573
|
description: "Auto exposure per camera: the last metered EV100 and the adapted one.",
|
|
@@ -613,21 +617,24 @@ function autoExposureNode(baseline) {
|
|
|
613
617
|
kind: "raw",
|
|
614
618
|
phase: RenderPhase.Post + 50,
|
|
615
619
|
enabled: hasEffect(PostEffect.AutoExposure),
|
|
616
|
-
reads: ["post-hdr"],
|
|
620
|
+
reads: ["post-hdr", "bloom"],
|
|
617
621
|
sideEffects: true,
|
|
618
622
|
run: (ctx) => {
|
|
619
623
|
const v = forwardView(ctx);
|
|
620
624
|
if (!v) return;
|
|
621
625
|
const gpu = ctx.gpu;
|
|
626
|
+
const bloom = (v.cam.post.effects & PostEffect.Bloom) !== 0;
|
|
627
|
+
const chain = bloom ? ctx.texture("bloom") : void 0;
|
|
622
628
|
if (baseline) {
|
|
623
629
|
const layout2 = baseline.meterLayout(gpu);
|
|
624
630
|
const pipeline2 = cache.render(
|
|
625
631
|
ctx,
|
|
626
|
-
"post/exposure-meter",
|
|
632
|
+
bloom ? "post/exposure-meter/bloom" : "post/exposure-meter",
|
|
627
633
|
"shard::post::baseline::meter",
|
|
628
634
|
"main",
|
|
629
635
|
[layout2],
|
|
630
|
-
[{ format: "rg16float" }]
|
|
636
|
+
[{ format: "rg16float" }],
|
|
637
|
+
{ BLOOM: bloom }
|
|
631
638
|
);
|
|
632
639
|
if (!pipeline2) return;
|
|
633
640
|
let m = meterViews.get(ctx.view.name);
|
|
@@ -636,7 +643,8 @@ function autoExposureNode(baseline) {
|
|
|
636
643
|
meterViews.set(ctx.view.name, m);
|
|
637
644
|
}
|
|
638
645
|
const input2 = ctx.texture("post-hdr");
|
|
639
|
-
const
|
|
646
|
+
const level02 = chain ?? whiteTexture(gpu);
|
|
647
|
+
const key = `${idOf(input2)}/${v.pv.uniform.version}/${idOf(level02)}`;
|
|
640
648
|
if (!m.group || m.groupKey !== key) {
|
|
641
649
|
m.groupKey = key;
|
|
642
650
|
m.group = gpu.device.createBindGroup({
|
|
@@ -645,7 +653,9 @@ function autoExposureNode(baseline) {
|
|
|
645
653
|
entries: [
|
|
646
654
|
{ binding: 0, resource: { buffer: v.pv.uniform.buffer } },
|
|
647
655
|
{ binding: 1, resource: input2.createView() },
|
|
648
|
-
{ binding: 2, resource: { buffer: m.params } }
|
|
656
|
+
{ binding: 2, resource: { buffer: m.params } },
|
|
657
|
+
{ binding: 3, resource: level02.createView({ mipLevelCount: 1 }) },
|
|
658
|
+
{ binding: 4, resource: cache.sampler(gpu) }
|
|
649
659
|
]
|
|
650
660
|
});
|
|
651
661
|
}
|
|
@@ -653,7 +663,15 @@ function autoExposureNode(baseline) {
|
|
|
653
663
|
scratch[1] = METER_BINS_PER_EV;
|
|
654
664
|
scratch[2] = v.cam.post.exposure.metering;
|
|
655
665
|
scratch[3] = 0;
|
|
656
|
-
|
|
666
|
+
bloomGlow(v.cam, chain, scratch, 4);
|
|
667
|
+
const k2 = baseline.renderMeter(
|
|
668
|
+
ctx,
|
|
669
|
+
m,
|
|
670
|
+
pipeline2,
|
|
671
|
+
m.group,
|
|
672
|
+
scratch.subarray(0, 4),
|
|
673
|
+
scratch.subarray(4, 8)
|
|
674
|
+
);
|
|
657
675
|
if (k2 === void 0) return;
|
|
658
676
|
const meters2 = ctx.world.resource(ExposureMeters);
|
|
659
677
|
const entity2 = v.cam.entity;
|
|
@@ -685,14 +703,21 @@ function autoExposureNode(baseline) {
|
|
|
685
703
|
uniform(0, C),
|
|
686
704
|
{ binding: 1, visibility: C, texture: { sampleType: "unfilterable-float" } },
|
|
687
705
|
{ binding: 2, visibility: C, buffer: { type: "storage" } },
|
|
688
|
-
uniform(3, C)
|
|
706
|
+
uniform(3, C),
|
|
707
|
+
{ binding: 4, visibility: C, texture: { sampleType: "float" } },
|
|
708
|
+
{ binding: 5, visibility: C, sampler: { type: "filtering" } }
|
|
689
709
|
]
|
|
690
710
|
});
|
|
691
711
|
perView.clear();
|
|
692
712
|
}
|
|
693
|
-
const pipeline = cache.compute(
|
|
694
|
-
|
|
695
|
-
|
|
713
|
+
const pipeline = cache.compute(
|
|
714
|
+
ctx,
|
|
715
|
+
bloom ? "post/exposure/bloom" : "post/exposure",
|
|
716
|
+
"shard::post::exposure",
|
|
717
|
+
"meter",
|
|
718
|
+
[layout],
|
|
719
|
+
{ BLOOM: bloom }
|
|
720
|
+
);
|
|
696
721
|
if (!pipeline) return;
|
|
697
722
|
let state = perView.get(ctx.view.name);
|
|
698
723
|
if (!state || state.generation !== gpu.generation) {
|
|
@@ -720,20 +745,24 @@ function autoExposureNode(baseline) {
|
|
|
720
745
|
scratch[1] = METER_BINS_PER_EV;
|
|
721
746
|
scratch[2] = e.metering;
|
|
722
747
|
scratch[3] = 0;
|
|
723
|
-
|
|
724
|
-
params.
|
|
748
|
+
bloomGlow(v.cam, chain, scratch, 4);
|
|
749
|
+
const params = cache.buffer(gpu, `${ctx.view.name}/exposure`, 32);
|
|
750
|
+
params.write(scratch, 0, 0, 8);
|
|
725
751
|
const input = ctx.texture("post-hdr");
|
|
726
752
|
const histogram = state.histogram;
|
|
753
|
+
const level0 = chain ?? whiteTexture(gpu);
|
|
727
754
|
const group = cache.group(
|
|
728
755
|
gpu,
|
|
729
756
|
`${ctx.view.name}/exposure`,
|
|
730
|
-
`${idOf(input)}/${idOf(histogram)}/${v.pv.uniform.version}/${params.version}`,
|
|
757
|
+
`${idOf(input)}/${idOf(histogram)}/${v.pv.uniform.version}/${params.version}/${idOf(level0)}`,
|
|
731
758
|
layout,
|
|
732
759
|
() => [
|
|
733
760
|
{ binding: 0, resource: { buffer: v.pv.uniform.buffer } },
|
|
734
761
|
{ binding: 1, resource: input.createView() },
|
|
735
762
|
{ binding: 2, resource: { buffer: histogram } },
|
|
736
|
-
{ binding: 3, resource: { buffer: params.buffer } }
|
|
763
|
+
{ binding: 3, resource: { buffer: params.buffer } },
|
|
764
|
+
{ binding: 4, resource: level0.createView({ mipLevelCount: 1 }) },
|
|
765
|
+
{ binding: 5, resource: cache.sampler(gpu) }
|
|
737
766
|
]
|
|
738
767
|
);
|
|
739
768
|
gpu.device.queue.writeBuffer(histogram, 0, zero);
|
|
@@ -825,13 +854,15 @@ const halfSize = { divide: 2 };
|
|
|
825
854
|
function addPostNodes(world, baseline) {
|
|
826
855
|
const graph = world.resource(Graph);
|
|
827
856
|
world.initResource(ExposureMeters);
|
|
828
|
-
graph.declare({ name: "prepass-normal", format: "
|
|
857
|
+
graph.declare({ name: "prepass-normal", format: "rg16float" });
|
|
829
858
|
graph.declare({ name: "velocity", format: "rg16float" });
|
|
830
859
|
graph.declare({ name: "prepass-depth", format: "depth32float" });
|
|
831
|
-
graph.declare({ name: "ssao", format: "
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
graph.declare({ name: "post-
|
|
860
|
+
graph.declare({ name: "ssao", format: "rg16float", size: halfSize });
|
|
861
|
+
const gpu = world.resource(Gpu);
|
|
862
|
+
const chainFormat = (view) => postFormat(gpu, view);
|
|
863
|
+
graph.declare({ name: "post-a", format: chainFormat });
|
|
864
|
+
graph.declare({ name: "post-b", format: chainFormat });
|
|
865
|
+
graph.declare({ name: "taa-history", format: "rgba16float" });
|
|
835
866
|
graph.declare({ name: "dof-half", format: "rgba16float", size: halfSize });
|
|
836
867
|
graph.declare({ name: "dof-blur", format: "rgba16float", size: halfSize });
|
|
837
868
|
graph.declare({
|
|
@@ -935,6 +966,7 @@ export {
|
|
|
935
966
|
POST_NODES,
|
|
936
967
|
adaptExposure,
|
|
937
968
|
addPostNodes,
|
|
969
|
+
bloomGlow,
|
|
938
970
|
bloomLevels,
|
|
939
971
|
describePost,
|
|
940
972
|
histogramEv
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { type World } from '@aethervtt/shard-core';
|
|
2
|
+
/** Each post effect's component and settings, by the name `?effects=` uses. */
|
|
3
|
+
export declare const POST_EFFECTS: {
|
|
4
|
+
readonly bloom: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
5
|
+
readonly intensity: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
6
|
+
readonly threshold: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
7
|
+
readonly knee: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
8
|
+
readonly radius: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
9
|
+
}>, {
|
|
10
|
+
readonly intensity: 0.12;
|
|
11
|
+
}];
|
|
12
|
+
readonly exposure: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
13
|
+
readonly minEv: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
14
|
+
readonly maxEv: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
15
|
+
readonly compensation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
16
|
+
readonly speedUp: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
17
|
+
readonly speedDown: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
18
|
+
readonly metering: import("@aethervtt/shard-core").FieldType<"average" | "center" | "spot", "u8">;
|
|
19
|
+
}>, {
|
|
20
|
+
readonly compensation: 0.5;
|
|
21
|
+
}];
|
|
22
|
+
readonly dof: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
23
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"bokeh" | "gaussian", "u8">;
|
|
24
|
+
readonly focusDistance: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
25
|
+
readonly maxBlur: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
26
|
+
}>, {
|
|
27
|
+
readonly focusDistance: 9;
|
|
28
|
+
readonly maxBlur: 0.012;
|
|
29
|
+
}];
|
|
30
|
+
readonly 'motion-blur': readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
31
|
+
readonly samples: import("@aethervtt/shard-core").FieldType<number, "u32">;
|
|
32
|
+
readonly maxBlur: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
33
|
+
}>, {}];
|
|
34
|
+
readonly taa: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
35
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"fxaa" | "msaa" | "none" | "taa", "u8">;
|
|
36
|
+
}>, {
|
|
37
|
+
readonly mode: 'taa';
|
|
38
|
+
}];
|
|
39
|
+
readonly fxaa: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
40
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"fxaa" | "msaa" | "none" | "taa", "u8">;
|
|
41
|
+
}>, {
|
|
42
|
+
readonly mode: 'fxaa';
|
|
43
|
+
}];
|
|
44
|
+
readonly noaa: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
45
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"fxaa" | "msaa" | "none" | "taa", "u8">;
|
|
46
|
+
}>, {
|
|
47
|
+
readonly mode: 'none';
|
|
48
|
+
}];
|
|
49
|
+
readonly ssao: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
50
|
+
readonly radius: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
51
|
+
readonly intensity: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
52
|
+
readonly quality: import("@aethervtt/shard-core").FieldType<"high" | "low" | "medium", "u8">;
|
|
53
|
+
}>, {
|
|
54
|
+
readonly radius: 0.6;
|
|
55
|
+
}];
|
|
56
|
+
readonly fog: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
57
|
+
readonly color: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
|
|
58
|
+
readonly density: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
59
|
+
readonly heightFalloff: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
60
|
+
readonly start: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
61
|
+
readonly sunScattering: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
62
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"add" | "default", "u8">;
|
|
63
|
+
}>, {
|
|
64
|
+
readonly density: 0.015;
|
|
65
|
+
readonly heightFalloff: 0.08;
|
|
66
|
+
readonly sunScattering: 0.6;
|
|
67
|
+
}];
|
|
68
|
+
readonly grading: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
69
|
+
readonly temperature: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
70
|
+
readonly tint: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
71
|
+
readonly saturation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
72
|
+
readonly contrast: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
73
|
+
readonly lift: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
74
|
+
readonly gamma: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
75
|
+
readonly gain: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
76
|
+
readonly lut: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
77
|
+
}>, {
|
|
78
|
+
readonly temperature: 0.1;
|
|
79
|
+
readonly saturation: 1.1;
|
|
80
|
+
readonly contrast: 1.05;
|
|
81
|
+
}];
|
|
82
|
+
readonly vignette: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
83
|
+
readonly intensity: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
84
|
+
readonly smoothness: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
85
|
+
}>, {
|
|
86
|
+
readonly intensity: 0.25;
|
|
87
|
+
}];
|
|
88
|
+
};
|
|
89
|
+
export type PostEffectName = keyof typeof POST_EFFECTS;
|
|
90
|
+
/** The full stack: every effect but the other anti-aliasing modes (TAA is on). */
|
|
91
|
+
export declare const ALL_POST_EFFECTS: readonly PostEffectName[];
|
|
92
|
+
/** The components for effect names; unknown names are skipped. */
|
|
93
|
+
export declare function postEffects(names: readonly string[]): unknown[];
|
|
94
|
+
/** A ball on a circle round the courtyard. */
|
|
95
|
+
export declare const PostSpin: import("@aethervtt/shard-core").ComponentDef<{
|
|
96
|
+
readonly radius: import("@aethervtt/shard-core").FieldType<number, "f32"> & ((options?: import("@aethervtt/shard-core").FieldOptions<number> | undefined) => import("@aethervtt/shard-core").FieldType<number, "f32">);
|
|
97
|
+
readonly speed: import("@aethervtt/shard-core").FieldType<number, "f32"> & ((options?: import("@aethervtt/shard-core").FieldOptions<number> | undefined) => import("@aethervtt/shard-core").FieldType<number, "f32">);
|
|
98
|
+
readonly height: import("@aethervtt/shard-core").FieldType<number, "f32"> & ((options?: import("@aethervtt/shard-core").FieldOptions<number> | undefined) => import("@aethervtt/shard-core").FieldType<number, "f32">);
|
|
99
|
+
}>;
|
|
100
|
+
/** Moves PostSpin balls along their circles by `Time.elapsed`. */
|
|
101
|
+
export declare const postSpin: import("@aethervtt/shard-core").SystemDef<{
|
|
102
|
+
q: import("@aethervtt/shard-core").Query;
|
|
103
|
+
}>;
|
|
104
|
+
/** The courtyard's moving parts: PostSpin and its system. Add it with the render plugins. */
|
|
105
|
+
export declare const postScenePlugin: import("@aethervtt/shard-runtime").Plugin;
|
|
106
|
+
/** Where the courtyard is looked at from: 9 m out, 2.2 m up. */
|
|
107
|
+
export declare const POST_EYE: [number, number, number];
|
|
108
|
+
export declare const POST_TARGET: [number, number, number];
|
|
109
|
+
/**
|
|
110
|
+
* The `post-stack` camera path: from POST_EYE, swinging 30° either side of the courtyard's axis
|
|
111
|
+
* once every 20 s and drifting 1.5 m in and out, always looking at POST_TARGET. `seconds` along it.
|
|
112
|
+
*/
|
|
113
|
+
export declare function postCamera(seconds: number): {
|
|
114
|
+
eye: [number, number, number];
|
|
115
|
+
target: [number, number, number];
|
|
116
|
+
};
|
|
117
|
+
/** The camera's lens: a physical exposure, so auto exposure and DoF read real values. */
|
|
118
|
+
export declare const POST_LENS: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
119
|
+
readonly aperture: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
120
|
+
readonly shutterSpeed: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
121
|
+
readonly iso: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
122
|
+
readonly sensorHeight: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
123
|
+
readonly focalLength: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
124
|
+
}>, {
|
|
125
|
+
readonly aperture: 5.6;
|
|
126
|
+
readonly shutterSpeed: number;
|
|
127
|
+
readonly iso: 100;
|
|
128
|
+
}];
|
|
129
|
+
/** The courtyard, its sun and sky; the camera is the caller's. */
|
|
130
|
+
export declare function spawnPostScene(world: World): void;
|