@certe/atmos-renderer 0.1.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 (204) hide show
  1. package/LICENCE +674 -0
  2. package/README.md +166 -0
  3. package/dist/bloom-pass.d.ts +29 -0
  4. package/dist/bloom-pass.d.ts.map +1 -0
  5. package/dist/bloom-pass.js +173 -0
  6. package/dist/bloom-pass.js.map +1 -0
  7. package/dist/bloom-shader.d.ts +9 -0
  8. package/dist/bloom-shader.d.ts.map +1 -0
  9. package/dist/bloom-shader.js +69 -0
  10. package/dist/bloom-shader.js.map +1 -0
  11. package/dist/bounds.d.ts +10 -0
  12. package/dist/bounds.d.ts.map +1 -0
  13. package/dist/bounds.js +37 -0
  14. package/dist/bounds.js.map +1 -0
  15. package/dist/camera.d.ts +31 -0
  16. package/dist/camera.d.ts.map +1 -0
  17. package/dist/camera.js +53 -0
  18. package/dist/camera.js.map +1 -0
  19. package/dist/depth-prepass.d.ts +24 -0
  20. package/dist/depth-prepass.d.ts.map +1 -0
  21. package/dist/depth-prepass.js +107 -0
  22. package/dist/depth-prepass.js.map +1 -0
  23. package/dist/directional-light.d.ts +23 -0
  24. package/dist/directional-light.d.ts.map +1 -0
  25. package/dist/directional-light.js +36 -0
  26. package/dist/directional-light.js.map +1 -0
  27. package/dist/frustum.d.ts +15 -0
  28. package/dist/frustum.d.ts.map +1 -0
  29. package/dist/frustum.js +51 -0
  30. package/dist/frustum.js.map +1 -0
  31. package/dist/fullscreen-quad.d.ts +8 -0
  32. package/dist/fullscreen-quad.d.ts.map +1 -0
  33. package/dist/fullscreen-quad.js +30 -0
  34. package/dist/fullscreen-quad.js.map +1 -0
  35. package/dist/geometry.d.ts +28 -0
  36. package/dist/geometry.d.ts.map +1 -0
  37. package/dist/geometry.js +245 -0
  38. package/dist/geometry.js.map +1 -0
  39. package/dist/grid-renderer.d.ts +10 -0
  40. package/dist/grid-renderer.d.ts.map +1 -0
  41. package/dist/grid-renderer.js +77 -0
  42. package/dist/grid-renderer.js.map +1 -0
  43. package/dist/grid-shader.d.ts +3 -0
  44. package/dist/grid-shader.d.ts.map +1 -0
  45. package/dist/grid-shader.js +89 -0
  46. package/dist/grid-shader.js.map +1 -0
  47. package/dist/index.d.ts +64 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +45 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/light.d.ts +59 -0
  52. package/dist/light.d.ts.map +1 -0
  53. package/dist/light.js +184 -0
  54. package/dist/light.js.map +1 -0
  55. package/dist/material-asset.d.ts +19 -0
  56. package/dist/material-asset.d.ts.map +1 -0
  57. package/dist/material-asset.js +30 -0
  58. package/dist/material-asset.js.map +1 -0
  59. package/dist/material.d.ts +50 -0
  60. package/dist/material.d.ts.map +1 -0
  61. package/dist/material.js +48 -0
  62. package/dist/material.js.map +1 -0
  63. package/dist/mesh-renderer.d.ts +43 -0
  64. package/dist/mesh-renderer.d.ts.map +1 -0
  65. package/dist/mesh-renderer.js +162 -0
  66. package/dist/mesh-renderer.js.map +1 -0
  67. package/dist/mesh.d.ts +16 -0
  68. package/dist/mesh.d.ts.map +1 -0
  69. package/dist/mesh.js +33 -0
  70. package/dist/mesh.js.map +1 -0
  71. package/dist/mipmap-generator.d.ts +7 -0
  72. package/dist/mipmap-generator.d.ts.map +1 -0
  73. package/dist/mipmap-generator.js +96 -0
  74. package/dist/mipmap-generator.js.map +1 -0
  75. package/dist/pbr-wgsl.d.ts +12 -0
  76. package/dist/pbr-wgsl.d.ts.map +1 -0
  77. package/dist/pbr-wgsl.js +159 -0
  78. package/dist/pbr-wgsl.js.map +1 -0
  79. package/dist/pipeline.d.ts +13 -0
  80. package/dist/pipeline.d.ts.map +1 -0
  81. package/dist/pipeline.js +77 -0
  82. package/dist/pipeline.js.map +1 -0
  83. package/dist/point-light.d.ts +16 -0
  84. package/dist/point-light.d.ts.map +1 -0
  85. package/dist/point-light.js +22 -0
  86. package/dist/point-light.js.map +1 -0
  87. package/dist/point-shadow-pass.d.ts +24 -0
  88. package/dist/point-shadow-pass.d.ts.map +1 -0
  89. package/dist/point-shadow-pass.js +192 -0
  90. package/dist/point-shadow-pass.js.map +1 -0
  91. package/dist/point-shadow-shader.d.ts +12 -0
  92. package/dist/point-shadow-shader.d.ts.map +1 -0
  93. package/dist/point-shadow-shader.js +52 -0
  94. package/dist/point-shadow-shader.js.map +1 -0
  95. package/dist/register-builtins.d.ts +2 -0
  96. package/dist/register-builtins.d.ts.map +1 -0
  97. package/dist/register-builtins.js +98 -0
  98. package/dist/register-builtins.js.map +1 -0
  99. package/dist/render-system.d.ts +99 -0
  100. package/dist/render-system.d.ts.map +1 -0
  101. package/dist/render-system.js +476 -0
  102. package/dist/render-system.js.map +1 -0
  103. package/dist/scene-depth.d.ts +36 -0
  104. package/dist/scene-depth.d.ts.map +1 -0
  105. package/dist/scene-depth.js +183 -0
  106. package/dist/scene-depth.js.map +1 -0
  107. package/dist/shader.d.ts +3 -0
  108. package/dist/shader.d.ts.map +1 -0
  109. package/dist/shader.js +144 -0
  110. package/dist/shader.js.map +1 -0
  111. package/dist/shadow-fragment-wgsl.d.ts +13 -0
  112. package/dist/shadow-fragment-wgsl.d.ts.map +1 -0
  113. package/dist/shadow-fragment-wgsl.js +205 -0
  114. package/dist/shadow-fragment-wgsl.js.map +1 -0
  115. package/dist/shadow-manager.d.ts +46 -0
  116. package/dist/shadow-manager.d.ts.map +1 -0
  117. package/dist/shadow-manager.js +259 -0
  118. package/dist/shadow-manager.js.map +1 -0
  119. package/dist/shadow-pass.d.ts +31 -0
  120. package/dist/shadow-pass.d.ts.map +1 -0
  121. package/dist/shadow-pass.js +135 -0
  122. package/dist/shadow-pass.js.map +1 -0
  123. package/dist/shadow-shader.d.ts +10 -0
  124. package/dist/shadow-shader.d.ts.map +1 -0
  125. package/dist/shadow-shader.js +24 -0
  126. package/dist/shadow-shader.js.map +1 -0
  127. package/dist/shadow-uniforms.d.ts +38 -0
  128. package/dist/shadow-uniforms.d.ts.map +1 -0
  129. package/dist/shadow-uniforms.js +97 -0
  130. package/dist/shadow-uniforms.js.map +1 -0
  131. package/dist/skinned-geometry.d.ts +14 -0
  132. package/dist/skinned-geometry.d.ts.map +1 -0
  133. package/dist/skinned-geometry.js +23 -0
  134. package/dist/skinned-geometry.js.map +1 -0
  135. package/dist/skinned-mesh-renderer.d.ts +54 -0
  136. package/dist/skinned-mesh-renderer.d.ts.map +1 -0
  137. package/dist/skinned-mesh-renderer.js +177 -0
  138. package/dist/skinned-mesh-renderer.js.map +1 -0
  139. package/dist/skinned-pipeline.d.ts +16 -0
  140. package/dist/skinned-pipeline.d.ts.map +1 -0
  141. package/dist/skinned-pipeline.js +112 -0
  142. package/dist/skinned-pipeline.js.map +1 -0
  143. package/dist/skinned-shader.d.ts +7 -0
  144. package/dist/skinned-shader.d.ts.map +1 -0
  145. package/dist/skinned-shader.js +52 -0
  146. package/dist/skinned-shader.js.map +1 -0
  147. package/dist/skinned-shadow-shader.d.ts +6 -0
  148. package/dist/skinned-shadow-shader.d.ts.map +1 -0
  149. package/dist/skinned-shadow-shader.js +31 -0
  150. package/dist/skinned-shadow-shader.js.map +1 -0
  151. package/dist/spot-light.d.ts +24 -0
  152. package/dist/spot-light.d.ts.map +1 -0
  153. package/dist/spot-light.js +41 -0
  154. package/dist/spot-light.js.map +1 -0
  155. package/dist/spot-shadow-pass.d.ts +36 -0
  156. package/dist/spot-shadow-pass.d.ts.map +1 -0
  157. package/dist/spot-shadow-pass.js +144 -0
  158. package/dist/spot-shadow-pass.js.map +1 -0
  159. package/dist/ssao-pass.d.ts +37 -0
  160. package/dist/ssao-pass.d.ts.map +1 -0
  161. package/dist/ssao-pass.js +208 -0
  162. package/dist/ssao-pass.js.map +1 -0
  163. package/dist/ssao-shader.d.ts +9 -0
  164. package/dist/ssao-shader.d.ts.map +1 -0
  165. package/dist/ssao-shader.js +120 -0
  166. package/dist/ssao-shader.js.map +1 -0
  167. package/dist/terrain-mesh-renderer.d.ts +39 -0
  168. package/dist/terrain-mesh-renderer.d.ts.map +1 -0
  169. package/dist/terrain-mesh-renderer.js +131 -0
  170. package/dist/terrain-mesh-renderer.js.map +1 -0
  171. package/dist/terrain-pipeline.d.ts +17 -0
  172. package/dist/terrain-pipeline.d.ts.map +1 -0
  173. package/dist/terrain-pipeline.js +70 -0
  174. package/dist/terrain-pipeline.js.map +1 -0
  175. package/dist/terrain-shader.d.ts +10 -0
  176. package/dist/terrain-shader.d.ts.map +1 -0
  177. package/dist/terrain-shader.js +154 -0
  178. package/dist/terrain-shader.js.map +1 -0
  179. package/dist/texture.d.ts +20 -0
  180. package/dist/texture.d.ts.map +1 -0
  181. package/dist/texture.js +87 -0
  182. package/dist/texture.js.map +1 -0
  183. package/dist/tonemap-pass.d.ts +22 -0
  184. package/dist/tonemap-pass.d.ts.map +1 -0
  185. package/dist/tonemap-pass.js +125 -0
  186. package/dist/tonemap-pass.js.map +1 -0
  187. package/dist/unlit-pipeline.d.ts +12 -0
  188. package/dist/unlit-pipeline.d.ts.map +1 -0
  189. package/dist/unlit-pipeline.js +59 -0
  190. package/dist/unlit-pipeline.js.map +1 -0
  191. package/dist/unlit-shader.d.ts +3 -0
  192. package/dist/unlit-shader.d.ts.map +1 -0
  193. package/dist/unlit-shader.js +33 -0
  194. package/dist/unlit-shader.js.map +1 -0
  195. package/dist/webgpu-device.d.ts +13 -0
  196. package/dist/webgpu-device.d.ts.map +1 -0
  197. package/dist/webgpu-device.js +70 -0
  198. package/dist/webgpu-device.js.map +1 -0
  199. package/dist/wireframe-pipeline.d.ts +7 -0
  200. package/dist/wireframe-pipeline.d.ts.map +1 -0
  201. package/dist/wireframe-pipeline.js +72 -0
  202. package/dist/wireframe-pipeline.js.map +1 -0
  203. package/package.json +28 -0
  204. package/src/index.ts +87 -0
package/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # 🎨 @certe/atmos-renderer
2
+
3
+ WebGPU-first rendering package for the Atmos Engine. Handles GPU initialization, PBR shading, multi-light systems, cascaded shadow maps, skeletal animation (GPU skinning), terrain rendering, and a full HDR post-processing pipeline.
4
+
5
+ ---
6
+
7
+ ## 🔑 Key Features
8
+
9
+ - **PBR Cook-Torrance** shading with albedo, metallic, roughness, normal, and emissive maps
10
+ - **3 light types** — Directional, Point, Spot (up to 4 each)
11
+ - **Cascaded Shadow Maps** (2-cascade CSM) + point cube shadows + spot shadows
12
+ - **HDR pipeline** — MSAA 4× → HDR resolve → Bloom → SSAO → Tonemapping (ACES)
13
+ - **GPU skinning** — separate skinned pipeline with bone matrix storage buffer
14
+ - **Terrain pipeline** — splat-textured terrain with 3-layer blending
15
+ - **Geometry primitives** — cube, sphere, cylinder, plane
16
+
17
+ ---
18
+
19
+ ## 🚀 Quick Start
20
+
21
+ ```ts
22
+ import { initWebGPU, createRenderPipeline, createMesh, createMaterial,
23
+ RenderSystem, Camera, MeshRenderer, createCubeGeometry } from '@certe/atmos-renderer';
24
+
25
+ const gpu = await initWebGPU(canvas);
26
+ const pipeline = createRenderPipeline(gpu);
27
+
28
+ // Create a mesh + material
29
+ const mesh = createMesh(gpu.device, createCubeGeometry());
30
+ const material = createMaterial(gpu.device, pipeline, {
31
+ albedo: [0.8, 0.2, 0.2, 1],
32
+ metallic: 0.0,
33
+ roughness: 0.5,
34
+ });
35
+
36
+ // Attach to a GameObject
37
+ const cube = new GameObject('Cube');
38
+ const mr = cube.addComponent(MeshRenderer);
39
+ mr.init({ mesh, material, device: gpu.device, pipelineResources: pipeline });
40
+
41
+ // Render
42
+ const renderSystem = new RenderSystem(gpu, pipeline, scene);
43
+ ```
44
+
45
+ ---
46
+
47
+ ## 📖 API Overview
48
+
49
+ ### Initialization
50
+
51
+ | Function | Description |
52
+ |---|---|
53
+ | `initWebGPU(canvas)` | Request adapter + device, configure canvas |
54
+ | `resizeGPU(gpu)` | Sync canvas pixel size, recreate depth/MSAA/HDR textures |
55
+ | `createRenderPipeline(gpu)` | Create the PBR render pipeline + bind group layouts |
56
+
57
+ ### Geometry & Mesh
58
+
59
+ | Function | Description |
60
+ |---|---|
61
+ | `createCubeGeometry()` | 32B/vertex (pos+normal+uv) |
62
+ | `createSphereGeometry(seg?)` | Latitude-longitude sphere |
63
+ | `createCylinderGeometry(seg?)` | With top/bottom caps |
64
+ | `createPlaneGeometry(size?)` | XZ plane |
65
+ | `createMesh(device, geo)` | Upload to GPU, retains CPU data for picking |
66
+
67
+ ### Materials
68
+
69
+ ```ts
70
+ const mat = createMaterial(device, pipeline, {
71
+ albedo: [1, 1, 1, 1],
72
+ metallic: 0.5,
73
+ roughness: 0.3,
74
+ emissive: [1, 0.5, 0],
75
+ emissiveIntensity: 2.0,
76
+ });
77
+ // Textures: mat.albedoTexture, mat.normalTexture, mat.metallicRoughnessTexture
78
+ ```
79
+
80
+ ### Lights
81
+
82
+ ```ts
83
+ // Add as components to GameObjects
84
+ const light = obj.addComponent(DirectionalLight);
85
+ light.color = new Float32Array([1, 1, 1]);
86
+ light.intensity = 1.5;
87
+ light.castShadows = true;
88
+
89
+ obj.addComponent(PointLight); // color, intensity, range
90
+ obj.addComponent(SpotLight); // + innerAngle, outerAngle
91
+ ```
92
+
93
+ ### Camera
94
+
95
+ ```ts
96
+ const cam = obj.addComponent(Camera);
97
+ cam.fov = 60;
98
+ cam.near = 0.1;
99
+ cam.far = 1000;
100
+
101
+ // Screen-to-world unprojection
102
+ const worldPos = Camera.screenToWorldPoint(sx, sy, depth, camera, canvas);
103
+ ```
104
+
105
+ ### Post-Processing
106
+
107
+ The RenderSystem exposes controls for the full post-fx chain:
108
+
109
+ | Property | Default | Description |
110
+ |---|---|---|
111
+ | `exposure` | `1.0` | Pre-tonemap multiplier |
112
+ | `bloomIntensity` | `0.04` | Bloom strength |
113
+ | `bloomThreshold` | `1.0` | HDR threshold for bloom |
114
+ | `ssaoEnabled` | `false` | Toggle SSAO |
115
+ | `ssaoRadius` / `ssaoBias` / `ssaoIntensity` | — | SSAO tuning |
116
+ | `vignetteIntensity` / `vignetteRadius` | — | Screen-edge darkening |
117
+
118
+ ### Render Pipeline Flow
119
+
120
+ ```
121
+ Depth Pre-pass
122
+ Shadow Passes (CSM × 2 + Point + Spot)
123
+ Main Pass (MSAA 4× → HDR resolve)
124
+ SSAO (half-res, 16 samples)
125
+ Bloom (5-level downsample/upsample)
126
+ Tonemap (ACES + gamma 2.2 + vignette) → Swapchain
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 📁 Structure
132
+
133
+ ```
134
+ packages/renderer/src/
135
+ index.ts # Public API
136
+ pipeline.ts # PBR pipeline, constants (HDR_FORMAT, MSAA_SAMPLE_COUNT)
137
+ shader.ts # PBR WGSL vertex + fragment shaders
138
+ material.ts # Material creation + uniform writes
139
+ camera.ts # Camera component
140
+ render-system.ts # Frame orchestration
141
+ directional-light.ts # DirectionalLight component
142
+ point-light.ts # PointLight component
143
+ spot-light.ts # SpotLight component
144
+ geometry.ts # Primitive factories
145
+ mesh.ts # GPU mesh creation
146
+ mesh-renderer.ts # MeshRenderer component
147
+ skinned-pipeline.ts # GPU skinning pipeline
148
+ skinned-mesh-renderer.ts # SkinnedMeshRenderer component
149
+ bloom-pass.ts # 5-level bloom
150
+ tonemap-pass.ts # ACES tonemapping + vignette
151
+ ssao-pass.ts # Screen-space ambient occlusion
152
+ depth-prepass.ts # Non-MSAA depth for SSAO
153
+ directional-shadow-pass.ts # Cascaded shadow maps
154
+ point-shadow-pass.ts # Cube shadow maps
155
+ spot-shadow-pass.ts # Spot light shadows
156
+ texture.ts # Texture creation + decoding
157
+ mipmap-generator.ts # Blit-based mipmap generation
158
+ grid-renderer.ts # Infinite XZ grid overlay
159
+ ```
160
+
161
+ ---
162
+
163
+ ## 🔗 Dependencies
164
+
165
+ - `@certe/atmos-core` — Component, GameObject, Transform, Scene
166
+ - `@certe/atmos-math` — Vec3, Mat4, Quat for camera/light math
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Bloom post-processing pass.
3
+ * Downsample chain: HDR → 1/2 → 1/4 → 1/8 → 1/16 (threshold on first pass).
4
+ * Upsample chain: 1/16 → 1/8 → 1/4 → 1/2 → full (additive blend).
5
+ * Output: full-res bloom texture.
6
+ */
7
+ export declare class BloomPass {
8
+ threshold: number;
9
+ intensity: number;
10
+ radius: number;
11
+ private readonly _device;
12
+ private readonly _downPipeline;
13
+ private readonly _upPipeline;
14
+ private readonly _sampler;
15
+ private readonly _downBGL;
16
+ private readonly _upBGL;
17
+ private readonly _paramsBuffer;
18
+ private readonly _paramsData;
19
+ private _mipTextures;
20
+ private _mipViews;
21
+ private _width;
22
+ private _height;
23
+ constructor(device: GPUDevice);
24
+ private _ensureMips;
25
+ /** Execute bloom and return the bloom texture view to be composited with HDR scene. */
26
+ execute(encoder: GPUCommandEncoder, hdrTexture: GPUTexture): GPUTextureView;
27
+ destroy(): void;
28
+ }
29
+ //# sourceMappingURL=bloom-pass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bloom-pass.d.ts","sourceRoot":"","sources":["../src/bloom-pass.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,qBAAa,SAAS;IACpB,SAAS,SAAO;IAChB,SAAS,SAAO;IAChB,MAAM,SAAO;IAEb,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAY;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAY;IAE1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;IAEnD,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAK;gBAER,MAAM,EAAE,SAAS;IAyD7B,OAAO,CAAC,WAAW;IAwBnB,uFAAuF;IACvF,OAAO,CAAC,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,UAAU,GAAG,cAAc;IA4E3E,OAAO,IAAI,IAAI;CAIhB"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Bloom post-processing pass.
3
+ * Downsample chain: HDR → 1/2 → 1/4 → 1/8 → 1/16 (threshold on first pass).
4
+ * Upsample chain: 1/16 → 1/8 → 1/4 → 1/2 → full (additive blend).
5
+ * Output: full-res bloom texture.
6
+ */
7
+ import { BLOOM_DOWNSAMPLE_SHADER, BLOOM_UPSAMPLE_SHADER } from './bloom-shader.js';
8
+ import { drawFullscreenTriangle } from './fullscreen-quad.js';
9
+ const HDR_FORMAT = 'rgba16float';
10
+ const MIP_COUNT = 5;
11
+ export class BloomPass {
12
+ threshold = 1.0;
13
+ intensity = 0.5;
14
+ radius = 0.5;
15
+ _device;
16
+ _downPipeline;
17
+ _upPipeline;
18
+ _sampler;
19
+ _downBGL;
20
+ _upBGL;
21
+ _paramsBuffer;
22
+ _paramsData = new Float32Array(4);
23
+ _mipTextures = [];
24
+ _mipViews = [];
25
+ _width = 0;
26
+ _height = 0;
27
+ constructor(device) {
28
+ this._device = device;
29
+ this._sampler = device.createSampler({
30
+ minFilter: 'linear',
31
+ magFilter: 'linear',
32
+ });
33
+ this._paramsBuffer = device.createBuffer({
34
+ size: 16,
35
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
36
+ });
37
+ // Bind group layouts (identical for both: texture + sampler + params)
38
+ this._downBGL = device.createBindGroupLayout({
39
+ entries: [
40
+ { binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
41
+ { binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
42
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
43
+ ],
44
+ });
45
+ this._upBGL = device.createBindGroupLayout({
46
+ entries: [
47
+ { binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
48
+ { binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
49
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
50
+ ],
51
+ });
52
+ const downModule = device.createShaderModule({ code: BLOOM_DOWNSAMPLE_SHADER });
53
+ const upModule = device.createShaderModule({ code: BLOOM_UPSAMPLE_SHADER });
54
+ this._downPipeline = device.createRenderPipeline({
55
+ layout: device.createPipelineLayout({ bindGroupLayouts: [this._downBGL] }),
56
+ vertex: { module: downModule, entryPoint: 'vs' },
57
+ fragment: { module: downModule, entryPoint: 'fs', targets: [{ format: HDR_FORMAT }] },
58
+ primitive: { topology: 'triangle-list' },
59
+ });
60
+ this._upPipeline = device.createRenderPipeline({
61
+ layout: device.createPipelineLayout({ bindGroupLayouts: [this._upBGL] }),
62
+ vertex: { module: upModule, entryPoint: 'vs' },
63
+ fragment: {
64
+ module: upModule,
65
+ entryPoint: 'fs',
66
+ targets: [{
67
+ format: HDR_FORMAT,
68
+ blend: {
69
+ color: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
70
+ alpha: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
71
+ },
72
+ }],
73
+ },
74
+ primitive: { topology: 'triangle-list' },
75
+ });
76
+ }
77
+ _ensureMips(w, h) {
78
+ if (this._width === w && this._height === h)
79
+ return;
80
+ this._width = w;
81
+ this._height = h;
82
+ for (const tex of this._mipTextures)
83
+ tex.destroy();
84
+ this._mipTextures = [];
85
+ this._mipViews = [];
86
+ let mw = Math.max(1, Math.floor(w / 2));
87
+ let mh = Math.max(1, Math.floor(h / 2));
88
+ for (let i = 0; i < MIP_COUNT; i++) {
89
+ const tex = this._device.createTexture({
90
+ size: { width: mw, height: mh },
91
+ format: HDR_FORMAT,
92
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
93
+ });
94
+ this._mipTextures.push(tex);
95
+ this._mipViews.push(tex.createView());
96
+ mw = Math.max(1, Math.floor(mw / 2));
97
+ mh = Math.max(1, Math.floor(mh / 2));
98
+ }
99
+ }
100
+ /** Execute bloom and return the bloom texture view to be composited with HDR scene. */
101
+ execute(encoder, hdrTexture) {
102
+ const w = hdrTexture.width;
103
+ const h = hdrTexture.height;
104
+ this._ensureMips(w, h);
105
+ const hdrView = hdrTexture.createView();
106
+ // --- Downsample chain ---
107
+ for (let i = 0; i < MIP_COUNT; i++) {
108
+ const srcView = i === 0 ? hdrView : this._mipViews[i - 1];
109
+ const isFirst = i === 0 ? 1.0 : 0.0;
110
+ this._paramsData[0] = this.threshold;
111
+ this._paramsData[1] = isFirst;
112
+ this._paramsData[2] = 0;
113
+ this._paramsData[3] = 0;
114
+ this._device.queue.writeBuffer(this._paramsBuffer, 0, this._paramsData);
115
+ const bg = this._device.createBindGroup({
116
+ layout: this._downBGL,
117
+ entries: [
118
+ { binding: 0, resource: srcView },
119
+ { binding: 1, resource: this._sampler },
120
+ { binding: 2, resource: { buffer: this._paramsBuffer } },
121
+ ],
122
+ });
123
+ const pass = encoder.beginRenderPass({
124
+ colorAttachments: [{
125
+ view: this._mipViews[i],
126
+ loadOp: 'clear',
127
+ storeOp: 'store',
128
+ clearValue: { r: 0, g: 0, b: 0, a: 0 },
129
+ }],
130
+ });
131
+ pass.setPipeline(this._downPipeline);
132
+ pass.setBindGroup(0, bg);
133
+ drawFullscreenTriangle(pass);
134
+ pass.end();
135
+ }
136
+ // --- Upsample chain (additive blend) ---
137
+ for (let i = MIP_COUNT - 2; i >= 0; i--) {
138
+ const srcView = this._mipViews[i + 1];
139
+ this._paramsData[0] = this.radius;
140
+ this._paramsData[1] = 0;
141
+ this._paramsData[2] = 0;
142
+ this._paramsData[3] = 0;
143
+ this._device.queue.writeBuffer(this._paramsBuffer, 0, this._paramsData);
144
+ const bg = this._device.createBindGroup({
145
+ layout: this._upBGL,
146
+ entries: [
147
+ { binding: 0, resource: srcView },
148
+ { binding: 1, resource: this._sampler },
149
+ { binding: 2, resource: { buffer: this._paramsBuffer } },
150
+ ],
151
+ });
152
+ const pass = encoder.beginRenderPass({
153
+ colorAttachments: [{
154
+ view: this._mipViews[i],
155
+ loadOp: 'load',
156
+ storeOp: 'store',
157
+ }],
158
+ });
159
+ pass.setPipeline(this._upPipeline);
160
+ pass.setBindGroup(0, bg);
161
+ drawFullscreenTriangle(pass);
162
+ pass.end();
163
+ }
164
+ // Return the largest bloom mip (half-res)
165
+ return this._mipViews[0];
166
+ }
167
+ destroy() {
168
+ for (const tex of this._mipTextures)
169
+ tex.destroy();
170
+ this._paramsBuffer.destroy();
171
+ }
172
+ }
173
+ //# sourceMappingURL=bloom-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bloom-pass.js","sourceRoot":"","sources":["../src/bloom-pass.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,UAAU,GAAqB,aAAa,CAAC;AACnD,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,MAAM,OAAO,SAAS;IACpB,SAAS,GAAG,GAAG,CAAC;IAChB,SAAS,GAAG,GAAG,CAAC;IAChB,MAAM,GAAG,GAAG,CAAC;IAEI,OAAO,CAAY;IACnB,aAAa,CAAoB;IACjC,WAAW,CAAoB;IAC/B,QAAQ,CAAa;IACrB,QAAQ,CAAqB;IAC7B,MAAM,CAAqB;IAC3B,aAAa,CAAY;IAEzB,WAAW,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;IAE3C,YAAY,GAAiB,EAAE,CAAC;IAChC,SAAS,GAAqB,EAAE,CAAC;IACjC,MAAM,GAAG,CAAC,CAAC;IACX,OAAO,GAAG,CAAC,CAAC;IAEpB,YAAY,MAAiB;QAC3B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QAEtB,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC;YACnC,SAAS,EAAE,QAAQ;YACnB,SAAS,EAAE,QAAQ;SACpB,CAAC,CAAC;QAEH,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,YAAY,CAAC;YACvC,IAAI,EAAE,EAAE;YACR,KAAK,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ;SACxD,CAAC,CAAC;QAEH,sEAAsE;QACtE,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,qBAAqB,CAAC;YAC3C,OAAO,EAAE;gBACP,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE;gBACrF,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;gBACnF,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;aACjF;SACF,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,qBAAqB,CAAC;YACzC,OAAO,EAAE;gBACP,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE;gBACrF,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;gBACnF,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;aACjF;SACF,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,MAAM,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAChF,MAAM,QAAQ,GAAG,MAAM,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAE5E,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,oBAAoB,CAAC;YAC/C,MAAM,EAAE,MAAM,CAAC,oBAAoB,CAAC,EAAE,gBAAgB,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1E,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE;YAChD,QAAQ,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE;YACrF,SAAS,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE;SACzC,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,oBAAoB,CAAC;YAC7C,MAAM,EAAE,MAAM,CAAC,oBAAoB,CAAC,EAAE,gBAAgB,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACxE,MAAM,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE;YAC9C,QAAQ,EAAE;gBACR,MAAM,EAAE,QAAQ;gBAChB,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC;wBACR,MAAM,EAAE,UAAU;wBAClB,KAAK,EAAE;4BACL,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE;4BAC/D,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE;yBAChE;qBACF,CAAC;aACH;YACD,SAAS,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE;SACzC,CAAC,CAAC;IACL,CAAC;IAEO,WAAW,CAAC,CAAS,EAAE,CAAS;QACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC;YAAE,OAAO;QACpD,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QAEjB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,YAAY;YAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QACnD,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QAEpB,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;gBACrC,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;gBAC/B,MAAM,EAAE,UAAU;gBAClB,KAAK,EAAE,eAAe,CAAC,eAAe,GAAG,eAAe,CAAC,iBAAiB;aAC3E,CAAC,CAAC;YACH,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;YACtC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;YACrC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,uFAAuF;IACvF,OAAO,CAAC,OAA0B,EAAE,UAAsB;QACxD,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC;QAC3B,MAAM,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAEvB,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,EAAE,CAAC;QAExC,2BAA2B;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;YAC3D,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YAEpC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;YACrC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,EAAE,IAAI,CAAC,WAAyC,CAAC,CAAC;YAEtG,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;gBACtC,MAAM,EAAE,IAAI,CAAC,QAAQ;gBACrB,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE;oBACjC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;oBACvC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE;iBACzD;aACF,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC;gBACnC,gBAAgB,EAAE,CAAC;wBACjB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAE;wBACxB,MAAM,EAAE,OAAO;wBACf,OAAO,EAAE,OAAO;wBAChB,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;qBACvC,CAAC;aACH,CAAC,CAAC;YACH,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACrC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,sBAAsB,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,CAAC;QAED,0CAA0C;QAC1C,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;YACvC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;YAClC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,EAAE,IAAI,CAAC,WAAyC,CAAC,CAAC;YAEtG,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;gBACtC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE;oBACjC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;oBACvC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE;iBACzD;aACF,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC;gBACnC,gBAAgB,EAAE,CAAC;wBACjB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAE;wBACxB,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE,OAAO;qBACjB,CAAC;aACH,CAAC,CAAC;YACH,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACnC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,sBAAsB,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,CAAC;QAED,0CAA0C;QAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC;IAC5B,CAAC;IAED,OAAO;QACL,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,YAAY;YAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QACnD,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * WGSL shaders for bloom post-processing.
3
+ * Uses shared fullscreen triangle vertex shader.
4
+ * Downsample applies a bright threshold on the first pass.
5
+ * Upsample blends additively with the previous mip level.
6
+ */
7
+ export declare const BLOOM_DOWNSAMPLE_SHADER: string;
8
+ export declare const BLOOM_UPSAMPLE_SHADER: string;
9
+ //# sourceMappingURL=bloom-shader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bloom-shader.d.ts","sourceRoot":"","sources":["../src/bloom-shader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,eAAO,MAAM,uBAAuB,QAoCnC,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAuBjC,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * WGSL shaders for bloom post-processing.
3
+ * Uses shared fullscreen triangle vertex shader.
4
+ * Downsample applies a bright threshold on the first pass.
5
+ * Upsample blends additively with the previous mip level.
6
+ */
7
+ import { FULLSCREEN_VERTEX_SHADER } from './fullscreen-quad.js';
8
+ export const BLOOM_DOWNSAMPLE_SHADER = FULLSCREEN_VERTEX_SHADER + /* wgsl */ `
9
+ @group(0) @binding(0) var srcTexture: texture_2d<f32>;
10
+ @group(0) @binding(1) var srcSampler: sampler;
11
+ @group(0) @binding(2) var<uniform> params: vec4<f32>; // x=threshold, y=isFirstPass
12
+
13
+ @fragment
14
+ fn fs(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
15
+ let dims = vec2<f32>(textureDimensions(srcTexture, 0));
16
+ let texelSize = 1.0 / dims;
17
+
18
+ // 13-tap downsampling filter (anti-firefly, from Call of Duty presentation)
19
+ var color = textureSample(srcTexture, srcSampler, uv) * 0.125;
20
+ color += textureSample(srcTexture, srcSampler, uv + vec2(-1.0, -1.0) * texelSize) * 0.03125;
21
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 1.0, -1.0) * texelSize) * 0.03125;
22
+ color += textureSample(srcTexture, srcSampler, uv + vec2(-1.0, 1.0) * texelSize) * 0.03125;
23
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 1.0, 1.0) * texelSize) * 0.03125;
24
+
25
+ color += textureSample(srcTexture, srcSampler, uv + vec2(-2.0, 0.0) * texelSize) * 0.0625;
26
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 2.0, 0.0) * texelSize) * 0.0625;
27
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 0.0, -2.0) * texelSize) * 0.0625;
28
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 0.0, 2.0) * texelSize) * 0.0625;
29
+
30
+ color += textureSample(srcTexture, srcSampler, uv + vec2(-2.0, -2.0) * texelSize) * 0.03125;
31
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 2.0, -2.0) * texelSize) * 0.03125;
32
+ color += textureSample(srcTexture, srcSampler, uv + vec2(-2.0, 2.0) * texelSize) * 0.03125;
33
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 2.0, 2.0) * texelSize) * 0.03125;
34
+
35
+ // Apply threshold on first pass only
36
+ if (params.y > 0.5) {
37
+ let brightness = max(color.r, max(color.g, color.b));
38
+ let contribution = max(brightness - params.x, 0.0) / max(brightness, 0.0001);
39
+ color = vec4(color.rgb * contribution, color.a);
40
+ }
41
+
42
+ return color;
43
+ }
44
+ `;
45
+ export const BLOOM_UPSAMPLE_SHADER = FULLSCREEN_VERTEX_SHADER + /* wgsl */ `
46
+ @group(0) @binding(0) var srcTexture: texture_2d<f32>;
47
+ @group(0) @binding(1) var srcSampler: sampler;
48
+ @group(0) @binding(2) var<uniform> params: vec4<f32>; // x=radius
49
+
50
+ @fragment
51
+ fn fs(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
52
+ let dims = vec2<f32>(textureDimensions(srcTexture, 0));
53
+ let texelSize = params.x / dims;
54
+
55
+ // 9-tap tent filter for smooth upsampling
56
+ var color = textureSample(srcTexture, srcSampler, uv + vec2(-1.0, -1.0) * texelSize);
57
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 0.0, -1.0) * texelSize) * 2.0;
58
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 1.0, -1.0) * texelSize);
59
+ color += textureSample(srcTexture, srcSampler, uv + vec2(-1.0, 0.0) * texelSize) * 2.0;
60
+ color += textureSample(srcTexture, srcSampler, uv) * 4.0;
61
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 1.0, 0.0) * texelSize) * 2.0;
62
+ color += textureSample(srcTexture, srcSampler, uv + vec2(-1.0, 1.0) * texelSize);
63
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 0.0, 1.0) * texelSize) * 2.0;
64
+ color += textureSample(srcTexture, srcSampler, uv + vec2( 1.0, 1.0) * texelSize);
65
+
66
+ return color / 16.0;
67
+ }
68
+ `;
69
+ //# sourceMappingURL=bloom-shader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bloom-shader.js","sourceRoot":"","sources":["../src/bloom-shader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,MAAM,CAAC,MAAM,uBAAuB,GAAG,wBAAwB,GAAG,UAAU,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoC3E,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,wBAAwB,GAAG,UAAU,CAAA;;;;;;;;;;;;;;;;;;;;;;;CAuBzE,CAAC"}
@@ -0,0 +1,10 @@
1
+ export interface BoundingSphere {
2
+ center: Float32Array;
3
+ radius: number;
4
+ }
5
+ /**
6
+ * Compute a bounding sphere from interleaved vertex data.
7
+ * Reads position from the first 3 floats of each vertex.
8
+ */
9
+ export declare function computeBoundingSphere(vertices: Float32Array, stride: number): BoundingSphere;
10
+ //# sourceMappingURL=bounds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounds.d.ts","sourceRoot":"","sources":["../src/bounds.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,MAAM,GACb,cAAc,CAiChB"}
package/dist/bounds.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Compute a bounding sphere from interleaved vertex data.
3
+ * Reads position from the first 3 floats of each vertex.
4
+ */
5
+ export function computeBoundingSphere(vertices, stride) {
6
+ const vertCount = Math.floor(vertices.length / stride);
7
+ if (vertCount === 0) {
8
+ return { center: new Float32Array(3), radius: 0 };
9
+ }
10
+ // Compute centroid
11
+ let cx = 0, cy = 0, cz = 0;
12
+ for (let i = 0; i < vertCount; i++) {
13
+ const o = i * stride;
14
+ cx += vertices[o];
15
+ cy += vertices[o + 1];
16
+ cz += vertices[o + 2];
17
+ }
18
+ cx /= vertCount;
19
+ cy /= vertCount;
20
+ cz /= vertCount;
21
+ // Find max distance from centroid
22
+ let maxDist2 = 0;
23
+ for (let i = 0; i < vertCount; i++) {
24
+ const o = i * stride;
25
+ const dx = vertices[o] - cx;
26
+ const dy = vertices[o + 1] - cy;
27
+ const dz = vertices[o + 2] - cz;
28
+ const dist2 = dx * dx + dy * dy + dz * dz;
29
+ if (dist2 > maxDist2)
30
+ maxDist2 = dist2;
31
+ }
32
+ return {
33
+ center: new Float32Array([cx, cy, cz]),
34
+ radius: Math.sqrt(maxDist2),
35
+ };
36
+ }
37
+ //# sourceMappingURL=bounds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounds.js","sourceRoot":"","sources":["../src/bounds.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAsB,EACtB,MAAc;IAEd,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACvD,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,MAAM,EAAE,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACpD,CAAC;IAED,mBAAmB;IACnB,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACrB,EAAE,IAAI,QAAQ,CAAC,CAAC,CAAE,CAAC;QACnB,EAAE,IAAI,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;QACvB,EAAE,IAAI,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;IACzB,CAAC;IACD,EAAE,IAAI,SAAS,CAAC;IAChB,EAAE,IAAI,SAAS,CAAC;IAChB,EAAE,IAAI,SAAS,CAAC;IAEhB,kCAAkC;IAClC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACrB,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAE,GAAG,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAE,GAAG,EAAE,CAAC;QACjC,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAE,GAAG,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QAC1C,IAAI,KAAK,GAAG,QAAQ;YAAE,QAAQ,GAAG,KAAK,CAAC;IACzC,CAAC;IAED,OAAO;QACL,MAAM,EAAE,IAAI,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACtC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;KAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { Component, Scene } from '@certe/atmos-core';
2
+ import type { Mat4Type } from '@certe/atmos-math';
3
+ /** Loose interface to avoid circular import with RenderSystem. */
4
+ export interface ScreenToWorldProvider {
5
+ screenToWorldPoint(x: number, y: number, nearClip?: number): Promise<Float32Array | null>;
6
+ }
7
+ export declare class Camera extends Component {
8
+ fovY: number;
9
+ near: number;
10
+ far: number;
11
+ isMainCamera: boolean;
12
+ clearColor: Float32Array<ArrayBuffer>;
13
+ /** Set by RenderSystem when it activates a camera. */
14
+ static _renderSystem: ScreenToWorldProvider | null;
15
+ private readonly _viewMatrix;
16
+ get viewMatrix(): Mat4Type;
17
+ /** Compute view matrix as inverse of the camera's world transform. */
18
+ updateViewMatrix(): void;
19
+ /** Extract world-space position from the worldMatrix translation column. */
20
+ getWorldPosition(out: Float32Array): Float32Array;
21
+ /**
22
+ * Convert screen pixel coordinates to a world-space point using GPU depth readback.
23
+ * Returns null if the pixel is sky (depth >= 1.0) or closer than nearClip.
24
+ */
25
+ screenToWorldPoint(x: number, y: number, nearClip?: number): Promise<Float32Array | null>;
26
+ /** Shortcut: get the main camera from the currently active scene. */
27
+ static get main(): Camera | null;
28
+ /** Find the first enabled Camera with isMainCamera=true in the scene. */
29
+ static getMain(scene: Scene): Camera | null;
30
+ }
31
+ //# sourceMappingURL=camera.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,kEAAkE;AAClE,MAAM,WAAW,qBAAqB;IACpC,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CAC3F;AAED,qBAAa,MAAO,SAAQ,SAAS;IACnC,IAAI,SAAe;IACnB,IAAI,SAAO;IACX,GAAG,SAAO;IACV,YAAY,UAAS;IACrB,UAAU,4BAA4C;IAEtD,sDAAsD;IACtD,MAAM,CAAC,aAAa,EAAE,qBAAqB,GAAG,IAAI,CAAQ;IAE1D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2B;IAEvD,IAAI,UAAU,IAAI,QAAQ,CAEzB;IAED,sEAAsE;IACtE,gBAAgB,IAAI,IAAI;IAIxB,4EAA4E;IAC5E,gBAAgB,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY;IAQjD;;;OAGG;IACG,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAK/F,qEAAqE;IACrE,MAAM,KAAK,IAAI,IAAI,MAAM,GAAG,IAAI,CAG/B;IAED,yEAAyE;IACzE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI;CAS5C"}
package/dist/camera.js ADDED
@@ -0,0 +1,53 @@
1
+ import { Component, Scene } from '@certe/atmos-core';
2
+ import { Mat4 } from '@certe/atmos-math';
3
+ export class Camera extends Component {
4
+ fovY = Math.PI / 4;
5
+ near = 0.1;
6
+ far = 100;
7
+ isMainCamera = false;
8
+ clearColor = new Float32Array([0.05, 0.05, 0.1, 1.0]);
9
+ /** Set by RenderSystem when it activates a camera. */
10
+ static _renderSystem = null;
11
+ _viewMatrix = Mat4.create();
12
+ get viewMatrix() {
13
+ return this._viewMatrix;
14
+ }
15
+ /** Compute view matrix as inverse of the camera's world transform. */
16
+ updateViewMatrix() {
17
+ Mat4.invert(this._viewMatrix, this.gameObject.transform.worldMatrix);
18
+ }
19
+ /** Extract world-space position from the worldMatrix translation column. */
20
+ getWorldPosition(out) {
21
+ const m = this.gameObject.transform.worldMatrix;
22
+ out[0] = m[12];
23
+ out[1] = m[13];
24
+ out[2] = m[14];
25
+ return out;
26
+ }
27
+ /**
28
+ * Convert screen pixel coordinates to a world-space point using GPU depth readback.
29
+ * Returns null if the pixel is sky (depth >= 1.0) or closer than nearClip.
30
+ */
31
+ async screenToWorldPoint(x, y, nearClip) {
32
+ if (!Camera._renderSystem)
33
+ return null;
34
+ return Camera._renderSystem.screenToWorldPoint(x, y, nearClip);
35
+ }
36
+ /** Shortcut: get the main camera from the currently active scene. */
37
+ static get main() {
38
+ if (!Scene.current)
39
+ return null;
40
+ return Camera.getMain(Scene.current);
41
+ }
42
+ /** Find the first enabled Camera with isMainCamera=true in the scene. */
43
+ static getMain(scene) {
44
+ for (const obj of scene.getAllObjects()) {
45
+ const cam = obj.getComponent(Camera);
46
+ if (cam && cam.enabled && cam.isMainCamera) {
47
+ return cam;
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+ }
53
+ //# sourceMappingURL=camera.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera.js","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAQzC,MAAM,OAAO,MAAO,SAAQ,SAAS;IACnC,IAAI,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IACnB,IAAI,GAAG,GAAG,CAAC;IACX,GAAG,GAAG,GAAG,CAAC;IACV,YAAY,GAAG,KAAK,CAAC;IACrB,UAAU,GAAG,IAAI,YAAY,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IAEtD,sDAAsD;IACtD,MAAM,CAAC,aAAa,GAAiC,IAAI,CAAC;IAEzC,WAAW,GAAa,IAAI,CAAC,MAAM,EAAE,CAAC;IAEvD,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,sEAAsE;IACtE,gBAAgB;QACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACvE,CAAC;IAED,4EAA4E;IAC5E,gBAAgB,CAAC,GAAiB;QAChC,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC;QAChD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAE,CAAC;QAChB,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAE,CAAC;QAChB,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAE,CAAC;QAChB,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,kBAAkB,CAAC,CAAS,EAAE,CAAS,EAAE,QAAiB;QAC9D,IAAI,CAAC,MAAM,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QACvC,OAAO,MAAM,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;IACjE,CAAC;IAED,qEAAqE;IACrE,MAAM,KAAK,IAAI;QACb,IAAI,CAAC,KAAK,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAChC,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,yEAAyE;IACzE,MAAM,CAAC,OAAO,CAAC,KAAY;QACzB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC;YACxC,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;gBAC3C,OAAO,GAAG,CAAC;YACb,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Non-MSAA depth pre-pass for screen-space effects (SSAO).
3
+ * Renders scene geometry to a depth32float texture using the camera VP matrix.
4
+ * The resulting texture has TEXTURE_BINDING so it can be sampled in post-processing.
5
+ */
6
+ import type { Scene } from '@certe/atmos-core';
7
+ import type { Mat4Type } from '@certe/atmos-math';
8
+ export declare class DepthPrepass {
9
+ private readonly _device;
10
+ private readonly _pipeline;
11
+ private readonly _vpBuffer;
12
+ private readonly _vpBindGroup;
13
+ private _depthTexture;
14
+ private _depthView;
15
+ private _width;
16
+ private _height;
17
+ get depthView(): GPUTextureView;
18
+ constructor(device: GPUDevice, objectBindGroupLayout: GPUBindGroupLayout, w: number, h: number);
19
+ private _createTexture;
20
+ resize(w: number, h: number): void;
21
+ execute(encoder: GPUCommandEncoder, scene: Scene, cameraVP: Mat4Type, extraDraw?: (pass: GPURenderPassEncoder) => void): void;
22
+ destroy(): void;
23
+ }
24
+ //# sourceMappingURL=depth-prepass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"depth-prepass.d.ts","sourceRoot":"","sources":["../src/depth-prepass.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAKlD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAY;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAK;IAEpB,IAAI,SAAS,IAAI,cAAc,CAA4B;gBAE/C,MAAM,EAAE,SAAS,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAgD9F,OAAO,CAAC,cAAc;IAQtB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IASlC,OAAO,CACL,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAC5D,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,GAC/C,IAAI;IAgCP,OAAO,IAAI,IAAI;CAIhB"}