@yali-engine/render 0.0.0-stage → 0.4.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 (131) hide show
  1. package/dist/bvh.d.ts +66 -0
  2. package/dist/bvh.js +493 -0
  3. package/dist/bvh.js.map +1 -0
  4. package/dist/camera.d.ts +34 -0
  5. package/dist/camera.js +66 -0
  6. package/dist/camera.js.map +1 -0
  7. package/dist/cluster-build.d.ts +46 -0
  8. package/dist/cluster-build.js +179 -0
  9. package/dist/cluster-build.js.map +1 -0
  10. package/dist/cluster-pages.d.ts +70 -0
  11. package/dist/cluster-pages.js +203 -0
  12. package/dist/cluster-pages.js.map +1 -0
  13. package/dist/cluster-stream.d.ts +83 -0
  14. package/dist/cluster-stream.js +226 -0
  15. package/dist/cluster-stream.js.map +1 -0
  16. package/dist/culled-draws.d.ts +30 -0
  17. package/dist/culled-draws.js +103 -0
  18. package/dist/culled-draws.js.map +1 -0
  19. package/dist/debug-lines.d.ts +51 -0
  20. package/dist/debug-lines.js +158 -0
  21. package/dist/debug-lines.js.map +1 -0
  22. package/dist/device-profile.d.ts +100 -0
  23. package/dist/device-profile.js +131 -0
  24. package/dist/device-profile.js.map +1 -0
  25. package/dist/device.d.ts +39 -0
  26. package/dist/device.js +79 -0
  27. package/dist/device.js.map +1 -0
  28. package/dist/environment.d.ts +60 -0
  29. package/dist/environment.js +204 -0
  30. package/dist/environment.js.map +1 -0
  31. package/dist/extract.d.ts +74 -0
  32. package/dist/extract.js +194 -0
  33. package/dist/extract.js.map +1 -0
  34. package/dist/frame-graph.d.ts +112 -0
  35. package/dist/frame-graph.js +226 -0
  36. package/dist/frame-graph.js.map +1 -0
  37. package/dist/gpu-health.d.ts +74 -0
  38. package/dist/gpu-health.js +106 -0
  39. package/dist/gpu-health.js.map +1 -0
  40. package/dist/gpu-timer.d.ts +26 -0
  41. package/dist/gpu-timer.js +93 -0
  42. package/dist/gpu-timer.js.map +1 -0
  43. package/dist/headless.d.ts +28 -0
  44. package/dist/headless.js +91 -0
  45. package/dist/headless.js.map +1 -0
  46. package/dist/index.d.ts +34 -0
  47. package/dist/index.js +36 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/lights.d.ts +81 -0
  50. package/dist/lights.js +247 -0
  51. package/dist/lights.js.map +1 -0
  52. package/dist/matrix.d.ts +6 -0
  53. package/dist/matrix.js +59 -0
  54. package/dist/matrix.js.map +1 -0
  55. package/dist/meshes.d.ts +411 -0
  56. package/dist/meshes.js +545 -0
  57. package/dist/meshes.js.map +1 -0
  58. package/dist/meshlets-decoder.d.ts +20 -0
  59. package/dist/meshlets-decoder.js +47 -0
  60. package/dist/meshlets-decoder.js.map +1 -0
  61. package/dist/meshlets.d.ts +186 -0
  62. package/dist/meshlets.js +1040 -0
  63. package/dist/meshlets.js.map +1 -0
  64. package/dist/occlusion.d.ts +51 -0
  65. package/dist/occlusion.js +188 -0
  66. package/dist/occlusion.js.map +1 -0
  67. package/dist/pick-decoder.d.ts +19 -0
  68. package/dist/pick-decoder.js +45 -0
  69. package/dist/pick-decoder.js.map +1 -0
  70. package/dist/pick.d.ts +86 -0
  71. package/dist/pick.js +452 -0
  72. package/dist/pick.js.map +1 -0
  73. package/dist/pipeline-cache.d.ts +53 -0
  74. package/dist/pipeline-cache.js +96 -0
  75. package/dist/pipeline-cache.js.map +1 -0
  76. package/dist/post-options.d.ts +109 -0
  77. package/dist/post-options.js +23 -0
  78. package/dist/post-options.js.map +1 -0
  79. package/dist/post-shaders.gen.d.ts +40 -0
  80. package/dist/post-shaders.gen.js +43 -0
  81. package/dist/post-shaders.gen.js.map +1 -0
  82. package/dist/post.d.ts +101 -0
  83. package/dist/post.js +555 -0
  84. package/dist/post.js.map +1 -0
  85. package/dist/render-plugin.d.ts +239 -0
  86. package/dist/render-plugin.js +633 -0
  87. package/dist/render-plugin.js.map +1 -0
  88. package/dist/scene.d.ts +406 -0
  89. package/dist/scene.js +2005 -0
  90. package/dist/scene.js.map +1 -0
  91. package/dist/shaders.gen.d.ts +40 -0
  92. package/dist/shaders.gen.js +43 -0
  93. package/dist/shaders.gen.js.map +1 -0
  94. package/dist/shadows.d.ts +104 -0
  95. package/dist/shadows.js +227 -0
  96. package/dist/shadows.js.map +1 -0
  97. package/dist/staging-ring.d.ts +41 -0
  98. package/dist/staging-ring.js +125 -0
  99. package/dist/staging-ring.js.map +1 -0
  100. package/dist/textures.d.ts +165 -0
  101. package/dist/textures.js +361 -0
  102. package/dist/textures.js.map +1 -0
  103. package/dist/tonemap.d.ts +45 -0
  104. package/dist/tonemap.js +141 -0
  105. package/dist/tonemap.js.map +1 -0
  106. package/dist/viewport.d.ts +78 -0
  107. package/dist/viewport.js +130 -0
  108. package/dist/viewport.js.map +1 -0
  109. package/dist/webgl2/plugin.d.ts +40 -0
  110. package/dist/webgl2/plugin.js +190 -0
  111. package/dist/webgl2/plugin.js.map +1 -0
  112. package/dist/webgl2/program.d.ts +27 -0
  113. package/dist/webgl2/program.js +74 -0
  114. package/dist/webgl2/program.js.map +1 -0
  115. package/dist/webgl2/renderer.d.ts +128 -0
  116. package/dist/webgl2/renderer.js +976 -0
  117. package/dist/webgl2/renderer.js.map +1 -0
  118. package/dist/webgl2/shaders.glsl.gen.d.ts +27 -0
  119. package/dist/webgl2/shaders.glsl.gen.js +20 -0
  120. package/dist/webgl2/shaders.glsl.gen.js.map +1 -0
  121. package/dist/webgl2/textures.d.ts +11 -0
  122. package/dist/webgl2/textures.js +82 -0
  123. package/dist/webgl2/textures.js.map +1 -0
  124. package/dist/webgl2/webgl2.d.ts +1 -0
  125. package/dist/webgl2/webgl2.js +4 -0
  126. package/dist/webgl2/webgl2.js.map +1 -0
  127. package/dist/worker-mode.d.ts +85 -0
  128. package/dist/worker-mode.js +204 -0
  129. package/dist/worker-mode.js.map +1 -0
  130. package/package.json +47 -3
  131. package/README.md +0 -3
@@ -0,0 +1,109 @@
1
+ /** Bloom (ADR-0092): HDR light spilling over its surroundings. */
2
+ export interface BloomOptions {
3
+ /** How much is added (default 0.6). */
4
+ readonly intensity?: number;
5
+ /** Linear brightness where bloom starts (default 1: only highlights; 0: everything, softly). */
6
+ readonly threshold?: number;
7
+ /** The threshold's soft knee, a share of it (default 0.5). */
8
+ readonly knee?: number;
9
+ /** The upsample's spread, in texels of each level (default 1). */
10
+ readonly radius?: number;
11
+ /** Half-size levels in the chain (default 5: the widest is 1/32 of the screen). */
12
+ readonly levels?: number;
13
+ }
14
+ /** Colour grading (ADR-0092): white balance, contrast and saturation before the tone curve, lift / gamma / gain after it. */
15
+ export interface GradingOptions {
16
+ /** Cooler (−1) to warmer (+1); default 0. */
17
+ temperature?: number;
18
+ /** Green (−1) to magenta (+1); default 0. */
19
+ tint?: number;
20
+ /** About mid grey in log space; default 1. */
21
+ contrast?: number;
22
+ /** 0 grey, 1 as drawn (default), above 1 more saturated. */
23
+ saturation?: number;
24
+ /** Shadows, added after the curve; default [0, 0, 0]. */
25
+ lift?: [number, number, number];
26
+ /** Mid tones; default [1, 1, 1]. */
27
+ gamma?: [number, number, number];
28
+ /** Highlights; default [1, 1, 1]. */
29
+ gain?: [number, number, number];
30
+ }
31
+ /**
32
+ * The post chain's passes: `postPasses` from `@yali-engine/render/post`. A separate entry, so a page
33
+ * that never asks for bloom, TAA or FXAA carries none of their code or shaders.
34
+ */
35
+ export interface PostPasses {
36
+ readonly Bloom: new (device: GPUDevice, o: BloomOptions) => import('./post.ts').Bloom;
37
+ readonly Taa: new (device: GPUDevice, blend?: number, upscale?: boolean) => import('./post.ts').Taa;
38
+ readonly Fxaa: new (device: GPUDevice, format: GPUTextureFormat) => import('./post.ts').Fxaa;
39
+ }
40
+ /**
41
+ * The post-processing chain (ADR-0092). Which passes exist is fixed at init; their knobs are live.
42
+ *
43
+ * @example
44
+ * import { postPasses } from '@yali-engine/render/post';
45
+ * renderPlugin({ canvas, post: { passes: postPasses, bloom: true, taa: true, grading: { contrast: 1.1 } } });
46
+ */
47
+ export interface PostOptions {
48
+ /** Needed for bloom, TAA and FXAA (grading is built in). */
49
+ readonly passes?: PostPasses;
50
+ /** `true` for the defaults. */
51
+ readonly bloom?: BloomOptions | boolean;
52
+ readonly grading?: GradingOptions;
53
+ /** Fast approximate anti-aliasing on the tone-mapped image. */
54
+ readonly fxaa?: boolean;
55
+ /** Temporal anti-aliasing: a jittered camera, accumulated over frames. */
56
+ /**
57
+ * `motion: false` reprojects by the camera only (default: per-object motion where the GPU cull's
58
+ * storage path draws it, ADR-0098). `sharpen` (0–1) and `motionBlur` (the shutter, 0–1 of each
59
+ * pixel's motion; needs per-object motion) add a pass after TAA (ADR-0119; both live in `render.post`).
60
+ * `upscale` (ADR-0135): temporal upscaling, the scene drawn at `viewport.renderScale` of the canvas and
61
+ * TAA reconstructing to the canvas's size (TAAU); the governor's `render.scale` then drives it.
62
+ */
63
+ readonly taa?: boolean | {
64
+ readonly blend?: number;
65
+ readonly motion?: boolean;
66
+ readonly sharpen?: number;
67
+ readonly motionBlur?: number;
68
+ readonly upscale?: boolean;
69
+ };
70
+ }
71
+ /** The live post knobs (`render.post`). */
72
+ export interface PostControls {
73
+ /** Which passes run (fixed at init; all false on WebGL2 and without the HDR target, where there is no chain). */
74
+ readonly bloom: boolean;
75
+ readonly fxaa: boolean;
76
+ readonly taa: boolean;
77
+ bloomIntensity: number;
78
+ bloomThreshold: number;
79
+ bloomKnee: number;
80
+ bloomRadius: number;
81
+ /** The current frame's weight in TAA's blend (default 0.1). */
82
+ taaBlend: number;
83
+ /** Sharpening after TAA, 0–1, and motion blur's shutter, 0–1 (ADR-0119; live when the pass was asked for at init). */
84
+ taaSharpen: number;
85
+ motionBlur: number;
86
+ /** Edit in place; `grading.enabled = false` turns it off. */
87
+ readonly grading: Required<GradingOptions> & {
88
+ enabled: boolean;
89
+ };
90
+ /** Drops TAA's history (call on a camera cut). */
91
+ resetHistory(): void;
92
+ /**
93
+ * What TAA's per-object motion held on the next frame (ADR-0124; reads back from the GPU): pixels drawn,
94
+ * those moving more than a twentieth of a pixel, the largest move in pixels, their mean view depth (last
95
+ * frame's). Null without TAA or per-object motion.
96
+ */
97
+ readMotion(): Promise<{
98
+ drawn: number;
99
+ moving: number;
100
+ maxPixels: number;
101
+ meanDepth: number;
102
+ } | null>;
103
+ }
104
+ /** The white-balance gains for `temperature` and `tint`, normalised to keep the luminance. */
105
+ export declare function whiteBalance(temperature: number, tint: number): [number, number, number];
106
+ /** Normalised grading options. */
107
+ export declare function gradingDefaults(o: GradingOptions | undefined): Required<GradingOptions> & {
108
+ enabled: boolean;
109
+ };
@@ -0,0 +1,23 @@
1
+ // The post chain's options and grading maths (ADR-0092), apart from its passes: the tone-mapping
2
+ // pass and WebGL2 need these, while the passes (post.ts, and their shaders) load only when an app
3
+ // asks for them.
4
+ /** The white-balance gains for `temperature` and `tint`, normalised to keep the luminance. */
5
+ export function whiteBalance(temperature, tint) {
6
+ const g = [1 + 0.2 * temperature, 1 - 0.2 * tint, 1 - 0.2 * temperature];
7
+ const y = 0.2126 * g[0] + 0.7152 * g[1] + 0.0722 * g[2];
8
+ return [g[0] / y, g[1] / y, g[2] / y];
9
+ }
10
+ /** Normalised grading options. */
11
+ export function gradingDefaults(o) {
12
+ return {
13
+ enabled: o !== undefined,
14
+ temperature: o?.temperature ?? 0,
15
+ tint: o?.tint ?? 0,
16
+ contrast: o?.contrast ?? 1,
17
+ saturation: o?.saturation ?? 1,
18
+ lift: o?.lift ?? [0, 0, 0],
19
+ gamma: o?.gamma ?? [1, 1, 1],
20
+ gain: o?.gain ?? [1, 1, 1],
21
+ };
22
+ }
23
+ //# sourceMappingURL=post-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-options.js","sourceRoot":"","sources":["../src/post-options.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,kGAAkG;AAClG,iBAAiB;AAiGjB,8FAA8F;AAC9F,MAAM,UAAU,YAAY,CAAC,WAAmB,EAAE,IAAY;IAC5D,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,GAAG,WAAW,EAAE,CAAC,GAAG,GAAG,GAAG,IAAI,EAAE,CAAC,GAAG,GAAG,GAAG,WAAW,CAAC,CAAC;IACzE,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,eAAe,CAAC,CAA6B;IAC3D,OAAO;QACL,OAAO,EAAE,CAAC,KAAK,SAAS;QACxB,WAAW,EAAE,CAAC,EAAE,WAAW,IAAI,CAAC;QAChC,IAAI,EAAE,CAAC,EAAE,IAAI,IAAI,CAAC;QAClB,QAAQ,EAAE,CAAC,EAAE,QAAQ,IAAI,CAAC;QAC1B,UAAU,EAAE,CAAC,EAAE,UAAU,IAAI,CAAC;QAC9B,IAAI,EAAE,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC1B,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC5B,IAAI,EAAE,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,40 @@
1
+ /** Linked from shaders/post/bloom.wesl. */
2
+ export declare const postBloom: {
3
+ readonly hash: "ce493f335d49f31e";
4
+ readonly wgsl: "struct BloomParams {\n/** Linear luminance where bloom starts (0: everything blooms, energy-conserving). */\nthreshold: f32,\n/** The soft knee's width, as a share of the threshold. */\nknee: f32,\n/** The upsample tent's radius, in texels of the smaller level. */\nradius: f32,\npad: f32,\n}\n@group(0) @binding(0) var src: texture_2d<f32>;\n@group(0) @binding(1) var smp: sampler;\n@group(0) @binding(2) var<uniform> bloom: BloomParams;\n@group(0) @binding(3) var low: texture_2d<f32>;\nstruct Out {\n@builtin(position) pos: vec4f,\n@location(0) uv: vec2f,\n}\n@vertex fn vs(@builtin(vertex_index) i: u32) -> Out {\nlet p = vec2f(f32((i << 1u) & 2u), f32(i & 2u));\nvar o: Out;\no.pos = vec4f(p * 2.0 - 1.0, 0.0, 1.0);\no.uv = vec2f(p.x, 1.0 - p.y);\nreturn o;\n}\nfn luma(c: vec3f) -> f32 {\nreturn dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\nfn tap(uv: vec2f) -> vec3f {\nreturn min(textureSampleLevel(src, smp, uv, 0.0).rgb, vec3f(65000.0));\n}\nfn downsample(uv: vec2f, karis: bool) -> vec3f {\nlet t = 1.0 / vec2f(textureDimensions(src));\nlet a = tap(uv + t * vec2f(-2.0, -2.0));\nlet b = tap(uv + t * vec2f(0.0, -2.0));\nlet c = tap(uv + t * vec2f(2.0, -2.0));\nlet d = tap(uv + t * vec2f(-1.0, -1.0));\nlet e = tap(uv + t * vec2f(1.0, -1.0));\nlet f = tap(uv + t * vec2f(-2.0, 0.0));\nlet g = tap(uv);\nlet h = tap(uv + t * vec2f(2.0, 0.0));\nlet i = tap(uv + t * vec2f(-1.0, 1.0));\nlet j = tap(uv + t * vec2f(1.0, 1.0));\nlet k = tap(uv + t * vec2f(-2.0, 2.0));\nlet l = tap(uv + t * vec2f(0.0, 2.0));\nlet m = tap(uv + t * vec2f(2.0, 2.0));\nlet boxes = array<vec3f, 5>((d + e + i + j) * 0.25, (a + b + f + g) * 0.25, (b + c + g + h) * 0.25, (f + g + k + l) * 0.25, (g + h + l + m) * 0.25);\nlet weights = array<f32, 5>(0.5, 0.125, 0.125, 0.125, 0.125);\nvar sum = vec3f(0.0);\nvar total = 0.0;\nfor (var n = 0; n < 5; n++) {\nvar w = weights[n];\nif (karis) {\nw = w / (1.0 + luma(boxes[n]));\n}\nsum += boxes[n] * w;\ntotal += w;\n}\nreturn sum / total;\n}\n@fragment fn fs_prefilter(in: Out) -> @location(0) vec4f {\nlet c = downsample(in.uv, true);\nif (bloom.threshold <= 0.0) {\nreturn vec4f(c, 1.0);\n}\nlet br = max(c.r, max(c.g, c.b));\nlet k = bloom.threshold * bloom.knee + 1e-5;\nvar soft = clamp(br - bloom.threshold + k, 0.0, 2.0 * k);\nsoft = soft * soft / (4.0 * k);\nlet w = max(soft, br - bloom.threshold) / max(br, 1e-5);\nreturn vec4f(c * w, 1.0);\n}\n@fragment fn fs_down(in: Out) -> @location(0) vec4f {\nreturn vec4f(downsample(in.uv, false), 1.0);\n}\n@fragment fn fs_up(in: Out) -> @location(0) vec4f {\nlet t = bloom.radius / vec2f(textureDimensions(low));\nvar s = textureSampleLevel(low, smp, in.uv, 0.0).rgb * 4.0;\ns += (textureSampleLevel(low, smp, in.uv + vec2f(-t.x, 0.0), 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(t.x, 0.0), 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(0.0, -t.y), 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(0.0, t.y), 0.0).rgb) * 2.0;\ns += textureSampleLevel(low, smp, in.uv - t, 0.0).rgb + textureSampleLevel(low, smp, in.uv + t, 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(-t.x, t.y), 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(t.x, -t.y), 0.0).rgb;\nreturn vec4f(textureSampleLevel(src, smp, in.uv, 0.0).rgb + s / 16.0, 1.0);\n}";
5
+ };
6
+ /** Linked from shaders/post/fxaa.wesl. */
7
+ export declare const postFxaa: {
8
+ readonly hash: "362146f9cb191613";
9
+ readonly wgsl: "@group(0) @binding(0) var ldr: texture_2d<f32>;\n@group(0) @binding(1) var smp: sampler;\nstruct Out {\n@builtin(position) pos: vec4f,\n@location(0) uv: vec2f,\n}\n@vertex fn vs(@builtin(vertex_index) i: u32) -> Out {\nlet p = vec2f(f32((i << 1u) & 2u), f32(i & 2u));\nvar o: Out;\no.pos = vec4f(p * 2.0 - 1.0, 0.0, 1.0);\no.uv = vec2f(p.x, 1.0 - p.y);\nreturn o;\n}\nconst EDGE_MIN = 0.0312;\nconst EDGE_MAX = 0.125;\nconst SUBPIXEL = 0.75;\nconst STEPS = 10;\nfn luma_at(uv: vec2f) -> f32 {\nreturn dot(textureSampleLevel(ldr, smp, uv, 0.0).rgb, vec3f(0.299, 0.587, 0.114));\n}\nfn quality(i: i32) -> f32 {\nif (i < 5) {\nreturn 1.0;\n}\nif (i == 5) {\nreturn 1.5;\n}\nif (i < 9) {\nreturn 2.0;\n}\nreturn 4.0;\n}\n@fragment fn fs(in: Out) -> @location(0) vec4f {\nlet texel = 1.0 / vec2f(textureDimensions(ldr));\nlet uv = in.uv;\nlet centre = textureSampleLevel(ldr, smp, uv, 0.0);\nlet m = dot(centre.rgb, vec3f(0.299, 0.587, 0.114));\nlet n = luma_at(uv + vec2f(0.0, -texel.y));\nlet s = luma_at(uv + vec2f(0.0, texel.y));\nlet e = luma_at(uv + vec2f(texel.x, 0.0));\nlet w = luma_at(uv + vec2f(-texel.x, 0.0));\nlet lo = min(m, min(min(n, s), min(e, w)));\nlet hi = max(m, max(max(n, s), max(e, w)));\nlet range = hi - lo;\nif (range < max(EDGE_MIN, hi * EDGE_MAX)) {\nreturn centre;\n}\nlet nw = luma_at(uv + vec2f(-texel.x, -texel.y));\nlet ne = luma_at(uv + vec2f(texel.x, -texel.y));\nlet sw = luma_at(uv + vec2f(-texel.x, texel.y));\nlet se = luma_at(uv + vec2f(texel.x, texel.y));\nlet horizontal = abs(-2.0 * w + nw + sw) + 2.0 * abs(-2.0 * m + n + s) + abs(-2.0 * e + ne + se) >= abs(-2.0 * n + nw + ne) + 2.0 * abs(-2.0 * m + w + e) + abs(-2.0 * s + sw + se);\nlet l1 = select(w, n, horizontal);\nlet l2 = select(e, s, horizontal);\nlet g1 = l1 - m;\nlet g2 = l2 - m;\nlet steep1 = abs(g1) >= abs(g2);\nlet scaled = 0.25 * max(abs(g1), abs(g2));\nvar step_len = select(texel.x, texel.y, horizontal);\nvar avg = 0.5 * (l2 + m);\nif (steep1) {\nstep_len = -step_len;\navg = 0.5 * (l1 + m);\n}\nvar cur = uv;\nif (horizontal) {\ncur.y += step_len * 0.5;\n} else {\ncur.x += step_len * 0.5;\n}\nlet along = select(vec2f(0.0, texel.y), vec2f(texel.x, 0.0), horizontal);\nvar uv1 = cur - along;\nvar uv2 = cur + along;\nvar end1 = luma_at(uv1) - avg;\nvar end2 = luma_at(uv2) - avg;\nvar done1 = abs(end1) >= scaled;\nvar done2 = abs(end2) >= scaled;\nfor (var i = 1; i < STEPS && !(done1 && done2); i++) {\nif (!done1) {\nuv1 -= along * quality(i);\nend1 = luma_at(uv1) - avg;\ndone1 = abs(end1) >= scaled;\n}\nif (!done2) {\nuv2 += along * quality(i);\nend2 = luma_at(uv2) - avg;\ndone2 = abs(end2) >= scaled;\n}\n}\nlet d1 = select(uv.y - uv1.y, uv.x - uv1.x, horizontal);\nlet d2 = select(uv2.y - uv.y, uv2.x - uv.x, horizontal);\nlet nearer1 = d1 < d2;\nlet dist = min(d1, d2);\nlet pixel_offset = -dist / (d1 + d2) + 0.5;\nlet smaller = m < avg;\nlet right_way = (select(end2, end1, nearer1) < 0.0) != smaller;\nvar offset = select(0.0, pixel_offset, right_way);\nlet mean = (2.0 * (n + s + e + w) + nw + ne + sw + se) / 12.0;\nlet sub1 = clamp(abs(mean - m) / range, 0.0, 1.0);\nlet sub2 = (-2.0 * sub1 + 3.0) * sub1 * sub1;\noffset = max(offset, sub2 * sub2 * SUBPIXEL);\nvar at = uv;\nif (horizontal) {\nat.y += offset * step_len;\n} else {\nat.x += offset * step_len;\n}\nreturn vec4f(textureSampleLevel(ldr, smp, at, 0.0).rgb, 1.0);\n}";
10
+ };
11
+ /** Linked from shaders/post/taa.wesl. */
12
+ export declare const postTaa: {
13
+ readonly hash: "05819cf45a86a800";
14
+ readonly wgsl: "struct TaaParams {\nreproject: mat4x4f,\nnear: f32,\nmotion: u32,\nblend: f32,\nreset: u32,\njitter_px: vec2f,\nupscale: u32,\npad: u32,\n}\n@group(0) @binding(0) var current: texture_2d<f32>;\n@group(0) @binding(1) var history: texture_2d<f32>;\n@group(0) @binding(2) var depth: texture_depth_2d;\n@group(0) @binding(3) var smp: sampler;\n@group(0) @binding(4) var<uniform> taa: TaaParams;\n@group(0) @binding(5) var motion_tex: texture_2d<f32>;\n@vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {\nlet p = vec2f(f32((i << 1u) & 2u), f32(i & 2u));\nreturn vec4f(p * 2.0 - 1.0, 0.0, 1.0);\n}\nfn to_ycocg(c: vec3f) -> vec3f {\nreturn vec3f(dot(c, vec3f(0.25, 0.5, 0.25)), dot(c, vec3f(0.5, 0.0, -0.5)), dot(c, vec3f(-0.25, 0.5, -0.25)));\n}\nfn from_ycocg(c: vec3f) -> vec3f {\nreturn vec3f(c.x + c.y - c.z, c.x + c.z, c.x - c.y - c.z);\n}\nfn compress(c: vec3f) -> vec3f {\nreturn c / (1.0 + max(c.r, max(c.g, c.b)));\n}\nfn expand(c: vec3f) -> vec3f {\nreturn c / max(1.0 - max(c.r, max(c.g, c.b)), 1e-4);\n}\nconst FAR = 60000.0;\nfn view_depth(d: f32) -> f32 {\nreturn select(FAR, min(taa.near / d, FAR), d > 0.0);\n}\nfn same_surface(puv: vec2f, size: vec2f, expected: f32) -> bool {\nlet base = vec2i(floor(puv * size - 0.5));\nlet hi = vec2i(size) - 1;\nlet e = min(expected, FAR);\nfor (var k = 0; k < 4; k++) {\nlet q = clamp(base + vec2i(k & 1, k >> 1), vec2i(0), hi);\nlet h = textureLoad(history, q, 0).a;\nif (abs(h - e) <= 0.05 * e + 0.01) {\nreturn true;\n}\n}\nreturn false;\n}\nfn load(p: vec2i, size: vec2i) -> vec3f {\nlet c = textureLoad(current, clamp(p, vec2i(0), size - 1), 0).rgb;\nreturn select(vec3f(0.0), compress(min(c, vec3f(65000.0))), all(c == c));\n}\nfn history_at(uv: vec2f, size: vec2f) -> vec3f {\nlet pos = uv * size;\nlet centre = floor(pos - 0.5) + 0.5;\nlet f = pos - centre;\nlet w0 = f * (-0.5 + f * (1.0 - 0.5 * f));\nlet w1 = 1.0 + f * f * (-2.5 + 1.5 * f);\nlet w2 = f * (0.5 + f * (2.0 - 1.5 * f));\nlet w3 = f * f * (-0.5 + 0.5 * f);\nlet w12 = w1 + w2;\nlet t0 = (centre - 1.0) / size;\nlet t3 = (centre + 2.0) / size;\nlet t12 = (centre + w2 / w12) / size;\nvar c = textureSampleLevel(history, smp, vec2f(t12.x, t0.y), 0.0).rgb * (w12.x * w0.y);\nc += textureSampleLevel(history, smp, vec2f(t0.x, t12.y), 0.0).rgb * (w0.x * w12.y);\nc += textureSampleLevel(history, smp, vec2f(t12.x, t12.y), 0.0).rgb * (w12.x * w12.y);\nc += textureSampleLevel(history, smp, vec2f(t3.x, t12.y), 0.0).rgb * (w3.x * w12.y);\nc += textureSampleLevel(history, smp, vec2f(t12.x, t3.y), 0.0).rgb * (w12.x * w3.y);\nlet total = w12.x * w0.y + w0.x * w12.y + w12.x * w12.y + w3.x * w12.y + w12.x * w3.y;\nreturn max(c / total, vec3f(0.0));\n}\nfn upscaled(at: vec4f) -> vec4f {\nlet isize = vec2i(textureDimensions(current));\nlet size = vec2f(isize);\nlet osize = vec2f(textureDimensions(history));\nlet uv = at.xy / osize;\nlet ratio = osize / size;\nlet pin = uv * size;\nlet base = vec2i(floor(pin));\nvar m1 = vec3f(0.0);\nvar m2 = vec3f(0.0);\nvar sum = vec3f(0.0);\nvar wsum = 0.0;\nvar wmax = 0.0;\nfor (var y = -1; y <= 1; y++) {\nfor (var x = -1; x <= 1; x++) {\nlet q = base + vec2i(x, y);\nlet c = to_ycocg(load(q, isize));\nm1 += c;\nm2 += c * c;\nlet s = (vec2f(q) + 0.5 - taa.jitter_px - pin) * ratio;\nlet w = exp(-2.29 * dot(s, s));\nsum += c * w;\nwsum += w;\nwmax = max(wmax, w);\n}\n}\nlet here = sum / max(wsum, 1e-5);\nlet pd = clamp(base, vec2i(0), isize - 1);\nlet d = textureLoad(depth, pd, 0);\nlet fresh = vec4f(expand(from_ycocg(here)), view_depth(d));\nif (taa.reset != 0u) {\nreturn fresh;\n}\nvar puv: vec2f;\nvar expected: f32;\nlet mv = select(vec4f(0.0), textureLoad(motion_tex, pd, 0), taa.motion != 0u);\nif (mv.a > 0.5) {\npuv = uv - mv.xy;\nexpected = mv.z;\n} else {\nlet ndc = vec2f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0);\nlet prev = taa.reproject * vec4f(ndc, d, 1.0);\nif (prev.w <= 0.0 && d > 0.0) {\nreturn fresh;\n}\nlet pn = prev.xy / prev.w;\npuv = vec2f(pn.x * 0.5 + 0.5, 0.5 - pn.y * 0.5);\nexpected = select(FAR, prev.w * view_depth(d), d > 0.0);\n}\nif (any(puv < vec2f(0.0)) || any(puv > vec2f(1.0)) || !same_surface(puv, osize, expected)) {\nreturn fresh;\n}\nlet hist = to_ycocg(compress(history_at(puv, osize)));\nlet mean = m1 / 9.0;\nlet sigma = sqrt(max(m2 / 9.0 - mean * mean, vec3f(0.0)));\nlet half_box = 1.25 * sigma + 1e-5;\nlet off = hist - mean;\nlet units = abs(off / half_box);\nlet k = max(units.x, max(units.y, units.z));\nlet clipped = select(hist, mean + off / k, k > 1.0);\nlet out = mix(clipped, here, taa.blend * wmax);\nreturn vec4f(expand(from_ycocg(out)), fresh.a);\n}\n@fragment fn fs(@builtin(position) at: vec4f) -> @location(0) vec4f {\nif (taa.upscale != 0u) {\nreturn upscaled(at);\n}\nlet isize = vec2i(textureDimensions(current));\nlet size = vec2f(isize);\nlet p = vec2i(at.xy);\nvar m1 = vec3f(0.0);\nvar m2 = vec3f(0.0);\nvar here = vec3f(0.0);\nfor (var y = -1; y <= 1; y++) {\nfor (var x = -1; x <= 1; x++) {\nlet c = to_ycocg(load(p + vec2i(x, y), isize));\nm1 += c;\nm2 += c * c;\nif (x == 0 && y == 0) {\nhere = c;\n}\n}\n}\nlet d = textureLoad(depth, p, 0);\nlet fresh = vec4f(expand(from_ycocg(here)), view_depth(d));\nif (taa.reset != 0u) {\nreturn fresh;\n}\nvar puv: vec2f;\nvar expected: f32;\nlet mv = select(vec4f(0.0), textureLoad(motion_tex, p, 0), taa.motion != 0u);\nif (mv.a > 0.5) {\npuv = at.xy / size - mv.xy;\nexpected = mv.z;\n} else {\nlet ndc = vec2f(at.x / size.x * 2.0 - 1.0, 1.0 - at.y / size.y * 2.0);\nlet prev = taa.reproject * vec4f(ndc, d, 1.0);\nif (prev.w <= 0.0 && d > 0.0) {\nreturn fresh;\n}\nlet pn = prev.xy / prev.w;\npuv = vec2f(pn.x * 0.5 + 0.5, 0.5 - pn.y * 0.5);\nexpected = select(FAR, prev.w * view_depth(d), d > 0.0);\n}\nif (any(puv < vec2f(0.0)) || any(puv > vec2f(1.0)) || !same_surface(puv, size, expected)) {\nreturn fresh;\n}\nlet hist = to_ycocg(compress(history_at(puv, size)));\nlet mean = m1 / 9.0;\nlet sigma = sqrt(max(m2 / 9.0 - mean * mean, vec3f(0.0)));\nlet half_box = 1.25 * sigma + 1e-5;\nlet off = hist - mean;\nlet units = abs(off / half_box);\nlet k = max(units.x, max(units.y, units.z));\nlet clipped = select(hist, mean + off / k, k > 1.0);\nlet out = mix(clipped, here, taa.blend);\nreturn vec4f(expand(from_ycocg(out)), fresh.a);\n}";
15
+ };
16
+ /** Linked from shaders/post/taa_post.wesl. */
17
+ export declare const postTaaPost: {
18
+ readonly hash: "5806c7af9e6201d4";
19
+ readonly wgsl: "@group(0) @binding(0) var color: texture_2d<f32>;\n@group(0) @binding(1) var motion: texture_2d<f32>;\n@group(0) @binding(2) var smp: sampler;\nstruct Params {\nsharpen: f32,\nblur: f32,\nsamples: f32,\nhas_motion: f32,\n}\n@group(0) @binding(3) var<uniform> p: Params;\nstruct Out {\n@builtin(position) pos: vec4f,\n@location(0) uv: vec2f,\n}\n@vertex fn vs(@builtin(vertex_index) i: u32) -> Out {\nlet q = vec2f(f32((i << 1u) & 2u), f32(i & 2u));\nvar o: Out;\no.pos = vec4f(q * 2.0 - 1.0, 0.0, 1.0);\no.uv = vec2f(q.x, 1.0 - q.y);\nreturn o;\n}\nfn load(px: vec2i, size: vec2i) -> vec3f {\nreturn textureLoad(color, clamp(px, vec2i(0), size - 1), 0).rgb;\n}\nfn tame(c: vec3f) -> vec3f {\nreturn c / (1.0 + c);\n}\n@fragment fn fs(in: Out) -> @location(0) vec4f {\nlet size = vec2i(textureDimensions(color));\nlet px = vec2i(in.pos.xy);\nlet c = load(px, size);\nvar out = c;\nif (p.sharpen > 0.0) {\nlet n = load(px + vec2i(0, -1), size);\nlet s = load(px + vec2i(0, 1), size);\nlet e = load(px + vec2i(1, 0), size);\nlet w = load(px + vec2i(-1, 0), size);\nlet tc = tame(c);\nlet mn = min(tc, min(min(tame(n), tame(s)), min(tame(e), tame(w))));\nlet mx = max(tc, max(max(tame(n), tame(s)), max(tame(e), tame(w))));\nlet amp = sqrt(clamp(min(mn, 1.0 - mx) / max(mx, vec3f(1e-4)), vec3f(0.0), vec3f(1.0)));\nlet k = amp * -mix(0.125, 0.2, clamp(p.sharpen, 0.0, 1.0));\nout = max((c + (n + s + e + w) * k) / (1.0 + 4.0 * k), vec3f(0.0));\n}\nif (p.blur > 0.0 && p.has_motion > 0.5) {\nlet msize = vec2i(textureDimensions(motion));\nlet m = textureLoad(motion, clamp(vec2i(vec2f(px) * vec2f(msize) / vec2f(size)), vec2i(0), msize - 1), 0);\nlet v = select(vec2f(0.0), m.rg * p.blur, m.a > 0.0);\nlet len_px = length(v * vec2f(size));\nif (len_px > 0.5) {\nlet n = i32(clamp(p.samples, 2.0, 32.0));\nvar sum = vec3f(0.0);\nfor (var k = 0; k < n; k++) {\nlet t = (f32(k) + 0.5) / f32(n) - 0.5;\nsum += textureSampleLevel(color, smp, in.uv - v * t, 0.0).rgb;\n}\nlet keep = clamp(1.0 - len_px / f32(n), 0.0, 1.0);\nout = mix(sum / f32(n), out, keep);\n}\n}\nreturn vec4f(out, 1.0);\n}";
20
+ };
21
+ /** Linked from shaders/scene/hzb.wesl. */
22
+ export declare const sceneHzb: {
23
+ readonly hash: "b26b6bc789bda1de";
24
+ readonly wgsl: "@group(0) @binding(0) var depth: texture_depth_2d;\n@group(0) @binding(1) var src: texture_2d<f32>;\n@group(0) @binding(2) var dst: texture_storage_2d<r32float, write>;\n@compute @workgroup_size(8, 8) fn hzb_first(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(dst);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\nlet hi = vec2i(textureDimensions(depth)) - 1;\nvar d = 1.0;\nfor (var y = 0; y < 3; y++) {\nfor (var x = 0; x < 3; x++) {\nd = min(d, textureLoad(depth, min(vec2i(id.xy) * 2 + vec2i(x, y), hi), 0));\n}\n}\ntextureStore(dst, id.xy, vec4f(d, 0.0, 0.0, 0.0));\n}\n@compute @workgroup_size(8, 8) fn hzb_down(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(dst);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\nlet hi = vec2i(textureDimensions(src)) - 1;\nvar d = 1.0;\nfor (var y = 0; y < 3; y++) {\nfor (var x = 0; x < 3; x++) {\nd = min(d, textureLoad(src, min(vec2i(id.xy) * 2 + vec2i(x, y), hi), 0).r);\n}\n}\ntextureStore(dst, id.xy, vec4f(d, 0.0, 0.0, 0.0));\n}";
25
+ };
26
+ /** Linked from shaders/scene/motion.wesl. */
27
+ export declare const postMotion: {
28
+ readonly hash: "407e14423b3c1d1b";
29
+ readonly wgsl: "struct Motion {\nview_proj: mat4x4f,\nprev_view_proj: mat4x4f,\nprev_origin: vec4f,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<storage, read> instances: array<vec4u>;\n@group(0) @binding(2) var<storage, read> visible: array<u32>;\n@group(0) @binding(3) var<storage, read> prev_instances: array<vec4u>;\n@group(0) @binding(4) var<uniform> motion: Motion;\n@group(0) @binding(5) var palette: texture_2d<f32>;\n@group(0) @binding(6) var prev_palette: texture_2d<f32>;\n@group(0) @binding(7) var<storage, read> prev_pool: array<f32>;\n@group(0) @binding(8) var<storage, read> prev_skinned: array<f32>;\nstruct Out {\n@builtin(position) clip: vec4f,\n@location(0) now: vec4f,\n@location(1) then: vec4f,\n}\nstruct Pose {\npos: vec3f,\nscale: f32,\nrot: vec4f,\n}\nfn pose_now(i: u32) -> Pose {\nlet a = bitcast<vec4f>(instances[2u * i]);\nreturn Pose(a.xyz - camera.origin.xyz, a.w, bitcast<vec4f>(instances[2u * i + 1u]));\n}\nfn pose_then(i: u32) -> Pose {\nlet a = bitcast<vec4f>(prev_instances[2u * i]);\nreturn Pose(a.xyz - motion.prev_origin.xyz, a.w, bitcast<vec4f>(prev_instances[2u * i + 1u]));\n}\nfn skin_row(i: u32) -> u32 {\nlet cap = arrayLength(&instances) / 3u;\nreturn instances[2u * cap + i].w;\n}\nfn place(now: Pose, then: Pose, p_now: vec3f, p_then: vec3f) -> Out {\nlet w_now = quat_rotate(now.rot, p_now * now.scale) + now.pos;\nlet w_then = quat_rotate(then.rot, p_then * then.scale) + then.pos;\nvar o: Out;\no.clip = camera.view_proj * vec4f(w_now, 1.0);\no.now = motion.view_proj * vec4f(w_now, 1.0);\no.then = motion.prev_view_proj * vec4f(w_then, 1.0);\nreturn o;\n}\n@vertex fn vs(@location(0) position: vec3f, @builtin(vertex_index) v: u32, @builtin(instance_index) slot: u32) -> Out {\nlet i = visible[slot];\nreturn place(pose_now(i), pose_then(i), position, vec3f(prev_pool[v * 8u], prev_pool[v * 8u + 1u], prev_pool[v * 8u + 2u]));\n}\n@vertex fn vs_skinned(@location(0) position: vec3f, @location(6) joints: vec4u, @location(7) weights: vec4f, @builtin(vertex_index) v: u32, @builtin(instance_index) slot: u32) -> Out {\nlet i = visible[slot];\nlet row = skin_row(i);\nlet n = vec3f(0.0, 0.0, 1.0);\nlet now = skin_vertex(palette, row, joints, weights, position, n);\nlet then = skin_vertex(prev_palette, row, joints, weights, vec3f(prev_skinned[v * 8u], prev_skinned[v * 8u + 1u], prev_skinned[v * 8u + 2u]), n);\nreturn place(pose_now(i), pose_then(i), now.position, then.position);\n}\n@fragment fn fs(in: Out) -> @location(0) vec4f {\nlet now = in.now.xy / in.now.w;\nlet then = in.then.xy / max(in.then.w, 1e-6);\nreturn vec4f((now - then) * vec2f(0.5, -0.5), in.then.w, 1.0);\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nfn quat_rotate(q: vec4f, v: vec3f) -> vec3f {\nlet t = 2.0 * cross(q.xyz, v);\nreturn v + q.w * t + cross(q.xyz, t);\n}\nfn skin_vertex(palette: texture_2d<f32>, row: u32, joints: vec4u, weights: vec4f, position: vec3f, normal: vec3f) -> Skinned {\nif (row == 0u || dot(weights, vec4f(1.0)) <= 0.0) {\nreturn Skinned(position, normal);\n}\nlet base = row - 1u;\nlet t = (vec4u(base) + joints) * 3u;\nlet r0 = weights.x * palette_texel(palette, t.x) + weights.y * palette_texel(palette, t.y) + weights.z * palette_texel(palette, t.z) + weights.w * palette_texel(palette, t.w);\nlet r1 = weights.x * palette_texel(palette, t.x + 1u) + weights.y * palette_texel(palette, t.y + 1u) + weights.z * palette_texel(palette, t.z + 1u) + weights.w * palette_texel(palette, t.w + 1u);\nlet r2 = weights.x * palette_texel(palette, t.x + 2u) + weights.y * palette_texel(palette, t.y + 2u) + weights.z * palette_texel(palette, t.z + 2u) + weights.w * palette_texel(palette, t.w + 2u);\nlet p = vec4f(position, 1.0);\nreturn Skinned(vec3f(dot(r0, p), dot(r1, p), dot(r2, p)), vec3f(dot(r0.xyz, normal), dot(r1.xyz, normal), dot(r2.xyz, normal)));\n}\nfn palette_texel(palette: texture_2d<f32>, i: u32) -> vec4f {\nreturn textureLoad(palette, vec2u(i % PALETTE_WIDTH, i / PALETTE_WIDTH), 0);\n}\nstruct Skinned {\nposition: vec3f,\nnormal: vec3f,\n}\nconst PALETTE_WIDTH: u32 = 1024u;";
30
+ };
31
+ /** Linked from shaders/scene/clusters.wesl. */
32
+ export declare const sceneClusters: {
33
+ readonly hash: "c7892829a492870a";
34
+ readonly wgsl: "struct Cluster {\ncenter: vec3f,\nradius: f32,\napex: vec3f,\ncutoff: f32,\naxis: vec3f,\nbase_vertex: u32,\nvert_offset: u32,\ntri_offset: u32,\ntri_count: u32,\nflags: u32,\nself_sphere: vec4f,\nparent_sphere: vec4f,\nself_error: f32,\nparent_error: f32,\nstream: u32,\nkids: u32,\npad1: vec4f,\n}\nstruct Job {\nargs_slot: u32,\nstart: u32,\ncluster_base: u32,\ncluster_count: u32,\nprefix: u32,\ndouble_sided: u32,\npad0: u32,\npad1: u32,\n}\nstruct Params {\njobs: u32,\ntotal: u32,\nhalf: u32,\nindex_half: u32,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<storage, read> instances: array<vec4u>;\n@group(0) @binding(2) var<storage, read> visible: array<u32>;\n@group(0) @binding(3) var<storage, read> args: array<u32>;\n@group(0) @binding(4) var<storage, read> clusters: array<Cluster>;\n@group(0) @binding(5) var<storage, read> jobs: array<Job>;\n@group(0) @binding(6) var<uniform> params: Params;\n@group(0) @binding(7) var<storage, read_write> out_args: array<atomic<u32>, 12>;\n@group(0) @binding(8) var<storage, read_write> out_visible: array<vec4u>;\n@group(0) @binding(9) var<storage, read_write> out_indices: array<u32>;\n@group(0) @binding(10) var<storage, read> triangles: array<u32>;\nfn load_instance(i: u32) -> Instance {\nlet cap = arrayLength(&instances) / 3u;\nlet a = bitcast<vec4f>(instances[2u * i]);\nlet b = bitcast<vec4f>(instances[2u * i + 1u]);\nlet s = instances[2u * cap + i];\nreturn Instance(a.xyz - camera.origin.xyz, a.w, b, s.x, s.y, s.z, s.w);\n}\nconst SLOT: u32 = 128u;\nstruct LodParams {\ncount: u32,\nlod_scale: f32,\nerror_px: f32,\nmin_radius_px: f32,\n}\n@group(0) @binding(16) var<uniform> lodp: LodParams;\nfn error_px(inst: Instance, s: vec4f, e: f32) -> f32 {\nlet c = inst.pos + quat_rotate(inst.rot, s.xyz * inst.scale);\nlet d = max(length(c) - s.w * inst.scale, 1e-3);\nreturn e * inst.scale * lodp.lod_scale / d;\n}\nconst STREAM_MISSING: u32 = 1u;\nconst STREAM_LEAF: u32 = 2u;\nfn on_cut(inst: Instance, cl: Cluster) -> bool {\nif ((cl.stream & STREAM_MISSING) != 0u) {\nreturn false;\n}\nif (cl.self_error <= 0.0 && cl.parent_error == 0.0) {\nreturn true;\n}\nif ((cl.stream & STREAM_LEAF) == 0u && error_px(inst, cl.self_sphere, cl.self_error) > lodp.error_px) {\nreturn false;\n}\nreturn cl.parent_error < 0.0 || error_px(inst, cl.parent_sphere, cl.parent_error) > lodp.error_px;\n}\nfn job_entry(id: vec3u) -> vec4u {\nlet x = id.x;\nif (x >= params.total) {\nreturn vec4u(0u);\n}\nvar j = 0u;\nfor (; j < params.jobs; j++) {\nif (x < jobs[j].prefix + jobs[j].cluster_count) {\nbreak;\n}\n}\nif (j == params.jobs) {\nreturn vec4u(0u);\n}\nlet job = jobs[j];\nif (id.y >= args[job.args_slot * 5u + 1u]) {\nreturn vec4u(0u);\n}\nreturn vec4u(visible[job.start + id.y], job.cluster_base + (x - job.prefix), 1u, 0u);\n}\nfn sphere_in_view(inst: Instance, cl: Cluster) -> vec4f {\nif (!on_cut(inst, cl)) {\nreturn vec4f(0.0, 0.0, 0.0, -1.0);\n}\nlet c = inst.pos + quat_rotate(inst.rot, cl.center * inst.scale);\nlet r = cl.radius * inst.scale;\nfor (var p = 0u; p < 6u; p++) {\nlet plane = camera.frustum[p];\nif (dot(plane.xyz, c) + plane.w < -r) {\nreturn vec4f(0.0, 0.0, 0.0, -1.0);\n}\n}\nif (cl.flags == 0u && cl.cutoff < 1.0) {\nlet apex = inst.pos + quat_rotate(inst.rot, cl.apex * inst.scale);\nif (dot(normalize(apex), quat_rotate(inst.rot, cl.axis)) >= cl.cutoff) {\nreturn vec4f(0.0, 0.0, 0.0, -1.0);\n}\n}\nreturn vec4f(c, r);\n}\n@group(0) @binding(21) var<storage, read_write> page_bits: array<atomic<u32>>;\nfn note_pages(index: u32, cl: Cluster) {\nif (cl.stream == 0u && cl.kids == 0u) {\nreturn;\n}\nlet half = arrayLength(&page_bits) / 2u;\nlet page = cl.stream >> 2u;\natomicOr(&page_bits[half + page / 32u], 1u << (page % 32u));\nif ((cl.stream & STREAM_LEAF) != 0u && cl.kids > 0u && error_px(load_instance(index), cl.self_sphere, cl.self_error) > lodp.error_px) {\nlet want = cl.kids - 1u;\natomicOr(&page_bits[want / 32u], 1u << (want % 32u));\n}\n}\nfn emit(index: u32, ci: u32, cl: Cluster) {\nnote_pages(index, cl);\nlet side = cl.flags;\nlet k = atomicAdd(&out_args[10u + side], 1u);\nlet at = side * params.index_half + atomicAdd(&out_args[side * 5u], cl.tri_count * 3u);\nout_visible[side * params.half + k] = vec4u(index, ci, at, k);\n}\n@compute @workgroup_size(64) fn cull_clusters(@builtin(global_invocation_id) id: vec3u) {\nlet e = job_entry(id);\nif (e.z == 0u) {\nreturn;\n}\nlet cl = clusters[e.y];\nif (sphere_in_view(load_instance(e.x), cl).w >= 0.0) {\nemit(e.x, e.y, cl);\n}\n}\n@group(0) @binding(12) var<uniform> occl: Occlusion;\n@group(0) @binding(13) var hzb: texture_2d<f32>;\n@group(0) @binding(14) var<storage, read_write> recheck: array<atomic<u32>>;\n@group(0) @binding(15) var<storage, read_write> recheck_dispatch: array<u32, 3>;\n@compute @workgroup_size(64) fn cull_clusters_first(@builtin(global_invocation_id) id: vec3u) {\nlet e = job_entry(id);\nif (e.z == 0u) {\nreturn;\n}\nlet cl = clusters[e.y];\nlet s = sphere_in_view(load_instance(e.x), cl);\nif (s.w < 0.0) {\nreturn;\n}\nif (hidden_in(hzb, occl.hzb_size, occl.hzb_levels, s.xyz, s.w, occl.prev)) {\nlet k = atomicAdd(&recheck[0], 1u);\nif (2u * k + 2u < arrayLength(&recheck)) {\natomicStore(&recheck[2u * k + 1u], e.x);\natomicStore(&recheck[2u * k + 2u], e.y);\n}\nreturn;\n}\nemit(e.x, e.y, cl);\n}\n@compute @workgroup_size(64) fn cull_clusters_second(@builtin(global_invocation_id) id: vec3u) {\nlet e = job_entry(id);\nif (e.z == 0u) {\nreturn;\n}\nlet cl = clusters[e.y];\nlet s = sphere_in_view(load_instance(e.x), cl);\nif (s.w >= 0.0 && !hidden_in(hzb, occl.hzb_size, occl.hzb_levels, s.xyz, s.w, camera.view_proj)) {\nemit(e.x, e.y, cl);\n}\n}\n@compute @workgroup_size(1) fn size_recheck() {\nlet n = min(atomicLoad(&recheck[0]), (arrayLength(&recheck) - 1u) / 2u);\nrecheck_dispatch[0] = (n + 63u) / 64u;\nrecheck_dispatch[1] = 1u;\nrecheck_dispatch[2] = 1u;\n}\n@compute @workgroup_size(64) fn recheck_clusters(@builtin(global_invocation_id) id: vec3u) {\nlet n = min(atomicLoad(&recheck[0]), (arrayLength(&recheck) - 1u) / 2u);\nif (id.x >= n) {\nreturn;\n}\nlet index = atomicLoad(&recheck[2u * id.x + 1u]);\nlet ci = atomicLoad(&recheck[2u * id.x + 2u]);\nlet cl = clusters[ci];\nlet s = sphere_in_view(load_instance(index), cl);\nif (s.w >= 0.0 && !hidden_in(hzb, occl.hzb_size, occl.hzb_levels, s.xyz, s.w, camera.view_proj)) {\nemit(index, ci, cl);\n}\n}\nstruct ShadowCull {\nplanes: array<vec4f, 20>,\nlight: vec4f,\n}\n@group(0) @binding(17) var<uniform> shc: ShadowCull;\n@group(0) @binding(18) var<storage, read_write> sh_args: array<atomic<u32>, 32>;\n@group(0) @binding(19) var<storage, read_write> sh_visible: array<vec4u>;\n@compute @workgroup_size(64) fn cull_clusters_shadow(@builtin(global_invocation_id) id: vec3u) {\nlet e = job_entry(id);\nif (e.z == 0u) {\nreturn;\n}\nlet c = id.z;\nlet cl = clusters[e.y];\nlet inst = load_instance(e.x);\nif (!on_cut(inst, cl)) {\nreturn;\n}\nlet center = inst.pos + quat_rotate(inst.rot, cl.center * inst.scale);\nlet r = cl.radius * inst.scale;\nfor (var p = 0u; p < 5u; p++) {\nlet plane = shc.planes[c * 5u + p];\nif (dot(plane.xyz, center) + plane.w < -r) {\nreturn;\n}\n}\nif (cl.flags == 0u && cl.cutoff < 1.0 && dot(-shc.light.xyz, quat_rotate(inst.rot, cl.axis)) >= cl.cutoff) {\nreturn;\n}\nlet side = cl.flags;\nlet b = c * 2u + side;\nlet k = atomicAdd(&sh_args[b * 4u + 1u], 1u);\nsh_visible[c * params.index_half + side * params.half + k] = vec4u(e.x, e.y, 0u, 0u);\n}\n@group(0) @binding(11) var<storage, read_write> dispatch: array<u32, 3>;\n@compute @workgroup_size(1) fn size_expand() {\ndispatch[0] = atomicLoad(&out_args[10]) + atomicLoad(&out_args[11]);\ndispatch[1] = 1u;\ndispatch[2] = 1u;\n}\n@compute @workgroup_size(128) fn expand_clusters(@builtin(workgroup_id) wg: vec3u, @builtin(local_invocation_index) t: u32) {\nlet single = atomicLoad(&out_args[10]);\nlet g = wg.x;\nlet rec = out_visible[select(params.half + g - single, g, g < single)];\nlet cl = clusters[rec.y];\nif (t >= cl.tri_count) {\nreturn;\n}\nfor (var c = 0u; c < 3u; c++) {\nlet b = cl.tri_offset + t * 3u + c;\nout_indices[rec.z + t * 3u + c] = rec.w * SLOT + ((triangles[b / 4u] >> ((b % 4u) * 8u)) & 0xffu);\n}\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nstruct Instance {\npos: vec3f,\nscale: f32,\nrot: vec4f,\ncolor: u32,\nmesh: u32,\nmaterial: u32,\nskin: u32,\n}\nfn quat_rotate(q: vec4f, v: vec3f) -> vec3f {\nlet t = 2.0 * cross(q.xyz, v);\nreturn v + q.w * t + cross(q.xyz, t);\n}\nstruct Occlusion {\nprev: mat4x4f,\nfrustum: array<vec4f, 6>,\nhzb_size: vec2f,\nhzb_levels: u32,\nshadows: u32,\nslots: u32,\nregion: u32,\npad0: u32,\npad1: u32,\n}\nfn hidden_in(hzb: texture_2d<f32>, size: vec2f, levels: u32, c: vec3f, r: f32, view_proj: mat4x4f) -> bool {\nif (levels == 0u) {\nreturn false;\n}\nvar lo = vec2f(1e9);\nvar hi = vec2f(-1e9);\nvar nearest = 0.0;\nfor (var k = 0u; k < 8u; k++) {\nlet corner = c + r * vec3f(select(-1.0, 1.0, (k & 1u) != 0u), select(-1.0, 1.0, (k & 2u) != 0u), select(-1.0, 1.0, (k & 4u) != 0u));\nlet p = view_proj * vec4f(corner, 1.0);\nif (p.w <= 1e-4) {\nreturn false;\n}\nlet n = p.xyz / p.w;\nlo = min(lo, n.xy);\nhi = max(hi, n.xy);\nnearest = max(nearest, n.z);\n}\nif (any(hi < vec2f(-1.0)) || any(lo > vec2f(1.0))) {\nreturn false;\n}\nlet uv_lo = clamp(vec2f(lo.x, -hi.y) * 0.5 + 0.5, vec2f(0.0), vec2f(1.0));\nlet uv_hi = clamp(vec2f(hi.x, -lo.y) * 0.5 + 0.5, vec2f(0.0), vec2f(1.0));\nlet extent = (uv_hi - uv_lo) * size;\nlet level = min(u32(max(ceil(log2(max(max(extent.x, extent.y), 1.0))), 0.0)), levels - 1u);\nlet dims = vec2f(textureDimensions(hzb, level));\nlet t0 = vec2i(uv_lo * dims);\nlet t1 = min(vec2i(uv_hi * dims), vec2i(dims) - 1);\nvar farthest = 1.0;\nfor (var y = t0.y; y <= min(t1.y, t0.y + 2); y++) {\nfor (var x = t0.x; x <= min(t1.x, t0.x + 2); x++) {\nfarthest = min(farthest, textureLoad(hzb, vec2i(x, y), i32(level)).r);\n}\n}\nreturn nearest < farthest;\n}";
35
+ };
36
+ /** Linked from shaders/scene/occlusion_cull.wesl. */
37
+ export declare const sceneOcclusionCull: {
38
+ readonly hash: "d66ce30369f82685";
39
+ readonly wgsl: "@group(0) @binding(7) var<uniform> occl: Occlusion;\n@group(0) @binding(8) var hzb: texture_2d<f32>;\n@group(0) @binding(9) var<storage, read_write> recheck: array<atomic<u32>>;\nfn hidden(c: vec3f, r: f32, view_proj: mat4x4f) -> bool {\nreturn hidden_in(hzb, occl.hzb_size, occl.hzb_levels, c, r, view_proj);\n}\n@compute @workgroup_size(64) fn cull_occlusion(@builtin(global_invocation_id) id: vec3u) {\nlet i = id.x;\nif (i >= params.count) {\nreturn;\n}\nlet inst = load_instance(i);\nlet m = meshes[inst.mesh];\nlet c = inst.pos + quat_rotate(inst.rot, m.center * inst.scale);\nlet r = m.radius * inst.scale;\nif (occl.shadows != 0u) {\nlet dst = place(inst);\nif (dst != 0xffffffffu) {\nvisible[dst] = i;\n}\n}\nfor (var p = 0u; p < 6u; p++) {\nlet plane = occl.frustum[p];\nif (dot(plane.xyz, c) + plane.w < -r) {\nreturn;\n}\n}\nif (hidden(c, r, occl.prev)) {\nlet k = atomicAdd(&recheck[0], 1u);\natomicStore(&recheck[k + 1u], i);\nreturn;\n}\nlet dst = place_lod(inst, c, r, occl.slots, occl.region);\nif (dst != 0xffffffffu) {\nvisible[dst] = i;\n}\n}\n@compute @workgroup_size(64) fn cull_recheck(@builtin(global_invocation_id) id: vec3u) {\nif (id.x >= atomicLoad(&recheck[0])) {\nreturn;\n}\nlet i = atomicLoad(&recheck[id.x + 1u]);\nlet inst = load_instance(i);\nlet m = meshes[inst.mesh];\nlet c = inst.pos + quat_rotate(inst.rot, m.center * inst.scale);\nlet r = m.radius * inst.scale;\nif (hidden(c, r, camera.view_proj)) {\nreturn;\n}\nlet dst = place_lod(inst, c, r, 2u * occl.slots, 2u * occl.region);\nif (dst != 0xffffffffu) {\nvisible[dst] = i;\n}\n}\nstruct Occlusion {\nprev: mat4x4f,\nfrustum: array<vec4f, 6>,\nhzb_size: vec2f,\nhzb_levels: u32,\nshadows: u32,\nslots: u32,\nregion: u32,\npad0: u32,\npad1: u32,\n}\nfn hidden_in(hzb: texture_2d<f32>, size: vec2f, levels: u32, c: vec3f, r: f32, view_proj: mat4x4f) -> bool {\nif (levels == 0u) {\nreturn false;\n}\nvar lo = vec2f(1e9);\nvar hi = vec2f(-1e9);\nvar nearest = 0.0;\nfor (var k = 0u; k < 8u; k++) {\nlet corner = c + r * vec3f(select(-1.0, 1.0, (k & 1u) != 0u), select(-1.0, 1.0, (k & 2u) != 0u), select(-1.0, 1.0, (k & 4u) != 0u));\nlet p = view_proj * vec4f(corner, 1.0);\nif (p.w <= 1e-4) {\nreturn false;\n}\nlet n = p.xyz / p.w;\nlo = min(lo, n.xy);\nhi = max(hi, n.xy);\nnearest = max(nearest, n.z);\n}\nif (any(hi < vec2f(-1.0)) || any(lo > vec2f(1.0))) {\nreturn false;\n}\nlet uv_lo = clamp(vec2f(lo.x, -hi.y) * 0.5 + 0.5, vec2f(0.0), vec2f(1.0));\nlet uv_hi = clamp(vec2f(hi.x, -lo.y) * 0.5 + 0.5, vec2f(0.0), vec2f(1.0));\nlet extent = (uv_hi - uv_lo) * size;\nlet level = min(u32(max(ceil(log2(max(max(extent.x, extent.y), 1.0))), 0.0)), levels - 1u);\nlet dims = vec2f(textureDimensions(hzb, level));\nlet t0 = vec2i(uv_lo * dims);\nlet t1 = min(vec2i(uv_hi * dims), vec2i(dims) - 1);\nvar farthest = 1.0;\nfor (var y = t0.y; y <= min(t1.y, t0.y + 2); y++) {\nfor (var x = t0.x; x <= min(t1.x, t0.x + 2); x++) {\nfarthest = min(farthest, textureLoad(hzb, vec2i(x, y), i32(level)).r);\n}\n}\nreturn nearest < farthest;\n}\n@group(0) @binding(4) var<uniform> params: CullParams;\nfn load_instance(i: u32) -> Instance {\nlet cap = arrayLength(&instances) / 3u;\nlet a = bitcast<vec4f>(instances[2u * i]);\nlet b = bitcast<vec4f>(instances[2u * i + 1u]);\nlet s = instances[2u * cap + i];\nreturn Instance(a.xyz - camera.origin.xyz, a.w, b, s.x, s.y, s.z, s.w);\n}\n@group(0) @binding(2) var<storage, read> meshes: array<MeshInfo>;\nfn quat_rotate(q: vec4f, v: vec3f) -> vec3f {\nlet t = 2.0 * cross(q.xyz, v);\nreturn v + q.w * t + cross(q.xyz, t);\n}\nfn place(inst: Instance) -> u32 {\nlet m = meshes[inst.mesh];\nlet c = inst.pos + quat_rotate(inst.rot, m.center * inst.scale);\nlet r = m.radius * inst.scale;\nfor (var p = 0u; p < 6u; p++) {\nlet plane = camera.frustum[p];\nif (dot(plane.xyz, c) + plane.w < -r) {\nreturn 0xffffffffu;\n}\n}\nreturn place_lod(inst, c, r, 0u, 0u);\n}\n@group(0) @binding(5) var<storage, read_write> visible: array<u32>;\nfn place_lod(inst: Instance, c: vec3f, r: f32, slots: u32, region: u32) -> u32 {\nlet m = meshes[inst.mesh];\nlet px = params.lod_scale / max(length(c) - r, 1e-3);\nif (r * px < params.min_radius_px) {\nreturn 0xffffffffu;\n}\nvar lod = 0u;\nfor (var l = 1u; l < m.lod_count; l++) {\nif (m.error[l / 4u][l % 4u] * inst.scale * px > params.error_px) {\nbreak;\n}\nlod = l;\n}\nlet k = atomicAdd(&args[m.draw_base + lod + slots].instance_count, 1u);\nreturn m.instance_base + lod * m.instance_count + k + region;\n}\nstruct Instance {\npos: vec3f,\nscale: f32,\nrot: vec4f,\ncolor: u32,\nmesh: u32,\nmaterial: u32,\nskin: u32,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\n@group(0) @binding(3) var<storage, read_write> args: array<DrawArgs>;\nstruct DrawArgs {\nindex_count: u32,\ninstance_count: atomic<u32>,\nfirst_index: u32,\nbase_vertex: i32,\nfirst_instance: u32,\n}\nstruct CullParams {\ncount: u32,\nlod_scale: f32,\nerror_px: f32,\nmin_radius_px: f32,\n}\n@group(0) @binding(1) var<storage, read> instances: array<vec4u>;\nstruct MeshInfo {\ncenter: vec3f,\nradius: f32,\nlod_count: u32,\ndraw_base: u32,\ninstance_base: u32,\ninstance_count: u32,\nerror: array<vec4f, 2>,\n}";
40
+ };
@@ -0,0 +1,43 @@
1
+ // GENERATED by tools/shaders-gen.mjs from shaders/**/*.wesl. Do not edit.
2
+ // Regenerate with `node tools/shaders-gen.mjs`; CI runs it with --check.
3
+ /** Linked from shaders/post/bloom.wesl. */
4
+ export const postBloom = {
5
+ hash: 'ce493f335d49f31e',
6
+ wgsl: "struct BloomParams {\n/** Linear luminance where bloom starts (0: everything blooms, energy-conserving). */\nthreshold: f32,\n/** The soft knee's width, as a share of the threshold. */\nknee: f32,\n/** The upsample tent's radius, in texels of the smaller level. */\nradius: f32,\npad: f32,\n}\n@group(0) @binding(0) var src: texture_2d<f32>;\n@group(0) @binding(1) var smp: sampler;\n@group(0) @binding(2) var<uniform> bloom: BloomParams;\n@group(0) @binding(3) var low: texture_2d<f32>;\nstruct Out {\n@builtin(position) pos: vec4f,\n@location(0) uv: vec2f,\n}\n@vertex fn vs(@builtin(vertex_index) i: u32) -> Out {\nlet p = vec2f(f32((i << 1u) & 2u), f32(i & 2u));\nvar o: Out;\no.pos = vec4f(p * 2.0 - 1.0, 0.0, 1.0);\no.uv = vec2f(p.x, 1.0 - p.y);\nreturn o;\n}\nfn luma(c: vec3f) -> f32 {\nreturn dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\nfn tap(uv: vec2f) -> vec3f {\nreturn min(textureSampleLevel(src, smp, uv, 0.0).rgb, vec3f(65000.0));\n}\nfn downsample(uv: vec2f, karis: bool) -> vec3f {\nlet t = 1.0 / vec2f(textureDimensions(src));\nlet a = tap(uv + t * vec2f(-2.0, -2.0));\nlet b = tap(uv + t * vec2f(0.0, -2.0));\nlet c = tap(uv + t * vec2f(2.0, -2.0));\nlet d = tap(uv + t * vec2f(-1.0, -1.0));\nlet e = tap(uv + t * vec2f(1.0, -1.0));\nlet f = tap(uv + t * vec2f(-2.0, 0.0));\nlet g = tap(uv);\nlet h = tap(uv + t * vec2f(2.0, 0.0));\nlet i = tap(uv + t * vec2f(-1.0, 1.0));\nlet j = tap(uv + t * vec2f(1.0, 1.0));\nlet k = tap(uv + t * vec2f(-2.0, 2.0));\nlet l = tap(uv + t * vec2f(0.0, 2.0));\nlet m = tap(uv + t * vec2f(2.0, 2.0));\nlet boxes = array<vec3f, 5>((d + e + i + j) * 0.25, (a + b + f + g) * 0.25, (b + c + g + h) * 0.25, (f + g + k + l) * 0.25, (g + h + l + m) * 0.25);\nlet weights = array<f32, 5>(0.5, 0.125, 0.125, 0.125, 0.125);\nvar sum = vec3f(0.0);\nvar total = 0.0;\nfor (var n = 0; n < 5; n++) {\nvar w = weights[n];\nif (karis) {\nw = w / (1.0 + luma(boxes[n]));\n}\nsum += boxes[n] * w;\ntotal += w;\n}\nreturn sum / total;\n}\n@fragment fn fs_prefilter(in: Out) -> @location(0) vec4f {\nlet c = downsample(in.uv, true);\nif (bloom.threshold <= 0.0) {\nreturn vec4f(c, 1.0);\n}\nlet br = max(c.r, max(c.g, c.b));\nlet k = bloom.threshold * bloom.knee + 1e-5;\nvar soft = clamp(br - bloom.threshold + k, 0.0, 2.0 * k);\nsoft = soft * soft / (4.0 * k);\nlet w = max(soft, br - bloom.threshold) / max(br, 1e-5);\nreturn vec4f(c * w, 1.0);\n}\n@fragment fn fs_down(in: Out) -> @location(0) vec4f {\nreturn vec4f(downsample(in.uv, false), 1.0);\n}\n@fragment fn fs_up(in: Out) -> @location(0) vec4f {\nlet t = bloom.radius / vec2f(textureDimensions(low));\nvar s = textureSampleLevel(low, smp, in.uv, 0.0).rgb * 4.0;\ns += (textureSampleLevel(low, smp, in.uv + vec2f(-t.x, 0.0), 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(t.x, 0.0), 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(0.0, -t.y), 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(0.0, t.y), 0.0).rgb) * 2.0;\ns += textureSampleLevel(low, smp, in.uv - t, 0.0).rgb + textureSampleLevel(low, smp, in.uv + t, 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(-t.x, t.y), 0.0).rgb + textureSampleLevel(low, smp, in.uv + vec2f(t.x, -t.y), 0.0).rgb;\nreturn vec4f(textureSampleLevel(src, smp, in.uv, 0.0).rgb + s / 16.0, 1.0);\n}",
7
+ };
8
+ /** Linked from shaders/post/fxaa.wesl. */
9
+ export const postFxaa = {
10
+ hash: '362146f9cb191613',
11
+ wgsl: "@group(0) @binding(0) var ldr: texture_2d<f32>;\n@group(0) @binding(1) var smp: sampler;\nstruct Out {\n@builtin(position) pos: vec4f,\n@location(0) uv: vec2f,\n}\n@vertex fn vs(@builtin(vertex_index) i: u32) -> Out {\nlet p = vec2f(f32((i << 1u) & 2u), f32(i & 2u));\nvar o: Out;\no.pos = vec4f(p * 2.0 - 1.0, 0.0, 1.0);\no.uv = vec2f(p.x, 1.0 - p.y);\nreturn o;\n}\nconst EDGE_MIN = 0.0312;\nconst EDGE_MAX = 0.125;\nconst SUBPIXEL = 0.75;\nconst STEPS = 10;\nfn luma_at(uv: vec2f) -> f32 {\nreturn dot(textureSampleLevel(ldr, smp, uv, 0.0).rgb, vec3f(0.299, 0.587, 0.114));\n}\nfn quality(i: i32) -> f32 {\nif (i < 5) {\nreturn 1.0;\n}\nif (i == 5) {\nreturn 1.5;\n}\nif (i < 9) {\nreturn 2.0;\n}\nreturn 4.0;\n}\n@fragment fn fs(in: Out) -> @location(0) vec4f {\nlet texel = 1.0 / vec2f(textureDimensions(ldr));\nlet uv = in.uv;\nlet centre = textureSampleLevel(ldr, smp, uv, 0.0);\nlet m = dot(centre.rgb, vec3f(0.299, 0.587, 0.114));\nlet n = luma_at(uv + vec2f(0.0, -texel.y));\nlet s = luma_at(uv + vec2f(0.0, texel.y));\nlet e = luma_at(uv + vec2f(texel.x, 0.0));\nlet w = luma_at(uv + vec2f(-texel.x, 0.0));\nlet lo = min(m, min(min(n, s), min(e, w)));\nlet hi = max(m, max(max(n, s), max(e, w)));\nlet range = hi - lo;\nif (range < max(EDGE_MIN, hi * EDGE_MAX)) {\nreturn centre;\n}\nlet nw = luma_at(uv + vec2f(-texel.x, -texel.y));\nlet ne = luma_at(uv + vec2f(texel.x, -texel.y));\nlet sw = luma_at(uv + vec2f(-texel.x, texel.y));\nlet se = luma_at(uv + vec2f(texel.x, texel.y));\nlet horizontal = abs(-2.0 * w + nw + sw) + 2.0 * abs(-2.0 * m + n + s) + abs(-2.0 * e + ne + se) >= abs(-2.0 * n + nw + ne) + 2.0 * abs(-2.0 * m + w + e) + abs(-2.0 * s + sw + se);\nlet l1 = select(w, n, horizontal);\nlet l2 = select(e, s, horizontal);\nlet g1 = l1 - m;\nlet g2 = l2 - m;\nlet steep1 = abs(g1) >= abs(g2);\nlet scaled = 0.25 * max(abs(g1), abs(g2));\nvar step_len = select(texel.x, texel.y, horizontal);\nvar avg = 0.5 * (l2 + m);\nif (steep1) {\nstep_len = -step_len;\navg = 0.5 * (l1 + m);\n}\nvar cur = uv;\nif (horizontal) {\ncur.y += step_len * 0.5;\n} else {\ncur.x += step_len * 0.5;\n}\nlet along = select(vec2f(0.0, texel.y), vec2f(texel.x, 0.0), horizontal);\nvar uv1 = cur - along;\nvar uv2 = cur + along;\nvar end1 = luma_at(uv1) - avg;\nvar end2 = luma_at(uv2) - avg;\nvar done1 = abs(end1) >= scaled;\nvar done2 = abs(end2) >= scaled;\nfor (var i = 1; i < STEPS && !(done1 && done2); i++) {\nif (!done1) {\nuv1 -= along * quality(i);\nend1 = luma_at(uv1) - avg;\ndone1 = abs(end1) >= scaled;\n}\nif (!done2) {\nuv2 += along * quality(i);\nend2 = luma_at(uv2) - avg;\ndone2 = abs(end2) >= scaled;\n}\n}\nlet d1 = select(uv.y - uv1.y, uv.x - uv1.x, horizontal);\nlet d2 = select(uv2.y - uv.y, uv2.x - uv.x, horizontal);\nlet nearer1 = d1 < d2;\nlet dist = min(d1, d2);\nlet pixel_offset = -dist / (d1 + d2) + 0.5;\nlet smaller = m < avg;\nlet right_way = (select(end2, end1, nearer1) < 0.0) != smaller;\nvar offset = select(0.0, pixel_offset, right_way);\nlet mean = (2.0 * (n + s + e + w) + nw + ne + sw + se) / 12.0;\nlet sub1 = clamp(abs(mean - m) / range, 0.0, 1.0);\nlet sub2 = (-2.0 * sub1 + 3.0) * sub1 * sub1;\noffset = max(offset, sub2 * sub2 * SUBPIXEL);\nvar at = uv;\nif (horizontal) {\nat.y += offset * step_len;\n} else {\nat.x += offset * step_len;\n}\nreturn vec4f(textureSampleLevel(ldr, smp, at, 0.0).rgb, 1.0);\n}",
12
+ };
13
+ /** Linked from shaders/post/taa.wesl. */
14
+ export const postTaa = {
15
+ hash: '05819cf45a86a800',
16
+ wgsl: "struct TaaParams {\nreproject: mat4x4f,\nnear: f32,\nmotion: u32,\nblend: f32,\nreset: u32,\njitter_px: vec2f,\nupscale: u32,\npad: u32,\n}\n@group(0) @binding(0) var current: texture_2d<f32>;\n@group(0) @binding(1) var history: texture_2d<f32>;\n@group(0) @binding(2) var depth: texture_depth_2d;\n@group(0) @binding(3) var smp: sampler;\n@group(0) @binding(4) var<uniform> taa: TaaParams;\n@group(0) @binding(5) var motion_tex: texture_2d<f32>;\n@vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {\nlet p = vec2f(f32((i << 1u) & 2u), f32(i & 2u));\nreturn vec4f(p * 2.0 - 1.0, 0.0, 1.0);\n}\nfn to_ycocg(c: vec3f) -> vec3f {\nreturn vec3f(dot(c, vec3f(0.25, 0.5, 0.25)), dot(c, vec3f(0.5, 0.0, -0.5)), dot(c, vec3f(-0.25, 0.5, -0.25)));\n}\nfn from_ycocg(c: vec3f) -> vec3f {\nreturn vec3f(c.x + c.y - c.z, c.x + c.z, c.x - c.y - c.z);\n}\nfn compress(c: vec3f) -> vec3f {\nreturn c / (1.0 + max(c.r, max(c.g, c.b)));\n}\nfn expand(c: vec3f) -> vec3f {\nreturn c / max(1.0 - max(c.r, max(c.g, c.b)), 1e-4);\n}\nconst FAR = 60000.0;\nfn view_depth(d: f32) -> f32 {\nreturn select(FAR, min(taa.near / d, FAR), d > 0.0);\n}\nfn same_surface(puv: vec2f, size: vec2f, expected: f32) -> bool {\nlet base = vec2i(floor(puv * size - 0.5));\nlet hi = vec2i(size) - 1;\nlet e = min(expected, FAR);\nfor (var k = 0; k < 4; k++) {\nlet q = clamp(base + vec2i(k & 1, k >> 1), vec2i(0), hi);\nlet h = textureLoad(history, q, 0).a;\nif (abs(h - e) <= 0.05 * e + 0.01) {\nreturn true;\n}\n}\nreturn false;\n}\nfn load(p: vec2i, size: vec2i) -> vec3f {\nlet c = textureLoad(current, clamp(p, vec2i(0), size - 1), 0).rgb;\nreturn select(vec3f(0.0), compress(min(c, vec3f(65000.0))), all(c == c));\n}\nfn history_at(uv: vec2f, size: vec2f) -> vec3f {\nlet pos = uv * size;\nlet centre = floor(pos - 0.5) + 0.5;\nlet f = pos - centre;\nlet w0 = f * (-0.5 + f * (1.0 - 0.5 * f));\nlet w1 = 1.0 + f * f * (-2.5 + 1.5 * f);\nlet w2 = f * (0.5 + f * (2.0 - 1.5 * f));\nlet w3 = f * f * (-0.5 + 0.5 * f);\nlet w12 = w1 + w2;\nlet t0 = (centre - 1.0) / size;\nlet t3 = (centre + 2.0) / size;\nlet t12 = (centre + w2 / w12) / size;\nvar c = textureSampleLevel(history, smp, vec2f(t12.x, t0.y), 0.0).rgb * (w12.x * w0.y);\nc += textureSampleLevel(history, smp, vec2f(t0.x, t12.y), 0.0).rgb * (w0.x * w12.y);\nc += textureSampleLevel(history, smp, vec2f(t12.x, t12.y), 0.0).rgb * (w12.x * w12.y);\nc += textureSampleLevel(history, smp, vec2f(t3.x, t12.y), 0.0).rgb * (w3.x * w12.y);\nc += textureSampleLevel(history, smp, vec2f(t12.x, t3.y), 0.0).rgb * (w12.x * w3.y);\nlet total = w12.x * w0.y + w0.x * w12.y + w12.x * w12.y + w3.x * w12.y + w12.x * w3.y;\nreturn max(c / total, vec3f(0.0));\n}\nfn upscaled(at: vec4f) -> vec4f {\nlet isize = vec2i(textureDimensions(current));\nlet size = vec2f(isize);\nlet osize = vec2f(textureDimensions(history));\nlet uv = at.xy / osize;\nlet ratio = osize / size;\nlet pin = uv * size;\nlet base = vec2i(floor(pin));\nvar m1 = vec3f(0.0);\nvar m2 = vec3f(0.0);\nvar sum = vec3f(0.0);\nvar wsum = 0.0;\nvar wmax = 0.0;\nfor (var y = -1; y <= 1; y++) {\nfor (var x = -1; x <= 1; x++) {\nlet q = base + vec2i(x, y);\nlet c = to_ycocg(load(q, isize));\nm1 += c;\nm2 += c * c;\nlet s = (vec2f(q) + 0.5 - taa.jitter_px - pin) * ratio;\nlet w = exp(-2.29 * dot(s, s));\nsum += c * w;\nwsum += w;\nwmax = max(wmax, w);\n}\n}\nlet here = sum / max(wsum, 1e-5);\nlet pd = clamp(base, vec2i(0), isize - 1);\nlet d = textureLoad(depth, pd, 0);\nlet fresh = vec4f(expand(from_ycocg(here)), view_depth(d));\nif (taa.reset != 0u) {\nreturn fresh;\n}\nvar puv: vec2f;\nvar expected: f32;\nlet mv = select(vec4f(0.0), textureLoad(motion_tex, pd, 0), taa.motion != 0u);\nif (mv.a > 0.5) {\npuv = uv - mv.xy;\nexpected = mv.z;\n} else {\nlet ndc = vec2f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0);\nlet prev = taa.reproject * vec4f(ndc, d, 1.0);\nif (prev.w <= 0.0 && d > 0.0) {\nreturn fresh;\n}\nlet pn = prev.xy / prev.w;\npuv = vec2f(pn.x * 0.5 + 0.5, 0.5 - pn.y * 0.5);\nexpected = select(FAR, prev.w * view_depth(d), d > 0.0);\n}\nif (any(puv < vec2f(0.0)) || any(puv > vec2f(1.0)) || !same_surface(puv, osize, expected)) {\nreturn fresh;\n}\nlet hist = to_ycocg(compress(history_at(puv, osize)));\nlet mean = m1 / 9.0;\nlet sigma = sqrt(max(m2 / 9.0 - mean * mean, vec3f(0.0)));\nlet half_box = 1.25 * sigma + 1e-5;\nlet off = hist - mean;\nlet units = abs(off / half_box);\nlet k = max(units.x, max(units.y, units.z));\nlet clipped = select(hist, mean + off / k, k > 1.0);\nlet out = mix(clipped, here, taa.blend * wmax);\nreturn vec4f(expand(from_ycocg(out)), fresh.a);\n}\n@fragment fn fs(@builtin(position) at: vec4f) -> @location(0) vec4f {\nif (taa.upscale != 0u) {\nreturn upscaled(at);\n}\nlet isize = vec2i(textureDimensions(current));\nlet size = vec2f(isize);\nlet p = vec2i(at.xy);\nvar m1 = vec3f(0.0);\nvar m2 = vec3f(0.0);\nvar here = vec3f(0.0);\nfor (var y = -1; y <= 1; y++) {\nfor (var x = -1; x <= 1; x++) {\nlet c = to_ycocg(load(p + vec2i(x, y), isize));\nm1 += c;\nm2 += c * c;\nif (x == 0 && y == 0) {\nhere = c;\n}\n}\n}\nlet d = textureLoad(depth, p, 0);\nlet fresh = vec4f(expand(from_ycocg(here)), view_depth(d));\nif (taa.reset != 0u) {\nreturn fresh;\n}\nvar puv: vec2f;\nvar expected: f32;\nlet mv = select(vec4f(0.0), textureLoad(motion_tex, p, 0), taa.motion != 0u);\nif (mv.a > 0.5) {\npuv = at.xy / size - mv.xy;\nexpected = mv.z;\n} else {\nlet ndc = vec2f(at.x / size.x * 2.0 - 1.0, 1.0 - at.y / size.y * 2.0);\nlet prev = taa.reproject * vec4f(ndc, d, 1.0);\nif (prev.w <= 0.0 && d > 0.0) {\nreturn fresh;\n}\nlet pn = prev.xy / prev.w;\npuv = vec2f(pn.x * 0.5 + 0.5, 0.5 - pn.y * 0.5);\nexpected = select(FAR, prev.w * view_depth(d), d > 0.0);\n}\nif (any(puv < vec2f(0.0)) || any(puv > vec2f(1.0)) || !same_surface(puv, size, expected)) {\nreturn fresh;\n}\nlet hist = to_ycocg(compress(history_at(puv, size)));\nlet mean = m1 / 9.0;\nlet sigma = sqrt(max(m2 / 9.0 - mean * mean, vec3f(0.0)));\nlet half_box = 1.25 * sigma + 1e-5;\nlet off = hist - mean;\nlet units = abs(off / half_box);\nlet k = max(units.x, max(units.y, units.z));\nlet clipped = select(hist, mean + off / k, k > 1.0);\nlet out = mix(clipped, here, taa.blend);\nreturn vec4f(expand(from_ycocg(out)), fresh.a);\n}",
17
+ };
18
+ /** Linked from shaders/post/taa_post.wesl. */
19
+ export const postTaaPost = {
20
+ hash: '5806c7af9e6201d4',
21
+ wgsl: "@group(0) @binding(0) var color: texture_2d<f32>;\n@group(0) @binding(1) var motion: texture_2d<f32>;\n@group(0) @binding(2) var smp: sampler;\nstruct Params {\nsharpen: f32,\nblur: f32,\nsamples: f32,\nhas_motion: f32,\n}\n@group(0) @binding(3) var<uniform> p: Params;\nstruct Out {\n@builtin(position) pos: vec4f,\n@location(0) uv: vec2f,\n}\n@vertex fn vs(@builtin(vertex_index) i: u32) -> Out {\nlet q = vec2f(f32((i << 1u) & 2u), f32(i & 2u));\nvar o: Out;\no.pos = vec4f(q * 2.0 - 1.0, 0.0, 1.0);\no.uv = vec2f(q.x, 1.0 - q.y);\nreturn o;\n}\nfn load(px: vec2i, size: vec2i) -> vec3f {\nreturn textureLoad(color, clamp(px, vec2i(0), size - 1), 0).rgb;\n}\nfn tame(c: vec3f) -> vec3f {\nreturn c / (1.0 + c);\n}\n@fragment fn fs(in: Out) -> @location(0) vec4f {\nlet size = vec2i(textureDimensions(color));\nlet px = vec2i(in.pos.xy);\nlet c = load(px, size);\nvar out = c;\nif (p.sharpen > 0.0) {\nlet n = load(px + vec2i(0, -1), size);\nlet s = load(px + vec2i(0, 1), size);\nlet e = load(px + vec2i(1, 0), size);\nlet w = load(px + vec2i(-1, 0), size);\nlet tc = tame(c);\nlet mn = min(tc, min(min(tame(n), tame(s)), min(tame(e), tame(w))));\nlet mx = max(tc, max(max(tame(n), tame(s)), max(tame(e), tame(w))));\nlet amp = sqrt(clamp(min(mn, 1.0 - mx) / max(mx, vec3f(1e-4)), vec3f(0.0), vec3f(1.0)));\nlet k = amp * -mix(0.125, 0.2, clamp(p.sharpen, 0.0, 1.0));\nout = max((c + (n + s + e + w) * k) / (1.0 + 4.0 * k), vec3f(0.0));\n}\nif (p.blur > 0.0 && p.has_motion > 0.5) {\nlet msize = vec2i(textureDimensions(motion));\nlet m = textureLoad(motion, clamp(vec2i(vec2f(px) * vec2f(msize) / vec2f(size)), vec2i(0), msize - 1), 0);\nlet v = select(vec2f(0.0), m.rg * p.blur, m.a > 0.0);\nlet len_px = length(v * vec2f(size));\nif (len_px > 0.5) {\nlet n = i32(clamp(p.samples, 2.0, 32.0));\nvar sum = vec3f(0.0);\nfor (var k = 0; k < n; k++) {\nlet t = (f32(k) + 0.5) / f32(n) - 0.5;\nsum += textureSampleLevel(color, smp, in.uv - v * t, 0.0).rgb;\n}\nlet keep = clamp(1.0 - len_px / f32(n), 0.0, 1.0);\nout = mix(sum / f32(n), out, keep);\n}\n}\nreturn vec4f(out, 1.0);\n}",
22
+ };
23
+ /** Linked from shaders/scene/hzb.wesl. */
24
+ export const sceneHzb = {
25
+ hash: 'b26b6bc789bda1de',
26
+ wgsl: "@group(0) @binding(0) var depth: texture_depth_2d;\n@group(0) @binding(1) var src: texture_2d<f32>;\n@group(0) @binding(2) var dst: texture_storage_2d<r32float, write>;\n@compute @workgroup_size(8, 8) fn hzb_first(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(dst);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\nlet hi = vec2i(textureDimensions(depth)) - 1;\nvar d = 1.0;\nfor (var y = 0; y < 3; y++) {\nfor (var x = 0; x < 3; x++) {\nd = min(d, textureLoad(depth, min(vec2i(id.xy) * 2 + vec2i(x, y), hi), 0));\n}\n}\ntextureStore(dst, id.xy, vec4f(d, 0.0, 0.0, 0.0));\n}\n@compute @workgroup_size(8, 8) fn hzb_down(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(dst);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\nlet hi = vec2i(textureDimensions(src)) - 1;\nvar d = 1.0;\nfor (var y = 0; y < 3; y++) {\nfor (var x = 0; x < 3; x++) {\nd = min(d, textureLoad(src, min(vec2i(id.xy) * 2 + vec2i(x, y), hi), 0).r);\n}\n}\ntextureStore(dst, id.xy, vec4f(d, 0.0, 0.0, 0.0));\n}",
27
+ };
28
+ /** Linked from shaders/scene/motion.wesl. */
29
+ export const postMotion = {
30
+ hash: '407e14423b3c1d1b',
31
+ wgsl: "struct Motion {\nview_proj: mat4x4f,\nprev_view_proj: mat4x4f,\nprev_origin: vec4f,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<storage, read> instances: array<vec4u>;\n@group(0) @binding(2) var<storage, read> visible: array<u32>;\n@group(0) @binding(3) var<storage, read> prev_instances: array<vec4u>;\n@group(0) @binding(4) var<uniform> motion: Motion;\n@group(0) @binding(5) var palette: texture_2d<f32>;\n@group(0) @binding(6) var prev_palette: texture_2d<f32>;\n@group(0) @binding(7) var<storage, read> prev_pool: array<f32>;\n@group(0) @binding(8) var<storage, read> prev_skinned: array<f32>;\nstruct Out {\n@builtin(position) clip: vec4f,\n@location(0) now: vec4f,\n@location(1) then: vec4f,\n}\nstruct Pose {\npos: vec3f,\nscale: f32,\nrot: vec4f,\n}\nfn pose_now(i: u32) -> Pose {\nlet a = bitcast<vec4f>(instances[2u * i]);\nreturn Pose(a.xyz - camera.origin.xyz, a.w, bitcast<vec4f>(instances[2u * i + 1u]));\n}\nfn pose_then(i: u32) -> Pose {\nlet a = bitcast<vec4f>(prev_instances[2u * i]);\nreturn Pose(a.xyz - motion.prev_origin.xyz, a.w, bitcast<vec4f>(prev_instances[2u * i + 1u]));\n}\nfn skin_row(i: u32) -> u32 {\nlet cap = arrayLength(&instances) / 3u;\nreturn instances[2u * cap + i].w;\n}\nfn place(now: Pose, then: Pose, p_now: vec3f, p_then: vec3f) -> Out {\nlet w_now = quat_rotate(now.rot, p_now * now.scale) + now.pos;\nlet w_then = quat_rotate(then.rot, p_then * then.scale) + then.pos;\nvar o: Out;\no.clip = camera.view_proj * vec4f(w_now, 1.0);\no.now = motion.view_proj * vec4f(w_now, 1.0);\no.then = motion.prev_view_proj * vec4f(w_then, 1.0);\nreturn o;\n}\n@vertex fn vs(@location(0) position: vec3f, @builtin(vertex_index) v: u32, @builtin(instance_index) slot: u32) -> Out {\nlet i = visible[slot];\nreturn place(pose_now(i), pose_then(i), position, vec3f(prev_pool[v * 8u], prev_pool[v * 8u + 1u], prev_pool[v * 8u + 2u]));\n}\n@vertex fn vs_skinned(@location(0) position: vec3f, @location(6) joints: vec4u, @location(7) weights: vec4f, @builtin(vertex_index) v: u32, @builtin(instance_index) slot: u32) -> Out {\nlet i = visible[slot];\nlet row = skin_row(i);\nlet n = vec3f(0.0, 0.0, 1.0);\nlet now = skin_vertex(palette, row, joints, weights, position, n);\nlet then = skin_vertex(prev_palette, row, joints, weights, vec3f(prev_skinned[v * 8u], prev_skinned[v * 8u + 1u], prev_skinned[v * 8u + 2u]), n);\nreturn place(pose_now(i), pose_then(i), now.position, then.position);\n}\n@fragment fn fs(in: Out) -> @location(0) vec4f {\nlet now = in.now.xy / in.now.w;\nlet then = in.then.xy / max(in.then.w, 1e-6);\nreturn vec4f((now - then) * vec2f(0.5, -0.5), in.then.w, 1.0);\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nfn quat_rotate(q: vec4f, v: vec3f) -> vec3f {\nlet t = 2.0 * cross(q.xyz, v);\nreturn v + q.w * t + cross(q.xyz, t);\n}\nfn skin_vertex(palette: texture_2d<f32>, row: u32, joints: vec4u, weights: vec4f, position: vec3f, normal: vec3f) -> Skinned {\nif (row == 0u || dot(weights, vec4f(1.0)) <= 0.0) {\nreturn Skinned(position, normal);\n}\nlet base = row - 1u;\nlet t = (vec4u(base) + joints) * 3u;\nlet r0 = weights.x * palette_texel(palette, t.x) + weights.y * palette_texel(palette, t.y) + weights.z * palette_texel(palette, t.z) + weights.w * palette_texel(palette, t.w);\nlet r1 = weights.x * palette_texel(palette, t.x + 1u) + weights.y * palette_texel(palette, t.y + 1u) + weights.z * palette_texel(palette, t.z + 1u) + weights.w * palette_texel(palette, t.w + 1u);\nlet r2 = weights.x * palette_texel(palette, t.x + 2u) + weights.y * palette_texel(palette, t.y + 2u) + weights.z * palette_texel(palette, t.z + 2u) + weights.w * palette_texel(palette, t.w + 2u);\nlet p = vec4f(position, 1.0);\nreturn Skinned(vec3f(dot(r0, p), dot(r1, p), dot(r2, p)), vec3f(dot(r0.xyz, normal), dot(r1.xyz, normal), dot(r2.xyz, normal)));\n}\nfn palette_texel(palette: texture_2d<f32>, i: u32) -> vec4f {\nreturn textureLoad(palette, vec2u(i % PALETTE_WIDTH, i / PALETTE_WIDTH), 0);\n}\nstruct Skinned {\nposition: vec3f,\nnormal: vec3f,\n}\nconst PALETTE_WIDTH: u32 = 1024u;",
32
+ };
33
+ /** Linked from shaders/scene/clusters.wesl. */
34
+ export const sceneClusters = {
35
+ hash: 'c7892829a492870a',
36
+ wgsl: "struct Cluster {\ncenter: vec3f,\nradius: f32,\napex: vec3f,\ncutoff: f32,\naxis: vec3f,\nbase_vertex: u32,\nvert_offset: u32,\ntri_offset: u32,\ntri_count: u32,\nflags: u32,\nself_sphere: vec4f,\nparent_sphere: vec4f,\nself_error: f32,\nparent_error: f32,\nstream: u32,\nkids: u32,\npad1: vec4f,\n}\nstruct Job {\nargs_slot: u32,\nstart: u32,\ncluster_base: u32,\ncluster_count: u32,\nprefix: u32,\ndouble_sided: u32,\npad0: u32,\npad1: u32,\n}\nstruct Params {\njobs: u32,\ntotal: u32,\nhalf: u32,\nindex_half: u32,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<storage, read> instances: array<vec4u>;\n@group(0) @binding(2) var<storage, read> visible: array<u32>;\n@group(0) @binding(3) var<storage, read> args: array<u32>;\n@group(0) @binding(4) var<storage, read> clusters: array<Cluster>;\n@group(0) @binding(5) var<storage, read> jobs: array<Job>;\n@group(0) @binding(6) var<uniform> params: Params;\n@group(0) @binding(7) var<storage, read_write> out_args: array<atomic<u32>, 12>;\n@group(0) @binding(8) var<storage, read_write> out_visible: array<vec4u>;\n@group(0) @binding(9) var<storage, read_write> out_indices: array<u32>;\n@group(0) @binding(10) var<storage, read> triangles: array<u32>;\nfn load_instance(i: u32) -> Instance {\nlet cap = arrayLength(&instances) / 3u;\nlet a = bitcast<vec4f>(instances[2u * i]);\nlet b = bitcast<vec4f>(instances[2u * i + 1u]);\nlet s = instances[2u * cap + i];\nreturn Instance(a.xyz - camera.origin.xyz, a.w, b, s.x, s.y, s.z, s.w);\n}\nconst SLOT: u32 = 128u;\nstruct LodParams {\ncount: u32,\nlod_scale: f32,\nerror_px: f32,\nmin_radius_px: f32,\n}\n@group(0) @binding(16) var<uniform> lodp: LodParams;\nfn error_px(inst: Instance, s: vec4f, e: f32) -> f32 {\nlet c = inst.pos + quat_rotate(inst.rot, s.xyz * inst.scale);\nlet d = max(length(c) - s.w * inst.scale, 1e-3);\nreturn e * inst.scale * lodp.lod_scale / d;\n}\nconst STREAM_MISSING: u32 = 1u;\nconst STREAM_LEAF: u32 = 2u;\nfn on_cut(inst: Instance, cl: Cluster) -> bool {\nif ((cl.stream & STREAM_MISSING) != 0u) {\nreturn false;\n}\nif (cl.self_error <= 0.0 && cl.parent_error == 0.0) {\nreturn true;\n}\nif ((cl.stream & STREAM_LEAF) == 0u && error_px(inst, cl.self_sphere, cl.self_error) > lodp.error_px) {\nreturn false;\n}\nreturn cl.parent_error < 0.0 || error_px(inst, cl.parent_sphere, cl.parent_error) > lodp.error_px;\n}\nfn job_entry(id: vec3u) -> vec4u {\nlet x = id.x;\nif (x >= params.total) {\nreturn vec4u(0u);\n}\nvar j = 0u;\nfor (; j < params.jobs; j++) {\nif (x < jobs[j].prefix + jobs[j].cluster_count) {\nbreak;\n}\n}\nif (j == params.jobs) {\nreturn vec4u(0u);\n}\nlet job = jobs[j];\nif (id.y >= args[job.args_slot * 5u + 1u]) {\nreturn vec4u(0u);\n}\nreturn vec4u(visible[job.start + id.y], job.cluster_base + (x - job.prefix), 1u, 0u);\n}\nfn sphere_in_view(inst: Instance, cl: Cluster) -> vec4f {\nif (!on_cut(inst, cl)) {\nreturn vec4f(0.0, 0.0, 0.0, -1.0);\n}\nlet c = inst.pos + quat_rotate(inst.rot, cl.center * inst.scale);\nlet r = cl.radius * inst.scale;\nfor (var p = 0u; p < 6u; p++) {\nlet plane = camera.frustum[p];\nif (dot(plane.xyz, c) + plane.w < -r) {\nreturn vec4f(0.0, 0.0, 0.0, -1.0);\n}\n}\nif (cl.flags == 0u && cl.cutoff < 1.0) {\nlet apex = inst.pos + quat_rotate(inst.rot, cl.apex * inst.scale);\nif (dot(normalize(apex), quat_rotate(inst.rot, cl.axis)) >= cl.cutoff) {\nreturn vec4f(0.0, 0.0, 0.0, -1.0);\n}\n}\nreturn vec4f(c, r);\n}\n@group(0) @binding(21) var<storage, read_write> page_bits: array<atomic<u32>>;\nfn note_pages(index: u32, cl: Cluster) {\nif (cl.stream == 0u && cl.kids == 0u) {\nreturn;\n}\nlet half = arrayLength(&page_bits) / 2u;\nlet page = cl.stream >> 2u;\natomicOr(&page_bits[half + page / 32u], 1u << (page % 32u));\nif ((cl.stream & STREAM_LEAF) != 0u && cl.kids > 0u && error_px(load_instance(index), cl.self_sphere, cl.self_error) > lodp.error_px) {\nlet want = cl.kids - 1u;\natomicOr(&page_bits[want / 32u], 1u << (want % 32u));\n}\n}\nfn emit(index: u32, ci: u32, cl: Cluster) {\nnote_pages(index, cl);\nlet side = cl.flags;\nlet k = atomicAdd(&out_args[10u + side], 1u);\nlet at = side * params.index_half + atomicAdd(&out_args[side * 5u], cl.tri_count * 3u);\nout_visible[side * params.half + k] = vec4u(index, ci, at, k);\n}\n@compute @workgroup_size(64) fn cull_clusters(@builtin(global_invocation_id) id: vec3u) {\nlet e = job_entry(id);\nif (e.z == 0u) {\nreturn;\n}\nlet cl = clusters[e.y];\nif (sphere_in_view(load_instance(e.x), cl).w >= 0.0) {\nemit(e.x, e.y, cl);\n}\n}\n@group(0) @binding(12) var<uniform> occl: Occlusion;\n@group(0) @binding(13) var hzb: texture_2d<f32>;\n@group(0) @binding(14) var<storage, read_write> recheck: array<atomic<u32>>;\n@group(0) @binding(15) var<storage, read_write> recheck_dispatch: array<u32, 3>;\n@compute @workgroup_size(64) fn cull_clusters_first(@builtin(global_invocation_id) id: vec3u) {\nlet e = job_entry(id);\nif (e.z == 0u) {\nreturn;\n}\nlet cl = clusters[e.y];\nlet s = sphere_in_view(load_instance(e.x), cl);\nif (s.w < 0.0) {\nreturn;\n}\nif (hidden_in(hzb, occl.hzb_size, occl.hzb_levels, s.xyz, s.w, occl.prev)) {\nlet k = atomicAdd(&recheck[0], 1u);\nif (2u * k + 2u < arrayLength(&recheck)) {\natomicStore(&recheck[2u * k + 1u], e.x);\natomicStore(&recheck[2u * k + 2u], e.y);\n}\nreturn;\n}\nemit(e.x, e.y, cl);\n}\n@compute @workgroup_size(64) fn cull_clusters_second(@builtin(global_invocation_id) id: vec3u) {\nlet e = job_entry(id);\nif (e.z == 0u) {\nreturn;\n}\nlet cl = clusters[e.y];\nlet s = sphere_in_view(load_instance(e.x), cl);\nif (s.w >= 0.0 && !hidden_in(hzb, occl.hzb_size, occl.hzb_levels, s.xyz, s.w, camera.view_proj)) {\nemit(e.x, e.y, cl);\n}\n}\n@compute @workgroup_size(1) fn size_recheck() {\nlet n = min(atomicLoad(&recheck[0]), (arrayLength(&recheck) - 1u) / 2u);\nrecheck_dispatch[0] = (n + 63u) / 64u;\nrecheck_dispatch[1] = 1u;\nrecheck_dispatch[2] = 1u;\n}\n@compute @workgroup_size(64) fn recheck_clusters(@builtin(global_invocation_id) id: vec3u) {\nlet n = min(atomicLoad(&recheck[0]), (arrayLength(&recheck) - 1u) / 2u);\nif (id.x >= n) {\nreturn;\n}\nlet index = atomicLoad(&recheck[2u * id.x + 1u]);\nlet ci = atomicLoad(&recheck[2u * id.x + 2u]);\nlet cl = clusters[ci];\nlet s = sphere_in_view(load_instance(index), cl);\nif (s.w >= 0.0 && !hidden_in(hzb, occl.hzb_size, occl.hzb_levels, s.xyz, s.w, camera.view_proj)) {\nemit(index, ci, cl);\n}\n}\nstruct ShadowCull {\nplanes: array<vec4f, 20>,\nlight: vec4f,\n}\n@group(0) @binding(17) var<uniform> shc: ShadowCull;\n@group(0) @binding(18) var<storage, read_write> sh_args: array<atomic<u32>, 32>;\n@group(0) @binding(19) var<storage, read_write> sh_visible: array<vec4u>;\n@compute @workgroup_size(64) fn cull_clusters_shadow(@builtin(global_invocation_id) id: vec3u) {\nlet e = job_entry(id);\nif (e.z == 0u) {\nreturn;\n}\nlet c = id.z;\nlet cl = clusters[e.y];\nlet inst = load_instance(e.x);\nif (!on_cut(inst, cl)) {\nreturn;\n}\nlet center = inst.pos + quat_rotate(inst.rot, cl.center * inst.scale);\nlet r = cl.radius * inst.scale;\nfor (var p = 0u; p < 5u; p++) {\nlet plane = shc.planes[c * 5u + p];\nif (dot(plane.xyz, center) + plane.w < -r) {\nreturn;\n}\n}\nif (cl.flags == 0u && cl.cutoff < 1.0 && dot(-shc.light.xyz, quat_rotate(inst.rot, cl.axis)) >= cl.cutoff) {\nreturn;\n}\nlet side = cl.flags;\nlet b = c * 2u + side;\nlet k = atomicAdd(&sh_args[b * 4u + 1u], 1u);\nsh_visible[c * params.index_half + side * params.half + k] = vec4u(e.x, e.y, 0u, 0u);\n}\n@group(0) @binding(11) var<storage, read_write> dispatch: array<u32, 3>;\n@compute @workgroup_size(1) fn size_expand() {\ndispatch[0] = atomicLoad(&out_args[10]) + atomicLoad(&out_args[11]);\ndispatch[1] = 1u;\ndispatch[2] = 1u;\n}\n@compute @workgroup_size(128) fn expand_clusters(@builtin(workgroup_id) wg: vec3u, @builtin(local_invocation_index) t: u32) {\nlet single = atomicLoad(&out_args[10]);\nlet g = wg.x;\nlet rec = out_visible[select(params.half + g - single, g, g < single)];\nlet cl = clusters[rec.y];\nif (t >= cl.tri_count) {\nreturn;\n}\nfor (var c = 0u; c < 3u; c++) {\nlet b = cl.tri_offset + t * 3u + c;\nout_indices[rec.z + t * 3u + c] = rec.w * SLOT + ((triangles[b / 4u] >> ((b % 4u) * 8u)) & 0xffu);\n}\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nstruct Instance {\npos: vec3f,\nscale: f32,\nrot: vec4f,\ncolor: u32,\nmesh: u32,\nmaterial: u32,\nskin: u32,\n}\nfn quat_rotate(q: vec4f, v: vec3f) -> vec3f {\nlet t = 2.0 * cross(q.xyz, v);\nreturn v + q.w * t + cross(q.xyz, t);\n}\nstruct Occlusion {\nprev: mat4x4f,\nfrustum: array<vec4f, 6>,\nhzb_size: vec2f,\nhzb_levels: u32,\nshadows: u32,\nslots: u32,\nregion: u32,\npad0: u32,\npad1: u32,\n}\nfn hidden_in(hzb: texture_2d<f32>, size: vec2f, levels: u32, c: vec3f, r: f32, view_proj: mat4x4f) -> bool {\nif (levels == 0u) {\nreturn false;\n}\nvar lo = vec2f(1e9);\nvar hi = vec2f(-1e9);\nvar nearest = 0.0;\nfor (var k = 0u; k < 8u; k++) {\nlet corner = c + r * vec3f(select(-1.0, 1.0, (k & 1u) != 0u), select(-1.0, 1.0, (k & 2u) != 0u), select(-1.0, 1.0, (k & 4u) != 0u));\nlet p = view_proj * vec4f(corner, 1.0);\nif (p.w <= 1e-4) {\nreturn false;\n}\nlet n = p.xyz / p.w;\nlo = min(lo, n.xy);\nhi = max(hi, n.xy);\nnearest = max(nearest, n.z);\n}\nif (any(hi < vec2f(-1.0)) || any(lo > vec2f(1.0))) {\nreturn false;\n}\nlet uv_lo = clamp(vec2f(lo.x, -hi.y) * 0.5 + 0.5, vec2f(0.0), vec2f(1.0));\nlet uv_hi = clamp(vec2f(hi.x, -lo.y) * 0.5 + 0.5, vec2f(0.0), vec2f(1.0));\nlet extent = (uv_hi - uv_lo) * size;\nlet level = min(u32(max(ceil(log2(max(max(extent.x, extent.y), 1.0))), 0.0)), levels - 1u);\nlet dims = vec2f(textureDimensions(hzb, level));\nlet t0 = vec2i(uv_lo * dims);\nlet t1 = min(vec2i(uv_hi * dims), vec2i(dims) - 1);\nvar farthest = 1.0;\nfor (var y = t0.y; y <= min(t1.y, t0.y + 2); y++) {\nfor (var x = t0.x; x <= min(t1.x, t0.x + 2); x++) {\nfarthest = min(farthest, textureLoad(hzb, vec2i(x, y), i32(level)).r);\n}\n}\nreturn nearest < farthest;\n}",
37
+ };
38
+ /** Linked from shaders/scene/occlusion_cull.wesl. */
39
+ export const sceneOcclusionCull = {
40
+ hash: 'd66ce30369f82685',
41
+ wgsl: "@group(0) @binding(7) var<uniform> occl: Occlusion;\n@group(0) @binding(8) var hzb: texture_2d<f32>;\n@group(0) @binding(9) var<storage, read_write> recheck: array<atomic<u32>>;\nfn hidden(c: vec3f, r: f32, view_proj: mat4x4f) -> bool {\nreturn hidden_in(hzb, occl.hzb_size, occl.hzb_levels, c, r, view_proj);\n}\n@compute @workgroup_size(64) fn cull_occlusion(@builtin(global_invocation_id) id: vec3u) {\nlet i = id.x;\nif (i >= params.count) {\nreturn;\n}\nlet inst = load_instance(i);\nlet m = meshes[inst.mesh];\nlet c = inst.pos + quat_rotate(inst.rot, m.center * inst.scale);\nlet r = m.radius * inst.scale;\nif (occl.shadows != 0u) {\nlet dst = place(inst);\nif (dst != 0xffffffffu) {\nvisible[dst] = i;\n}\n}\nfor (var p = 0u; p < 6u; p++) {\nlet plane = occl.frustum[p];\nif (dot(plane.xyz, c) + plane.w < -r) {\nreturn;\n}\n}\nif (hidden(c, r, occl.prev)) {\nlet k = atomicAdd(&recheck[0], 1u);\natomicStore(&recheck[k + 1u], i);\nreturn;\n}\nlet dst = place_lod(inst, c, r, occl.slots, occl.region);\nif (dst != 0xffffffffu) {\nvisible[dst] = i;\n}\n}\n@compute @workgroup_size(64) fn cull_recheck(@builtin(global_invocation_id) id: vec3u) {\nif (id.x >= atomicLoad(&recheck[0])) {\nreturn;\n}\nlet i = atomicLoad(&recheck[id.x + 1u]);\nlet inst = load_instance(i);\nlet m = meshes[inst.mesh];\nlet c = inst.pos + quat_rotate(inst.rot, m.center * inst.scale);\nlet r = m.radius * inst.scale;\nif (hidden(c, r, camera.view_proj)) {\nreturn;\n}\nlet dst = place_lod(inst, c, r, 2u * occl.slots, 2u * occl.region);\nif (dst != 0xffffffffu) {\nvisible[dst] = i;\n}\n}\nstruct Occlusion {\nprev: mat4x4f,\nfrustum: array<vec4f, 6>,\nhzb_size: vec2f,\nhzb_levels: u32,\nshadows: u32,\nslots: u32,\nregion: u32,\npad0: u32,\npad1: u32,\n}\nfn hidden_in(hzb: texture_2d<f32>, size: vec2f, levels: u32, c: vec3f, r: f32, view_proj: mat4x4f) -> bool {\nif (levels == 0u) {\nreturn false;\n}\nvar lo = vec2f(1e9);\nvar hi = vec2f(-1e9);\nvar nearest = 0.0;\nfor (var k = 0u; k < 8u; k++) {\nlet corner = c + r * vec3f(select(-1.0, 1.0, (k & 1u) != 0u), select(-1.0, 1.0, (k & 2u) != 0u), select(-1.0, 1.0, (k & 4u) != 0u));\nlet p = view_proj * vec4f(corner, 1.0);\nif (p.w <= 1e-4) {\nreturn false;\n}\nlet n = p.xyz / p.w;\nlo = min(lo, n.xy);\nhi = max(hi, n.xy);\nnearest = max(nearest, n.z);\n}\nif (any(hi < vec2f(-1.0)) || any(lo > vec2f(1.0))) {\nreturn false;\n}\nlet uv_lo = clamp(vec2f(lo.x, -hi.y) * 0.5 + 0.5, vec2f(0.0), vec2f(1.0));\nlet uv_hi = clamp(vec2f(hi.x, -lo.y) * 0.5 + 0.5, vec2f(0.0), vec2f(1.0));\nlet extent = (uv_hi - uv_lo) * size;\nlet level = min(u32(max(ceil(log2(max(max(extent.x, extent.y), 1.0))), 0.0)), levels - 1u);\nlet dims = vec2f(textureDimensions(hzb, level));\nlet t0 = vec2i(uv_lo * dims);\nlet t1 = min(vec2i(uv_hi * dims), vec2i(dims) - 1);\nvar farthest = 1.0;\nfor (var y = t0.y; y <= min(t1.y, t0.y + 2); y++) {\nfor (var x = t0.x; x <= min(t1.x, t0.x + 2); x++) {\nfarthest = min(farthest, textureLoad(hzb, vec2i(x, y), i32(level)).r);\n}\n}\nreturn nearest < farthest;\n}\n@group(0) @binding(4) var<uniform> params: CullParams;\nfn load_instance(i: u32) -> Instance {\nlet cap = arrayLength(&instances) / 3u;\nlet a = bitcast<vec4f>(instances[2u * i]);\nlet b = bitcast<vec4f>(instances[2u * i + 1u]);\nlet s = instances[2u * cap + i];\nreturn Instance(a.xyz - camera.origin.xyz, a.w, b, s.x, s.y, s.z, s.w);\n}\n@group(0) @binding(2) var<storage, read> meshes: array<MeshInfo>;\nfn quat_rotate(q: vec4f, v: vec3f) -> vec3f {\nlet t = 2.0 * cross(q.xyz, v);\nreturn v + q.w * t + cross(q.xyz, t);\n}\nfn place(inst: Instance) -> u32 {\nlet m = meshes[inst.mesh];\nlet c = inst.pos + quat_rotate(inst.rot, m.center * inst.scale);\nlet r = m.radius * inst.scale;\nfor (var p = 0u; p < 6u; p++) {\nlet plane = camera.frustum[p];\nif (dot(plane.xyz, c) + plane.w < -r) {\nreturn 0xffffffffu;\n}\n}\nreturn place_lod(inst, c, r, 0u, 0u);\n}\n@group(0) @binding(5) var<storage, read_write> visible: array<u32>;\nfn place_lod(inst: Instance, c: vec3f, r: f32, slots: u32, region: u32) -> u32 {\nlet m = meshes[inst.mesh];\nlet px = params.lod_scale / max(length(c) - r, 1e-3);\nif (r * px < params.min_radius_px) {\nreturn 0xffffffffu;\n}\nvar lod = 0u;\nfor (var l = 1u; l < m.lod_count; l++) {\nif (m.error[l / 4u][l % 4u] * inst.scale * px > params.error_px) {\nbreak;\n}\nlod = l;\n}\nlet k = atomicAdd(&args[m.draw_base + lod + slots].instance_count, 1u);\nreturn m.instance_base + lod * m.instance_count + k + region;\n}\nstruct Instance {\npos: vec3f,\nscale: f32,\nrot: vec4f,\ncolor: u32,\nmesh: u32,\nmaterial: u32,\nskin: u32,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\n@group(0) @binding(3) var<storage, read_write> args: array<DrawArgs>;\nstruct DrawArgs {\nindex_count: u32,\ninstance_count: atomic<u32>,\nfirst_index: u32,\nbase_vertex: i32,\nfirst_instance: u32,\n}\nstruct CullParams {\ncount: u32,\nlod_scale: f32,\nerror_px: f32,\nmin_radius_px: f32,\n}\n@group(0) @binding(1) var<storage, read> instances: array<vec4u>;\nstruct MeshInfo {\ncenter: vec3f,\nradius: f32,\nlod_count: u32,\ndraw_base: u32,\ninstance_base: u32,\ninstance_count: u32,\nerror: array<vec4f, 2>,\n}",
42
+ };
43
+ //# sourceMappingURL=post-shaders.gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-shaders.gen.js","sourceRoot":"","sources":["../src/post-shaders.gen.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AAEzE,2CAA2C;AAC3C,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,mrGAAmrG;CACjrG,CAAC;AAEX,0CAA0C;AAC1C,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,8xGAA8xG;CAC5xG,CAAC;AAEX,yCAAyC;AACzC,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,kmMAAkmM;CAChmM,CAAC;AAEX,8CAA8C;AAC9C,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,oiEAAoiE;CACliE,CAAC;AAEX,0CAA0C;AAC1C,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,qhCAAqhC;CACnhC,CAAC;AAEX,6CAA6C;AAC7C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,giIAAgiI;CAC9hI,CAAC;AAEX,+CAA+C;AAC/C,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,qwTAAqwT;CACnwT,CAAC;AAEX,qDAAqD;AACrD,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,yoKAAyoK;CACvoK,CAAC"}
package/dist/post.d.ts ADDED
@@ -0,0 +1,101 @@
1
+ import type { OrbitCamera } from './camera.ts';
2
+ import type { FrameGraph } from './frame-graph.ts';
3
+ import type { PipelineCache } from './pipeline-cache.ts';
4
+ import type { BloomOptions } from './post-options.ts';
5
+ import { type SceneRenderer } from './scene.ts';
6
+ export type { BloomOptions, GradingOptions, PostControls, PostOptions } from './post-options.ts';
7
+ export { gradingDefaults, whiteBalance } from './post-options.ts';
8
+ export { halton, invert4 } from './matrix.ts';
9
+ import type { PostPasses } from './post-options.ts';
10
+ /**
11
+ * The bloom chain: `levels` half-size downsamples of the HDR scene (the first Karis-weighted
12
+ * and thresholded), then upsamples back to half size, each adding the level below through a
13
+ * tent. The tone-mapping pass samples `output`.
14
+ */
15
+ export declare class Bloom {
16
+ #private;
17
+ readonly device: GPUDevice;
18
+ readonly levels: number;
19
+ intensity: number;
20
+ threshold: number;
21
+ knee: number;
22
+ radius: number;
23
+ constructor(device: GPUDevice, o: BloomOptions);
24
+ init(cache: PipelineCache): Promise<void>;
25
+ /** The chain's top level, which the tone-mapping pass reads. */
26
+ get output(): string;
27
+ /** Adds the chain's passes, reading `input` (the HDR scene). */
28
+ addPasses(graph: FrameGraph, input: string): void;
29
+ destroy(): void;
30
+ }
31
+ /** FXAA over the tone-mapped image (`input`, encoded) into `output`. */
32
+ export declare class Fxaa {
33
+ #private;
34
+ readonly device: GPUDevice;
35
+ constructor(device: GPUDevice, format: GPUTextureFormat);
36
+ init(cache: PipelineCache): Promise<void>;
37
+ addPass(graph: FrameGraph, input: string, output: string): void;
38
+ }
39
+ /**
40
+ * Temporal anti-aliasing: owns two history targets it ping-pongs (`post.taa`, imported into
41
+ * the graph), jitters the camera (`jitter`, applied by the scene to its view-projection), and
42
+ * reprojects by depth and the camera's move.
43
+ */
44
+ export declare class Taa {
45
+ #private;
46
+ readonly device: GPUDevice;
47
+ blend: number;
48
+ /**
49
+ * Sharpening after TAA (ADR-0119): 0 none to 1 strongest (live). Set before `init` to have the
50
+ * pass at all (with `motionBlur`); the history stays unsharpened, so it never accumulates.
51
+ */
52
+ sharpen: number;
53
+ /** Motion blur's shutter (ADR-0119): the share of each pixel's motion this frame it is smeared over, 0 to 1 (live; needs per-object motion). */
54
+ motionBlur: number;
55
+ /** Motion blur's taps (live). */
56
+ blurSamples: number;
57
+ /**
58
+ * What the motion target held on the next frame (ADR-0124; a diagnostic, reads back from the GPU):
59
+ * pixels drawn into it, those moving more than a twentieth of a pixel, the largest move in pixels, and
60
+ * the drawn pixels' mean view depth (last frame's).
61
+ * Null without per-object motion (the camera path).
62
+ *
63
+ * @example
64
+ * const m = await render.post.readMotion(); // { drawn: 18000, moving: 1200, maxPixels: 3.1, meanDepth: 7.4 }
65
+ */
66
+ readMotion(): Promise<{
67
+ drawn: number;
68
+ moving: number;
69
+ maxPixels: number;
70
+ meanDepth: number;
71
+ } | null>;
72
+ /** This frame's jitter in clip units (x, y), for the scene's view-projection. */
73
+ readonly jitter: Float32Array<ArrayBuffer>;
74
+ /** Temporal upscaling (ADR-0135): the history is the output's size, reconstructed from a smaller scene. */
75
+ readonly upscale: boolean;
76
+ constructor(device: GPUDevice, blend?: number, upscale?: boolean);
77
+ init(cache: PipelineCache): Promise<void>;
78
+ /** Draws per-object motion from `scene` (its GPU cull's storage path; elsewhere the camera's only). */
79
+ motionFrom(scene: SceneRenderer): void;
80
+ /** Whether per-object motion is drawn. */
81
+ get motion(): boolean;
82
+ /** This frame's camera offset from the records' anchor (ADR-0075), as the scene uploads it. */
83
+ setOrigin(origin: ArrayLike<number>): void;
84
+ /** Drops the history: the next frame starts over. */
85
+ reset(): void;
86
+ /**
87
+ * Before the camera upload, each frame: sizes the history to the output (the render target's size
88
+ * unless upscaling, ADR-0135), picks this frame's jitter (in the render target's pixels) and computes
89
+ * the reprojection from the camera (unjittered) and its eye.
90
+ */
91
+ prepare(camera: OrbitCamera, width: number, height: number, outputWidth?: number, outputHeight?: number): void;
92
+ /**
93
+ * Imports `post.taa` and adds the pass, reading `input` (the HDR scene) and `depth`; with
94
+ * sharpening or motion blur (ADR-0119), their pass after it. Returns the chain's next input.
95
+ */
96
+ addPass(graph: FrameGraph, input: string, depth: string): string;
97
+ get liveTextures(): number;
98
+ destroy(): void;
99
+ }
100
+ /** What `renderPlugin({ post: { passes } })` takes (ADR-0092). */
101
+ export declare const postPasses: PostPasses;