@aethervtt/shard-render 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/dist/ablation.d.ts +74 -0
  2. package/dist/ablation.js +117 -0
  3. package/dist/assets.d.ts +3 -0
  4. package/dist/assets.js +43 -2
  5. package/dist/atmosphere-nodes.d.ts +9 -0
  6. package/dist/atmosphere-nodes.js +157 -30
  7. package/dist/atmosphere-plugin.js +8 -2
  8. package/dist/atmosphere-shaders.js +17 -5
  9. package/dist/baseline/atmosphere.d.ts +16 -0
  10. package/dist/baseline/atmosphere.js +301 -0
  11. package/dist/baseline/data-texture.d.ts +36 -0
  12. package/dist/baseline/data-texture.js +181 -0
  13. package/dist/baseline/environment.d.ts +14 -0
  14. package/dist/baseline/environment.js +368 -0
  15. package/dist/baseline/exposure.d.ts +25 -0
  16. package/dist/baseline/exposure.js +133 -0
  17. package/dist/baseline/lights.d.ts +33 -0
  18. package/dist/baseline/lights.js +133 -0
  19. package/dist/baseline/textures.d.ts +97 -0
  20. package/dist/baseline/textures.js +203 -0
  21. package/dist/camera.d.ts +2 -0
  22. package/dist/camera.js +4 -0
  23. package/dist/crowd.d.ts +21 -0
  24. package/dist/crowd.js +121 -0
  25. package/dist/culling-shaders.js +21 -2
  26. package/dist/culling.d.ts +11 -2
  27. package/dist/culling.js +44 -18
  28. package/dist/cutaway-plugin.d.ts +48 -0
  29. package/dist/cutaway-plugin.js +364 -0
  30. package/dist/cutaway.d.ts +27 -0
  31. package/dist/cutaway.js +49 -0
  32. package/dist/data-store.d.ts +69 -0
  33. package/dist/data-store.js +105 -0
  34. package/dist/debug-shaders.js +17 -3
  35. package/dist/debug-views.js +4 -2
  36. package/dist/decals-plugin.d.ts +5 -0
  37. package/dist/decals-plugin.js +54 -0
  38. package/dist/decals.d.ts +69 -0
  39. package/dist/decals.js +326 -0
  40. package/dist/deferred-shaders.js +2 -0
  41. package/dist/deferred.js +28 -4
  42. package/dist/deform.d.ts +5 -4
  43. package/dist/deform.js +4 -9
  44. package/dist/describe-plugin.d.ts +4 -2
  45. package/dist/describe-plugin.js +75 -3
  46. package/dist/display-nodes.js +37 -13
  47. package/dist/dynamic-resolution.d.ts +2 -0
  48. package/dist/dynamic-resolution.js +6 -1
  49. package/dist/environment-plugin.js +17 -2
  50. package/dist/environment-state.d.ts +7 -3
  51. package/dist/environment-state.js +20 -9
  52. package/dist/environment.js +7 -1
  53. package/dist/features.d.ts +49 -0
  54. package/dist/features.js +106 -0
  55. package/dist/foliage-budget.d.ts +64 -0
  56. package/dist/foliage-budget.js +108 -0
  57. package/dist/foliage-path.d.ts +19 -0
  58. package/dist/foliage-path.js +7 -0
  59. package/dist/foliage-shaders.d.ts +11 -0
  60. package/dist/foliage-shaders.js +504 -0
  61. package/dist/foliage.d.ts +194 -0
  62. package/dist/foliage.js +1027 -0
  63. package/dist/forward.d.ts +38 -2
  64. package/dist/forward.js +608 -186
  65. package/dist/fxaa.js +12 -2
  66. package/dist/gizmos-plugin.js +7 -0
  67. package/dist/gizmos.d.ts +3 -3
  68. package/dist/gizmos.js +11 -13
  69. package/dist/gpu-assets.d.ts +58 -4
  70. package/dist/gpu-assets.js +191 -8
  71. package/dist/graph.d.ts +45 -0
  72. package/dist/graph.js +169 -19
  73. package/dist/health.d.ts +58 -0
  74. package/dist/health.js +112 -0
  75. package/dist/index.d.ts +40 -11
  76. package/dist/index.js +343 -9
  77. package/dist/instances.d.ts +119 -8
  78. package/dist/instances.js +387 -78
  79. package/dist/interior-plugin.d.ts +121 -0
  80. package/dist/interior-plugin.js +465 -0
  81. package/dist/interior.d.ts +24 -0
  82. package/dist/interior.js +13 -0
  83. package/dist/layers.d.ts +35 -0
  84. package/dist/layers.js +49 -0
  85. package/dist/lens-plugin.d.ts +43 -0
  86. package/dist/lens-plugin.js +304 -0
  87. package/dist/lens.d.ts +41 -0
  88. package/dist/lens.js +99 -0
  89. package/dist/lights.d.ts +50 -5
  90. package/dist/lights.js +101 -22
  91. package/dist/material-pipelines.d.ts +37 -4
  92. package/dist/material-pipelines.js +102 -5
  93. package/dist/materials.d.ts +23 -0
  94. package/dist/materials.js +44 -1
  95. package/dist/outline-plugin.d.ts +48 -0
  96. package/dist/outline-plugin.js +529 -0
  97. package/dist/outline.d.ts +15 -0
  98. package/dist/outline.js +42 -0
  99. package/dist/overlay-registry.d.ts +3 -1
  100. package/dist/overlay-registry.js +3 -1
  101. package/dist/overlays.js +6 -2
  102. package/dist/perf-overlay.d.ts +9 -0
  103. package/dist/perf-overlay.js +125 -0
  104. package/dist/picking.d.ts +17 -0
  105. package/dist/picking.js +40 -11
  106. package/dist/pixel-perfect-plugin.js +10 -2
  107. package/dist/plugin.d.ts +69 -7
  108. package/dist/plugin.js +223 -25
  109. package/dist/post-common.d.ts +22 -2
  110. package/dist/post-common.js +82 -2
  111. package/dist/post-nodes.d.ts +7 -1
  112. package/dist/post-nodes.js +235 -111
  113. package/dist/post-plugin.js +5 -3
  114. package/dist/post-scene.d.ts +130 -0
  115. package/dist/post-scene.js +164 -0
  116. package/dist/post-shaders.js +85 -62
  117. package/dist/post.js +11 -1
  118. package/dist/projection.d.ts +24 -0
  119. package/dist/projection.js +87 -0
  120. package/dist/readback.d.ts +1 -0
  121. package/dist/readback.js +1 -0
  122. package/dist/render-scale.js +1 -0
  123. package/dist/ribbons-plugin.d.ts +5 -0
  124. package/dist/ribbons-plugin.js +59 -0
  125. package/dist/ribbons.d.ts +44 -0
  126. package/dist/ribbons.js +288 -0
  127. package/dist/scenario.d.ts +70 -0
  128. package/dist/scenario.js +125 -0
  129. package/dist/screen-effects.d.ts +97 -0
  130. package/dist/screen-effects.js +192 -0
  131. package/dist/shaders.d.ts +13 -0
  132. package/dist/shaders.js +189 -34
  133. package/dist/shadow-catcher.d.ts +9 -0
  134. package/dist/shadow-catcher.js +133 -0
  135. package/dist/shadows.d.ts +27 -2
  136. package/dist/shadows.js +100 -20
  137. package/dist/standard-fields.d.ts +1 -0
  138. package/dist/standard-fields.js +5 -0
  139. package/dist/standard.js +18 -0
  140. package/dist/stats.d.ts +45 -1
  141. package/dist/stats.js +91 -3
  142. package/dist/surface-model.d.ts +57 -0
  143. package/dist/surface-model.js +289 -0
  144. package/dist/surface-shaders.d.ts +3 -0
  145. package/dist/surface-shaders.js +160 -0
  146. package/dist/surface.d.ts +40 -0
  147. package/dist/surface.js +86 -0
  148. package/dist/target.d.ts +1 -13
  149. package/dist/target.js +1 -26
  150. package/dist/testing.d.ts +11 -0
  151. package/dist/testing.js +39 -1
  152. package/dist/tier.d.ts +13 -0
  153. package/dist/tier.js +14 -0
  154. package/dist/timer.d.ts +18 -1
  155. package/dist/timer.js +73 -20
  156. package/dist/variants.d.ts +37 -0
  157. package/dist/variants.js +104 -0
  158. package/dist/vfx-draw.d.ts +37 -0
  159. package/dist/vfx-draw.js +291 -0
  160. package/dist/vfx-state.d.ts +42 -0
  161. package/dist/vfx-state.js +81 -0
  162. package/dist/vfx.d.ts +69 -0
  163. package/dist/vfx.js +103 -0
  164. package/dist/view-visibility-plugin.d.ts +61 -0
  165. package/dist/view-visibility-plugin.js +267 -0
  166. package/dist/view-visibility.d.ts +28 -0
  167. package/dist/view-visibility.js +46 -0
  168. package/dist/view.d.ts +29 -1
  169. package/dist/view.js +57 -34
  170. package/package.json +23 -11
@@ -6,10 +6,13 @@ import {
6
6
  Environments,
7
7
  environmentBakers
8
8
  } from "./environment.js";
9
- import { ForwardStateResource, sceneColor } from "./forward.js";
9
+ import { addRenderFeatures } from "./features.js";
10
+ import { ForwardStateResource, sceneColor, viewBindGroup } from "./forward.js";
10
11
  import { RenderPhase } from "./graph.js";
11
12
  import { Gpu, Graph, Shaders, Views } from "./plugin.js";
12
13
  import { hasEffect, PostEffect } from "./post.js";
14
+ import { roundsRg11, targetsFor } from "./post-common.js";
15
+ import { bindingDimension, depthReadEntry } from "./tier.js";
13
16
  import { cameraOf } from "./view.js";
14
17
  const LUT_LAYERS = 8;
15
18
  const PARAMS_FLOATS = 32;
@@ -39,22 +42,26 @@ class AtmosphereGpu {
39
42
  frame = 0;
40
43
  pipelines = /* @__PURE__ */ new Map();
41
44
  layouts;
45
+ /** The baseline tier's passes (0064): fragment passes, loaded only on a baseline device. */
46
+ baseline;
42
47
  groups = /* @__PURE__ */ new Map();
43
48
  ensure(gpu) {
44
49
  if (this.generation === gpu.generation) return;
45
50
  this.generation = gpu.generation;
46
- const usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_SRC;
51
+ const usage = GPUTextureUsage.TEXTURE_BINDING | written(gpu) | GPUTextureUsage.COPY_SRC;
47
52
  this.transmittance = gpu.device.createTexture({
48
53
  label: "atmosphere/transmittance",
49
54
  size: [TRANSMITTANCE_W, TRANSMITTANCE_H, LUT_LAYERS],
50
55
  format: "rgba16float",
51
- usage
56
+ usage,
57
+ ...bindingDimension(gpu, "2d-array")
52
58
  });
53
59
  this.multiscatter = gpu.device.createTexture({
54
60
  label: "atmosphere/multiscatter",
55
61
  size: [MULTISCATTER_SIZE, MULTISCATTER_SIZE, LUT_LAYERS],
56
62
  format: "rgba16float",
57
- usage
63
+ usage,
64
+ ...bindingDimension(gpu, "2d-array")
58
65
  });
59
66
  this.transmittanceArray = this.transmittance.createView({ dimension: "2d-array" });
60
67
  this.multiscatterArray = this.multiscatter.createView({ dimension: "2d-array" });
@@ -124,6 +131,7 @@ class AtmosphereGpu {
124
131
  scatter: void 0,
125
132
  transmittance: void 0,
126
133
  computed: -1,
134
+ packed: false,
127
135
  generation: gpu.generation,
128
136
  seen: 0
129
137
  };
@@ -141,6 +149,9 @@ class AtmosphereGpu {
141
149
  return g.group;
142
150
  }
143
151
  }
152
+ function written(gpu) {
153
+ return gpu.tier === "full" ? GPUTextureUsage.STORAGE_BINDING : GPUTextureUsage.RENDER_ATTACHMENT;
154
+ }
144
155
  const AtmosphereGpuResource = defineResource("render/AtmosphereGpu", {
145
156
  description: "Atmosphere LUTs, per-camera sky-view LUTs and froxels, and their pipelines.",
146
157
  init: () => new AtmosphereGpu()
@@ -194,6 +205,7 @@ function relativeInverse(cam, out) {
194
205
  }
195
206
  function packView(ca, cam, c, out) {
196
207
  out.fill(0);
208
+ c.packed = false;
197
209
  for (const rec of ca.list) if (rec.layerVersion < 0) return;
198
210
  const n = Math.min(ca.list.length, 4);
199
211
  for (let i = 0; i < n; i++) packParams(ca.list[i], out, i * PARAMS_FLOATS, ca.origins, i * 4);
@@ -219,6 +231,7 @@ function packView(ca, cam, c, out) {
219
231
  out[O_CAMERA + 1] = cam.position[1];
220
232
  out[O_CAMERA + 2] = cam.position[2];
221
233
  out[O_CAMERA + 3] = cam.exposure;
234
+ c.packed = true;
222
235
  }
223
236
  const uploadAtmospheres = defineSystem({
224
237
  name: "render/atmosphere-upload",
@@ -259,7 +272,7 @@ function ensureViewTextures(gpu, c, ca) {
259
272
  label: "atmosphere/sky-view",
260
273
  size: [sw, sh],
261
274
  format: "rgba16float",
262
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING
275
+ usage: GPUTextureUsage.TEXTURE_BINDING | written(gpu)
263
276
  });
264
277
  }
265
278
  const [fx, fy, fz] = ca.froxels;
@@ -271,7 +284,7 @@ function ensureViewTextures(gpu, c, ca) {
271
284
  size: [fx, fy, fz],
272
285
  dimension: "3d",
273
286
  format: "rgba16float",
274
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING
287
+ usage: GPUTextureUsage.TEXTURE_BINDING | written(gpu)
275
288
  };
276
289
  c.scatter = gpu.device.createTexture(desc);
277
290
  c.transmittance = gpu.device.createTexture({
@@ -309,7 +322,7 @@ function layouts(gpu, s) {
309
322
  viewDimension
310
323
  }
311
324
  });
312
- s.layouts = {
325
+ const computeLayouts = gpu.tier === "full" ? {
313
326
  transmittance: gpu.layouts.bindGroupLayout({
314
327
  label: "atmosphere/transmittance",
315
328
  entries: [uniform(0, C), storage(1, "2d")]
@@ -335,13 +348,22 @@ function layouts(gpu, s) {
335
348
  }),
336
349
  bake: gpu.layouts.bindGroupLayout({
337
350
  label: "atmosphere/bake",
338
- entries: [uniform(0, C), array(1, C), array(2, C), filtering(3, C), storage(4, "2d-array")]
339
- }),
351
+ entries: [
352
+ uniform(0, C),
353
+ array(1, C),
354
+ array(2, C),
355
+ filtering(3, C),
356
+ storage(4, "2d-array")
357
+ ]
358
+ })
359
+ } : {};
360
+ s.layouts = {
361
+ ...computeLayouts,
340
362
  composite: gpu.layouts.bindGroupLayout({
341
363
  label: "atmosphere/composite",
342
364
  entries: [
343
365
  { binding: 0, visibility: F, texture: { sampleType: "unfilterable-float" } },
344
- { binding: 1, visibility: F, texture: { sampleType: "depth" } },
366
+ depthReadEntry(gpu, 1, F),
345
367
  uniform(2, F),
346
368
  array(3, F),
347
369
  array(4, F),
@@ -350,6 +372,21 @@ function layouts(gpu, s) {
350
372
  { binding: 7, visibility: F, texture: { sampleType: "float", viewDimension: "3d" } }
351
373
  ]
352
374
  }),
375
+ // The composite with fog folded in: fog's parameters after the haze's bindings.
376
+ compositeFog: gpu.layouts.bindGroupLayout({
377
+ label: "atmosphere/composite-fog",
378
+ entries: [
379
+ { binding: 0, visibility: F, texture: { sampleType: "unfilterable-float" } },
380
+ depthReadEntry(gpu, 1, F),
381
+ uniform(2, F),
382
+ array(3, F),
383
+ array(4, F),
384
+ filtering(5, F),
385
+ { binding: 6, visibility: F, texture: { sampleType: "float", viewDimension: "3d" } },
386
+ { binding: 7, visibility: F, texture: { sampleType: "float", viewDimension: "3d" } },
387
+ uniform(8, F)
388
+ ]
389
+ }),
353
390
  viewOnly: gpu.layouts.bindGroupLayout({
354
391
  label: "atmosphere/view",
355
392
  entries: [uniform(0, GPUShaderStage.VERTEX | F)]
@@ -404,6 +441,10 @@ function lutNode() {
404
441
  s.lutFrame = s.frame;
405
442
  return;
406
443
  }
444
+ if (s.baseline) {
445
+ if (s.baseline.runLuts(ctx, s)) s.lutFrame = s.frame;
446
+ return;
447
+ }
407
448
  const t = compute(ctx, s, "transmittance", "shard::atmosphere::transmittance_lut");
408
449
  const m = compute(ctx, s, "multiscatter", "shard::atmosphere::multiscatter_lut");
409
450
  if (!t || !m) return;
@@ -471,6 +512,16 @@ function viewNode() {
471
512
  const c = s.cameras.get(cam.entity);
472
513
  if (!c?.skyView || !c.scatter || !c.transmittance) return;
473
514
  if (ca.primary.layerVersion !== ca.primary.version) return;
515
+ if (s.baseline) {
516
+ const view = {
517
+ uniform: c.uniform,
518
+ skyView: c.skyView,
519
+ scatter: c.scatter,
520
+ transmittance: c.transmittance
521
+ };
522
+ if (s.baseline.runView(ctx, s, ca, view)) c.computed = s.frame;
523
+ return;
524
+ }
474
525
  const sky = compute(ctx, s, "skyView", "shard::atmosphere::sky_view");
475
526
  const aerial = compute(ctx, s, "aerial", "shard::atmosphere::aerial");
476
527
  if (!sky || !aerial) return;
@@ -534,19 +585,17 @@ function idOf(o) {
534
585
  }
535
586
  return id;
536
587
  }
537
- let empty;
538
588
  function emptyTextures(gpu) {
539
- if (!empty || empty.generation !== gpu.generation) {
589
+ return gpu.shared("atmosphere/empty", (device) => {
540
590
  const usage = GPUTextureUsage.TEXTURE_BINDING;
541
- empty = {
542
- generation: gpu.generation,
543
- d2: gpu.device.createTexture({
591
+ return {
592
+ d2: device.createTexture({
544
593
  label: "atmosphere/empty",
545
594
  size: [1, 1],
546
595
  format: "rgba16float",
547
596
  usage
548
597
  }),
549
- d3: gpu.device.createTexture({
598
+ d3: device.createTexture({
550
599
  label: "atmosphere/empty-3d",
551
600
  size: [1, 1, 1],
552
601
  dimension: "3d",
@@ -554,8 +603,7 @@ function emptyTextures(gpu) {
554
603
  usage
555
604
  })
556
605
  };
557
- }
558
- return empty;
606
+ });
559
607
  }
560
608
  function skyNode() {
561
609
  const bgData = new Float32Array(8);
@@ -660,12 +708,31 @@ function skyNode() {
660
708
  }
661
709
  };
662
710
  }
711
+ const fogScratch = new Float32Array(8);
712
+ const FOG_COMPOSITE_KEYS = [
713
+ "composite-fog",
714
+ "composite-fog/alpha",
715
+ "composite-fog/interior",
716
+ "composite-fog/alpha/interior",
717
+ "composite-fog/rg11",
718
+ "composite-fog/alpha/rg11",
719
+ "composite-fog/interior/rg11",
720
+ "composite-fog/alpha/interior/rg11"
721
+ ];
663
722
  function compositeNode() {
723
+ const fogBuffers = /* @__PURE__ */ new Map();
664
724
  return {
665
725
  kind: "render",
666
726
  phase: RenderPhase.Post,
667
727
  enabled: hasEffect(PostEffect.Atmosphere),
668
- reads: ["atmosphere-in", "depth", "atmosphere-luts", "atmosphere-view"],
728
+ reads: [
729
+ "atmosphere-in",
730
+ "depth",
731
+ "atmosphere-luts",
732
+ "atmosphere-view",
733
+ "environment",
734
+ "clusters"
735
+ ],
669
736
  writes: ["atmosphere-out"],
670
737
  color: [{ resource: "atmosphere-out", clear: { r: 0, g: 0, b: 0, a: 1 } }],
671
738
  run: (ctx) => {
@@ -673,30 +740,43 @@ function compositeNode() {
673
740
  const ca = ctx.view.data.atmosphere;
674
741
  const s = ctx.world.resource(AtmosphereGpuResource);
675
742
  const c = cam ? s.cameras.get(cam.entity) : void 0;
676
- const pv = ctx.world.resource(ForwardStateResource).views.get(ctx.view.name);
743
+ const forward = ctx.world.resource(ForwardStateResource);
744
+ const pv = forward.views.get(ctx.view.name);
677
745
  if (!cam || !ca || !c || !pv) return;
678
746
  const gpu = ctx.gpu;
679
747
  const l = layouts(gpu, s);
680
- let pipeline = s.pipelines.get("composite");
748
+ const fog = (cam.post.effects & PostEffect.Fog) !== 0;
749
+ const alpha = cam.alphaOutput;
750
+ const out = ctx.texture("atmosphere-out");
751
+ const rg11 = out.format === "rg11b10ufloat";
752
+ const name = fog ? FOG_COMPOSITE_KEYS[(alpha ? 1 : 0) + (forward.interiorMode !== 0 ? 2 : 0) + (rg11 ? 4 : 0)] : rg11 ? "composite/rg11" : "composite";
753
+ let pipeline = s.pipelines.get(name);
681
754
  if (!pipeline) {
682
755
  const shaders = ctx.world.resource(Shaders);
683
- const fs = shaders.module(gpu, { root: "shard::atmosphere::composite" });
756
+ const fs = shaders.module(gpu, {
757
+ root: "shard::atmosphere::composite",
758
+ defines: { FOG: fog, TRANSPARENT: fog && alpha, RG11_ROUND: roundsRg11(gpu, out) }
759
+ });
684
760
  const vs = shaders.module(gpu, { root: "shard::fullscreen" });
685
761
  if (!fs || !vs) {
686
762
  gpu.pipelines.skipped++;
687
763
  return;
688
764
  }
689
765
  pipeline = gpu.pipelines.render({
690
- label: "atmosphere/composite",
766
+ label: `atmosphere/${name}`,
691
767
  layout: gpu.layouts.pipelineLayout({
692
- label: "atmosphere/composite",
693
- bindGroupLayouts: [l.viewOnly, l.composite]
768
+ label: `atmosphere/${name}`,
769
+ bindGroupLayouts: fog ? [forward.layouts.view, l.compositeFog] : [l.viewOnly, l.composite]
694
770
  }),
695
771
  vertex: { module: vs, entryPoint: "vs" },
696
- fragment: { module: fs, entryPoint: "fs", targets: [{ format: "rgba16float" }] }
772
+ fragment: {
773
+ module: fs,
774
+ entryPoint: fog ? "fs_fog" : "fs",
775
+ targets: targetsFor(out)
776
+ }
697
777
  });
698
778
  if (!pipeline) return;
699
- s.pipelines.set("composite", pipeline);
779
+ s.pipelines.set(name, pipeline);
700
780
  }
701
781
  const input = ctx.texture("atmosphere-in");
702
782
  const depth = ctx.texture("depth");
@@ -704,6 +784,48 @@ function compositeNode() {
704
784
  const inside = ca.inside && c.computed === s.frame;
705
785
  const scatter = inside && c.scatter ? c.scatter : e.d3;
706
786
  const trans = inside && c.transmittance ? c.transmittance : e.d3;
787
+ const pass = ctx.renderPass;
788
+ pass.setPipeline(pipeline);
789
+ if (fog) {
790
+ let buffer = fogBuffers.get(cam.entity);
791
+ if (!buffer) {
792
+ buffer = gpu.device.createBuffer({
793
+ label: `${ctx.view.name}/composite-fog`,
794
+ size: 32,
795
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
796
+ });
797
+ fogBuffers.set(cam.entity, buffer);
798
+ }
799
+ const f = cam.post.fog;
800
+ fogScratch.set(f.color, 0);
801
+ fogScratch[4] = f.density;
802
+ fogScratch[5] = f.heightFalloff;
803
+ fogScratch[6] = f.start;
804
+ fogScratch[7] = f.sunScattering;
805
+ gpu.device.queue.writeBuffer(buffer, 0, fogScratch);
806
+ const fogBuffer = buffer;
807
+ const group2 = s.group(
808
+ gpu,
809
+ `${cam.entity}/composite-fog`,
810
+ `${gpu.generation}/${idOf(input)}/${idOf(depth)}/${c.uniform.version}/${idOf(scatter)}/${idOf(trans)}/${idOf(s.transmittance)}/${idOf(fogBuffer)}`,
811
+ l.compositeFog,
812
+ () => [
813
+ { binding: 0, resource: input.createView() },
814
+ { binding: 1, resource: depth.createView() },
815
+ { binding: 2, resource: { buffer: c.uniform.buffer } },
816
+ { binding: 3, resource: s.transmittanceArray },
817
+ { binding: 4, resource: s.multiscatterArray },
818
+ { binding: 5, resource: s.clamp },
819
+ { binding: 6, resource: scatter.createView() },
820
+ { binding: 7, resource: trans.createView() },
821
+ { binding: 8, resource: { buffer: fogBuffer } }
822
+ ]
823
+ );
824
+ pass.setBindGroup(0, viewBindGroup(gpu, ctx.world, pv, cam));
825
+ pass.setBindGroup(1, group2);
826
+ pass.draw(3);
827
+ return;
828
+ }
707
829
  const group = s.group(
708
830
  gpu,
709
831
  `${cam.entity}/composite`,
@@ -727,8 +849,6 @@ function compositeNode() {
727
849
  l.viewOnly,
728
850
  () => [{ binding: 0, resource: { buffer: pv.uniform.buffer } }]
729
851
  );
730
- const pass = ctx.renderPass;
731
- pass.setPipeline(pipeline);
732
852
  pass.setBindGroup(0, viewGroup);
733
853
  pass.setBindGroup(1, group);
734
854
  pass.draw(3);
@@ -741,7 +861,7 @@ function bakeAtmosphere(ctx, pass, env, level0) {
741
861
  const primary = ca?.primary;
742
862
  if (!ca || !primary || primary.layerVersion !== primary.version) return false;
743
863
  const c = s.cameras.get(ca.camera);
744
- if (!c) return false;
864
+ if (!c?.packed) return false;
745
865
  const pipeline = compute(ctx, s, "bake", "shard::atmosphere::bake");
746
866
  if (!pipeline) return false;
747
867
  const gpu = ctx.gpu;
@@ -766,6 +886,12 @@ function addAtmosphereNodes(world) {
766
886
  const graph = world.resource(Graph);
767
887
  world.initResource(AtmosphereGpuResource);
768
888
  environmentBakers.set("atmosphere", bakeAtmosphere);
889
+ addRenderFeatures(world, {
890
+ name: "render/atmosphere",
891
+ description: "Atmosphere scattering LUTs, sky-view and aerial-perspective froxels, sky, composite.",
892
+ nodes: ["atmosphere/luts", "atmosphere/view", "atmosphere/sky", "post/atmosphere"],
893
+ baseline: { strategy: "LUTs, sky-view and froxel slices as fragment passes" }
894
+ });
769
895
  graph.addNode("atmosphere/luts", lutNode());
770
896
  graph.addNode("atmosphere/view", viewNode());
771
897
  graph.addNode("atmosphere/sky", skyNode());
@@ -776,5 +902,6 @@ export {
776
902
  AtmosphereGpuResource,
777
903
  LUT_LAYERS,
778
904
  addAtmosphereNodes,
905
+ packParams,
779
906
  uploadAtmospheres
780
907
  };
@@ -11,7 +11,7 @@ import { AtmosphereGpuResource, addAtmosphereNodes, uploadAtmospheres } from "./
11
11
  import { ATMOSPHERE_SHADERS } from "./atmosphere-shaders.js";
12
12
  import { Atmospheres } from "./atmosphere-state.js";
13
13
  import { extractLights } from "./lights.js";
14
- import { RenderDescribers, RenderSet, Shaders } from "./plugin.js";
14
+ import { Gpu, RenderDescribers, RenderSet, Shaders } from "./plugin.js";
15
15
  import { registerShaders } from "./shaders.js";
16
16
  import { extractCameras } from "./view.js";
17
17
  const atmospherePlugin = definePlugin({
@@ -28,13 +28,19 @@ const atmospherePlugin = definePlugin({
28
28
  );
29
29
  app.addMethod(...atmosphereMethods);
30
30
  },
31
- ready(app) {
31
+ async ready(app) {
32
+ const baseline = app.world.resource(Gpu).tier === "baseline" ? await import("./baseline/atmosphere.js") : void 0;
32
33
  registerShaders(app.world.resource(Shaders), ATMOSPHERE_SHADERS);
34
+ if (baseline) registerShaders(app.world.resource(Shaders), baseline.BASELINE_ATMOSPHERE_SHADERS);
33
35
  app.world.initResource(RenderDescribers).set(
34
36
  "atmosphere",
35
37
  (world) => describeAtmospheres(world, world.tryResource(ProfilerResource)?.all())
36
38
  );
37
39
  addAtmosphereNodes(app.world);
40
+ if (baseline) {
41
+ app.world.resource(AtmosphereGpuResource).baseline = baseline;
42
+ baseline.installAtmosphereBaseline();
43
+ }
38
44
  }
39
45
  });
40
46
  export {
@@ -518,8 +518,9 @@ struct SkyOutput {
518
518
  }`,
519
519
  "shard::atmosphere::composite": `
520
520
  import shard::view::view;
521
- import shard::post::common::uv_of;
521
+ import shard::post::common::{ uv_of, post_out };
522
522
  import shard::atmosphere::{ AtmosphereView, KCD, integrate, froxel_slice, relative_at };
523
+ @if(FOG) import shard::post::fog::common::{ FogParams, fog_apply };
523
524
 
524
525
  @group(1) @binding(0) var input: texture_2d<f32>;
525
526
  @group(1) @binding(1) var depth_texture: texture_depth_2d;
@@ -529,16 +530,15 @@ import shard::atmosphere::{ AtmosphereView, KCD, integrate, froxel_slice, relati
529
530
  @group(1) @binding(5) var lut_sampler: sampler;
530
531
  @group(1) @binding(6) var ap_scatter: texture_3d<f32>;
531
532
  @group(1) @binding(7) var ap_transmittance: texture_3d<f32>;
533
+ /** A view with fog too: fog follows in the same pass (\`fs_fog\`). */
534
+ @if(FOG) @group(1) @binding(8) var<uniform> fog: FogParams;
532
535
 
533
536
  /**
534
537
  * Aerial perspective on geometry: color \xD7 transmittance + in-scattering for every atmosphere the
535
538
  * view ray crosses before the surface, far to near. The primary reads its froxels within their
536
539
  * range; beyond it (terrain seen from orbit), and for the others, the ray is marched.
537
540
  */
538
- @fragment fn fs(@builtin(position) frag: vec4f) -> @location(0) vec4f {
539
- let px = vec2i(frag.xy);
540
- let c = textureLoad(input, px, 0);
541
- let depth = textureLoad(depth_texture, px, 0);
541
+ fn haze(frag: vec4f, c: vec4f, depth: f32) -> vec4f {
542
542
  let count = u32(atmo.info.x);
543
543
  if (depth <= 0.0 || count == 0u) { return c; }
544
544
  let uv = uv_of(frag.xy);
@@ -568,6 +568,18 @@ import shard::atmosphere::{ AtmosphereView, KCD, integrate, froxel_slice, relati
568
568
  }
569
569
  }
570
570
  return vec4f(min(color, vec3f(60000.0)), c.a);
571
+ }
572
+
573
+ @fragment fn fs(@builtin(position) frag: vec4f) -> @location(0) vec4f {
574
+ let px = vec2i(frag.xy);
575
+ return post_out(haze(frag, textureLoad(input, px, 0), textureLoad(depth_texture, px, 0)));
576
+ }
577
+
578
+ /** Aerial perspective, then height fog: the view's fog pass folded into this one. */
579
+ @if(FOG) @fragment fn fs_fog(@builtin(position) frag: vec4f) -> @location(0) vec4f {
580
+ let px = vec2i(frag.xy);
581
+ let depth = textureLoad(depth_texture, px, 0);
582
+ return post_out(fog_apply(haze(frag, textureLoad(input, px, 0), depth), uv_of(frag.xy), depth, fog));
571
583
  }`,
572
584
  "shard::atmosphere::bake": `
573
585
  import shard::env::common::cube_dir;
@@ -0,0 +1,16 @@
1
+ import { GpuBuffer } from '@aethervtt/shard-gpu';
2
+ import { type AtmosphereGpu } from '../atmosphere-nodes.js';
3
+ import type { CameraAtmosphere } from '../atmosphere-state.js';
4
+ import type { NodeContext } from '../graph.js';
5
+ export declare const BASELINE_ATMOSPHERE_SHADERS: Record<string, string>;
6
+ /** The transmittance and multiple-scattering LUTs of every layer whose atmosphere changed. */
7
+ export declare function runLuts(ctx: NodeContext, s: AtmosphereGpu): boolean;
8
+ /** A camera's sky-view LUT and (with aerial perspective) its froxels, slice by slice. */
9
+ export declare function runView(ctx: NodeContext, s: AtmosphereGpu, ca: CameraAtmosphere, c: {
10
+ uniform: GpuBuffer;
11
+ skyView: GPUTexture;
12
+ scatter: GPUTexture;
13
+ transmittance: GPUTexture;
14
+ }): boolean;
15
+ /** Registers the environment baker: atmosphere environments bake by fragment passes too. */
16
+ export declare function installAtmosphereBaseline(): void;