topazcube 0.1.31 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +0 -0
- package/README.md +0 -0
- package/dist/Renderer.cjs +20844 -0
- package/dist/Renderer.cjs.map +1 -0
- package/dist/Renderer.js +20827 -0
- package/dist/Renderer.js.map +1 -0
- package/dist/client.cjs +91 -260
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +68 -215
- package/dist/client.js.map +1 -1
- package/dist/server.cjs +165 -432
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +117 -370
- package/dist/server.js.map +1 -1
- package/dist/terminal.cjs +113 -200
- package/dist/terminal.cjs.map +1 -1
- package/dist/terminal.js +50 -51
- package/dist/terminal.js.map +1 -1
- package/dist/utils-CRhi1BDa.cjs +259 -0
- package/dist/utils-CRhi1BDa.cjs.map +1 -0
- package/dist/utils-D7tXt6-2.js +260 -0
- package/dist/utils-D7tXt6-2.js.map +1 -0
- package/package.json +19 -15
- package/src/{client.ts → network/client.js} +170 -403
- package/src/{compress-browser.ts → network/compress-browser.js} +2 -4
- package/src/{compress-node.ts → network/compress-node.js} +8 -14
- package/src/{server.ts → network/server.js} +229 -317
- package/src/{terminal.js → network/terminal.js} +0 -0
- package/src/{topazcube.ts → network/topazcube.js} +2 -2
- package/src/network/utils.js +375 -0
- package/src/renderer/Camera.js +191 -0
- package/src/renderer/DebugUI.js +703 -0
- package/src/renderer/Geometry.js +1049 -0
- package/src/renderer/Material.js +64 -0
- package/src/renderer/Mesh.js +211 -0
- package/src/renderer/Node.js +112 -0
- package/src/renderer/Pipeline.js +645 -0
- package/src/renderer/Renderer.js +1496 -0
- package/src/renderer/Skin.js +792 -0
- package/src/renderer/Texture.js +584 -0
- package/src/renderer/core/AssetManager.js +394 -0
- package/src/renderer/core/CullingSystem.js +308 -0
- package/src/renderer/core/EntityManager.js +541 -0
- package/src/renderer/core/InstanceManager.js +343 -0
- package/src/renderer/core/ParticleEmitter.js +358 -0
- package/src/renderer/core/ParticleSystem.js +564 -0
- package/src/renderer/core/SpriteSystem.js +349 -0
- package/src/renderer/gltf.js +563 -0
- package/src/renderer/math.js +161 -0
- package/src/renderer/rendering/HistoryBufferManager.js +333 -0
- package/src/renderer/rendering/ProbeCapture.js +1495 -0
- package/src/renderer/rendering/ReflectionProbeManager.js +352 -0
- package/src/renderer/rendering/RenderGraph.js +2258 -0
- package/src/renderer/rendering/passes/AOPass.js +308 -0
- package/src/renderer/rendering/passes/AmbientCapturePass.js +593 -0
- package/src/renderer/rendering/passes/BasePass.js +101 -0
- package/src/renderer/rendering/passes/BloomPass.js +420 -0
- package/src/renderer/rendering/passes/CRTPass.js +724 -0
- package/src/renderer/rendering/passes/FogPass.js +445 -0
- package/src/renderer/rendering/passes/GBufferPass.js +730 -0
- package/src/renderer/rendering/passes/HiZPass.js +744 -0
- package/src/renderer/rendering/passes/LightingPass.js +753 -0
- package/src/renderer/rendering/passes/ParticlePass.js +841 -0
- package/src/renderer/rendering/passes/PlanarReflectionPass.js +456 -0
- package/src/renderer/rendering/passes/PostProcessPass.js +405 -0
- package/src/renderer/rendering/passes/ReflectionPass.js +157 -0
- package/src/renderer/rendering/passes/RenderPostPass.js +364 -0
- package/src/renderer/rendering/passes/SSGIPass.js +266 -0
- package/src/renderer/rendering/passes/SSGITilePass.js +305 -0
- package/src/renderer/rendering/passes/ShadowPass.js +2072 -0
- package/src/renderer/rendering/passes/TransparentPass.js +831 -0
- package/src/renderer/rendering/passes/VolumetricFogPass.js +715 -0
- package/src/renderer/rendering/shaders/ao.wgsl +182 -0
- package/src/renderer/rendering/shaders/bloom.wgsl +97 -0
- package/src/renderer/rendering/shaders/bloom_blur.wgsl +80 -0
- package/src/renderer/rendering/shaders/crt.wgsl +455 -0
- package/src/renderer/rendering/shaders/depth_copy.wgsl +17 -0
- package/src/renderer/rendering/shaders/geometry.wgsl +580 -0
- package/src/renderer/rendering/shaders/hiz_reduce.wgsl +114 -0
- package/src/renderer/rendering/shaders/light_culling.wgsl +204 -0
- package/src/renderer/rendering/shaders/lighting.wgsl +932 -0
- package/src/renderer/rendering/shaders/lighting_common.wgsl +143 -0
- package/src/renderer/rendering/shaders/particle_render.wgsl +672 -0
- package/src/renderer/rendering/shaders/particle_simulate.wgsl +440 -0
- package/src/renderer/rendering/shaders/postproc.wgsl +293 -0
- package/src/renderer/rendering/shaders/render_post.wgsl +289 -0
- package/src/renderer/rendering/shaders/shadow.wgsl +117 -0
- package/src/renderer/rendering/shaders/ssgi.wgsl +266 -0
- package/src/renderer/rendering/shaders/ssgi_accumulate.wgsl +114 -0
- package/src/renderer/rendering/shaders/ssgi_propagate.wgsl +132 -0
- package/src/renderer/rendering/shaders/volumetric_blur.wgsl +80 -0
- package/src/renderer/rendering/shaders/volumetric_composite.wgsl +80 -0
- package/src/renderer/rendering/shaders/volumetric_raymarch.wgsl +634 -0
- package/src/renderer/utils/BoundingSphere.js +439 -0
- package/src/renderer/utils/Frustum.js +281 -0
- package/src/renderer/utils/Raycaster.js +761 -0
- package/dist/client.d.cts +0 -211
- package/dist/client.d.ts +0 -211
- package/dist/server.d.cts +0 -120
- package/dist/server.d.ts +0 -120
- package/dist/terminal.d.cts +0 -64
- package/dist/terminal.d.ts +0 -64
- package/src/utils.ts +0 -403
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { BasePass } from "./BasePass.js"
|
|
2
|
+
|
|
3
|
+
import bloomExtractWGSL from "../shaders/bloom.wgsl"
|
|
4
|
+
import bloomBlurWGSL from "../shaders/bloom_blur.wgsl"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* BloomPass - HDR Bloom/Glare effect
|
|
8
|
+
*
|
|
9
|
+
* Extracts bright pixels with exponential falloff and applies
|
|
10
|
+
* two-pass diagonal Gaussian blur for X-shaped glare effect.
|
|
11
|
+
*
|
|
12
|
+
* Input: HDR lighting output
|
|
13
|
+
* Output: Blurred bloom texture with X-shaped glare
|
|
14
|
+
*/
|
|
15
|
+
class BloomPass extends BasePass {
|
|
16
|
+
constructor(engine = null) {
|
|
17
|
+
super('Bloom', engine)
|
|
18
|
+
|
|
19
|
+
// Pipelines
|
|
20
|
+
this.extractPipeline = null
|
|
21
|
+
this.blurPipeline = null
|
|
22
|
+
|
|
23
|
+
// Textures (ping-pong for blur)
|
|
24
|
+
this.brightTexture = null // Extracted bright pixels
|
|
25
|
+
this.blurTextureA = null // After horizontal blur
|
|
26
|
+
this.blurTextureB = null // After vertical blur (final output)
|
|
27
|
+
|
|
28
|
+
// Resources
|
|
29
|
+
this.inputTexture = null
|
|
30
|
+
this.extractUniformBuffer = null
|
|
31
|
+
this.blurUniformBufferH = null // Horizontal blur uniforms
|
|
32
|
+
this.blurUniformBufferV = null // Vertical blur uniforms
|
|
33
|
+
this.extractBindGroup = null
|
|
34
|
+
this.blurBindGroupH = null // Horizontal blur
|
|
35
|
+
this.blurBindGroupV = null // Vertical blur
|
|
36
|
+
this.sampler = null
|
|
37
|
+
|
|
38
|
+
// Textures pending destruction (wait for GPU to finish using them)
|
|
39
|
+
// Use a ring buffer of 3 frames to ensure GPU is definitely done
|
|
40
|
+
this._pendingDestroyRing = [[], [], []]
|
|
41
|
+
this._pendingDestroyIndex = 0
|
|
42
|
+
|
|
43
|
+
// Render dimensions (may differ from canvas when effect scaling is applied)
|
|
44
|
+
this.width = 0
|
|
45
|
+
this.height = 0
|
|
46
|
+
// Bloom internal resolution (scaled down for performance)
|
|
47
|
+
this.bloomWidth = 0
|
|
48
|
+
this.bloomHeight = 0
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Convenience getters for bloom settings
|
|
52
|
+
get bloomEnabled() { return this.settings?.bloom?.enabled ?? true }
|
|
53
|
+
get intensity() { return this.settings?.bloom?.intensity ?? 1.0 }
|
|
54
|
+
get threshold() { return this.settings?.bloom?.threshold ?? 0.8 }
|
|
55
|
+
get softThreshold() { return this.settings?.bloom?.softThreshold ?? 0.5 }
|
|
56
|
+
get radius() { return this.settings?.bloom?.radius ?? 32 }
|
|
57
|
+
get emissiveBoost() { return this.settings?.bloom?.emissiveBoost ?? 2.0 }
|
|
58
|
+
get maxBrightness() { return this.settings?.bloom?.maxBrightness ?? 4.0 }
|
|
59
|
+
get renderScale() { return this.settings?.rendering?.renderScale ?? 1.0 }
|
|
60
|
+
// Bloom resolution scale - 0.5 = half res (faster), 1.0 = full res (quality)
|
|
61
|
+
get bloomScale() { return this.settings?.bloom?.scale ?? 0.5 }
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Set the input texture (HDR lighting output)
|
|
65
|
+
* @param {Object} texture - Input texture with view property
|
|
66
|
+
*/
|
|
67
|
+
setInputTexture(texture) {
|
|
68
|
+
// Only rebuild if the texture actually changed
|
|
69
|
+
if (this.inputTexture !== texture) {
|
|
70
|
+
this.inputTexture = texture
|
|
71
|
+
this._needsRebuild = true
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async _init() {
|
|
76
|
+
const { device } = this.engine
|
|
77
|
+
|
|
78
|
+
// Create sampler for all bloom textures
|
|
79
|
+
this.sampler = device.createSampler({
|
|
80
|
+
label: 'Bloom Sampler',
|
|
81
|
+
minFilter: 'linear',
|
|
82
|
+
magFilter: 'linear',
|
|
83
|
+
addressModeU: 'clamp-to-edge',
|
|
84
|
+
addressModeV: 'clamp-to-edge',
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create or recreate bloom textures at scaled resolution
|
|
90
|
+
*/
|
|
91
|
+
_createTextures(width, height) {
|
|
92
|
+
const { device } = this.engine
|
|
93
|
+
|
|
94
|
+
// Queue old textures for deferred destruction (GPU may still be using them)
|
|
95
|
+
// Add to current slot in ring buffer - will be destroyed 3 frames later
|
|
96
|
+
const slot = this._pendingDestroyRing[this._pendingDestroyIndex]
|
|
97
|
+
if (this.brightTexture?.texture) slot.push(this.brightTexture.texture)
|
|
98
|
+
if (this.blurTextureA?.texture) slot.push(this.blurTextureA.texture)
|
|
99
|
+
if (this.blurTextureB?.texture) slot.push(this.blurTextureB.texture)
|
|
100
|
+
|
|
101
|
+
// Calculate scaled resolution for bloom (0.5 = half res for performance)
|
|
102
|
+
// Bloom is blurry anyway, so half-res is usually sufficient
|
|
103
|
+
const scale = this.bloomScale
|
|
104
|
+
const bloomWidth = Math.max(1, Math.floor(width * scale))
|
|
105
|
+
const bloomHeight = Math.max(1, Math.floor(height * scale))
|
|
106
|
+
|
|
107
|
+
// Only log if dimensions actually changed
|
|
108
|
+
if (this.bloomWidth !== bloomWidth || this.bloomHeight !== bloomHeight) {
|
|
109
|
+
console.log(`Bloom: ${width}x${height} -> ${bloomWidth}x${bloomHeight} (scale: ${scale})`)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.bloomWidth = bloomWidth
|
|
113
|
+
this.bloomHeight = bloomHeight
|
|
114
|
+
|
|
115
|
+
// Create textures at scaled resolution
|
|
116
|
+
const createBloomTexture = (label) => {
|
|
117
|
+
const texture = device.createTexture({
|
|
118
|
+
label,
|
|
119
|
+
size: { width: bloomWidth, height: bloomHeight, depthOrArrayLayers: 1 },
|
|
120
|
+
format: 'rgba16float',
|
|
121
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
122
|
+
})
|
|
123
|
+
return {
|
|
124
|
+
texture,
|
|
125
|
+
view: texture.createView({ label: `${label} View` }),
|
|
126
|
+
sampler: this.sampler,
|
|
127
|
+
width: bloomWidth,
|
|
128
|
+
height: bloomHeight,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.brightTexture = createBloomTexture('Bloom Bright')
|
|
133
|
+
this.blurTextureA = createBloomTexture('Bloom Blur A')
|
|
134
|
+
this.blurTextureB = createBloomTexture('Bloom Blur B')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async _buildPipeline() {
|
|
138
|
+
if (!this.inputTexture) {
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const { device, canvas } = this.engine
|
|
143
|
+
// Store initial dimensions (may be updated by resize)
|
|
144
|
+
this.width = canvas.width
|
|
145
|
+
this.height = canvas.height
|
|
146
|
+
|
|
147
|
+
// Create bloom textures
|
|
148
|
+
this._createTextures(this.width, this.height)
|
|
149
|
+
|
|
150
|
+
// Create uniform buffers
|
|
151
|
+
this.extractUniformBuffer = device.createBuffer({
|
|
152
|
+
label: 'Bloom Extract Uniforms',
|
|
153
|
+
size: 32, // 5 floats + padding for 16-byte alignment
|
|
154
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
this.blurUniformBufferH = device.createBuffer({
|
|
158
|
+
label: 'Bloom Blur H Uniforms',
|
|
159
|
+
size: 32, // 8 floats (2 vec2 + 2 float + padding)
|
|
160
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
this.blurUniformBufferV = device.createBuffer({
|
|
164
|
+
label: 'Bloom Blur V Uniforms',
|
|
165
|
+
size: 32,
|
|
166
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// ===== EXTRACT PIPELINE =====
|
|
170
|
+
const extractBGL = device.createBindGroupLayout({
|
|
171
|
+
label: 'Bloom Extract BGL',
|
|
172
|
+
entries: [
|
|
173
|
+
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
|
|
174
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
|
|
175
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
|
|
176
|
+
],
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const extractModule = device.createShaderModule({
|
|
180
|
+
label: 'Bloom Extract Shader',
|
|
181
|
+
code: bloomExtractWGSL,
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// ===== BLUR PIPELINE =====
|
|
185
|
+
const blurBGL = device.createBindGroupLayout({
|
|
186
|
+
label: 'Bloom Blur BGL',
|
|
187
|
+
entries: [
|
|
188
|
+
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
|
|
189
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
|
|
190
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
|
|
191
|
+
],
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
const blurModule = device.createShaderModule({
|
|
195
|
+
label: 'Bloom Blur Shader',
|
|
196
|
+
code: bloomBlurWGSL,
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
// Create both pipelines in parallel for faster initialization
|
|
200
|
+
const [extractPipeline, blurPipeline] = await Promise.all([
|
|
201
|
+
device.createRenderPipelineAsync({
|
|
202
|
+
label: 'Bloom Extract Pipeline',
|
|
203
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [extractBGL] }),
|
|
204
|
+
vertex: { module: extractModule, entryPoint: 'vertexMain' },
|
|
205
|
+
fragment: {
|
|
206
|
+
module: extractModule,
|
|
207
|
+
entryPoint: 'fragmentMain',
|
|
208
|
+
targets: [{ format: 'rgba16float' }],
|
|
209
|
+
},
|
|
210
|
+
primitive: { topology: 'triangle-list' },
|
|
211
|
+
}),
|
|
212
|
+
device.createRenderPipelineAsync({
|
|
213
|
+
label: 'Bloom Blur Pipeline',
|
|
214
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [blurBGL] }),
|
|
215
|
+
vertex: { module: blurModule, entryPoint: 'vertexMain' },
|
|
216
|
+
fragment: {
|
|
217
|
+
module: blurModule,
|
|
218
|
+
entryPoint: 'fragmentMain',
|
|
219
|
+
targets: [{ format: 'rgba16float' }],
|
|
220
|
+
},
|
|
221
|
+
primitive: { topology: 'triangle-list' },
|
|
222
|
+
})
|
|
223
|
+
])
|
|
224
|
+
|
|
225
|
+
this.extractPipeline = extractPipeline
|
|
226
|
+
this.blurPipeline = blurPipeline
|
|
227
|
+
|
|
228
|
+
this.extractBindGroup = device.createBindGroup({
|
|
229
|
+
label: 'Bloom Extract Bind Group',
|
|
230
|
+
layout: extractBGL,
|
|
231
|
+
entries: [
|
|
232
|
+
{ binding: 0, resource: { buffer: this.extractUniformBuffer } },
|
|
233
|
+
{ binding: 1, resource: this.inputTexture.view },
|
|
234
|
+
{ binding: 2, resource: this.sampler },
|
|
235
|
+
],
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
// Horizontal blur: brightTexture -> blurTextureA
|
|
239
|
+
this.blurBindGroupH = device.createBindGroup({
|
|
240
|
+
label: 'Bloom Blur H Bind Group',
|
|
241
|
+
layout: blurBGL,
|
|
242
|
+
entries: [
|
|
243
|
+
{ binding: 0, resource: { buffer: this.blurUniformBufferH } },
|
|
244
|
+
{ binding: 1, resource: this.brightTexture.view },
|
|
245
|
+
{ binding: 2, resource: this.sampler },
|
|
246
|
+
],
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// Vertical blur: blurTextureA -> blurTextureB
|
|
250
|
+
this.blurBindGroupV = device.createBindGroup({
|
|
251
|
+
label: 'Bloom Blur V Bind Group',
|
|
252
|
+
layout: blurBGL,
|
|
253
|
+
entries: [
|
|
254
|
+
{ binding: 0, resource: { buffer: this.blurUniformBufferV } },
|
|
255
|
+
{ binding: 1, resource: this.blurTextureA.view },
|
|
256
|
+
{ binding: 2, resource: this.sampler },
|
|
257
|
+
],
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
this._needsRebuild = false
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async _execute(context) {
|
|
264
|
+
// Skip if bloom is disabled in settings
|
|
265
|
+
if (!this.bloomEnabled) {
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const { device, canvas } = this.engine
|
|
270
|
+
|
|
271
|
+
// Rotate ring buffer and destroy textures from 3 frames ago
|
|
272
|
+
this._pendingDestroyIndex = (this._pendingDestroyIndex + 1) % 3
|
|
273
|
+
const toDestroy = this._pendingDestroyRing[this._pendingDestroyIndex]
|
|
274
|
+
for (const tex of toDestroy) {
|
|
275
|
+
tex.destroy()
|
|
276
|
+
}
|
|
277
|
+
this._pendingDestroyRing[this._pendingDestroyIndex] = []
|
|
278
|
+
|
|
279
|
+
// Rebuild pipeline if needed
|
|
280
|
+
if (this._needsRebuild) {
|
|
281
|
+
await this._buildPipeline()
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// If rebuild was attempted but failed, don't use stale pipeline with old bind groups
|
|
285
|
+
if (!this.extractPipeline || !this.blurPipeline || !this.inputTexture || this._needsRebuild) {
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Use bloom-specific dimensions (may be scaled down for performance)
|
|
290
|
+
const bloomWidth = this.bloomWidth
|
|
291
|
+
const bloomHeight = this.bloomHeight
|
|
292
|
+
|
|
293
|
+
// Scale radius based on bloom height relative to 1080p (settings are authored for 1080p)
|
|
294
|
+
// Also scale by bloomScale since we're working at lower resolution
|
|
295
|
+
const heightScale = bloomHeight / 1080
|
|
296
|
+
const blurRadius = this.radius * this.renderScale * heightScale
|
|
297
|
+
|
|
298
|
+
// Update all uniforms BEFORE creating command encoder
|
|
299
|
+
// (writeBuffer is immediate, commands are batched)
|
|
300
|
+
device.queue.writeBuffer(this.extractUniformBuffer, 0, new Float32Array([
|
|
301
|
+
this.threshold,
|
|
302
|
+
this.softThreshold,
|
|
303
|
+
this.intensity,
|
|
304
|
+
this.emissiveBoost,
|
|
305
|
+
this.maxBrightness,
|
|
306
|
+
0.0, 0.0, 0.0, // padding
|
|
307
|
+
]))
|
|
308
|
+
|
|
309
|
+
// Diagonal blur uniforms (X-shaped glare)
|
|
310
|
+
const diag = 0.7071067811865476 // 1/sqrt(2)
|
|
311
|
+
device.queue.writeBuffer(this.blurUniformBufferH, 0, new Float32Array([
|
|
312
|
+
diag, diag, // direction (diagonal: top-left to bottom-right)
|
|
313
|
+
1.0 / bloomWidth, 1.0 / bloomHeight, // texelSize (at bloom resolution)
|
|
314
|
+
blurRadius, 0.0, // blurRadius, padding
|
|
315
|
+
0.0, 0.0, // more padding for alignment
|
|
316
|
+
]))
|
|
317
|
+
|
|
318
|
+
// Second diagonal blur uniforms
|
|
319
|
+
device.queue.writeBuffer(this.blurUniformBufferV, 0, new Float32Array([
|
|
320
|
+
diag, -diag, // direction (diagonal: bottom-left to top-right)
|
|
321
|
+
1.0 / bloomWidth, 1.0 / bloomHeight, // texelSize (at bloom resolution)
|
|
322
|
+
blurRadius, 0.0, // blurRadius, padding
|
|
323
|
+
0.0, 0.0, // more padding for alignment
|
|
324
|
+
]))
|
|
325
|
+
|
|
326
|
+
const commandEncoder = device.createCommandEncoder({ label: 'Bloom Pass' })
|
|
327
|
+
|
|
328
|
+
// ===== PASS 1: Extract bright pixels =====
|
|
329
|
+
{
|
|
330
|
+
const pass = commandEncoder.beginRenderPass({
|
|
331
|
+
label: 'Bloom Extract',
|
|
332
|
+
colorAttachments: [{
|
|
333
|
+
view: this.brightTexture.view,
|
|
334
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
335
|
+
loadOp: 'clear',
|
|
336
|
+
storeOp: 'store',
|
|
337
|
+
}],
|
|
338
|
+
})
|
|
339
|
+
pass.setPipeline(this.extractPipeline)
|
|
340
|
+
pass.setBindGroup(0, this.extractBindGroup)
|
|
341
|
+
pass.draw(3)
|
|
342
|
+
pass.end()
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// ===== PASS 2: Diagonal blur (top-left to bottom-right) =====
|
|
346
|
+
{
|
|
347
|
+
const pass = commandEncoder.beginRenderPass({
|
|
348
|
+
label: 'Bloom Blur Diag1',
|
|
349
|
+
colorAttachments: [{
|
|
350
|
+
view: this.blurTextureA.view,
|
|
351
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
352
|
+
loadOp: 'clear',
|
|
353
|
+
storeOp: 'store',
|
|
354
|
+
}],
|
|
355
|
+
})
|
|
356
|
+
pass.setPipeline(this.blurPipeline)
|
|
357
|
+
pass.setBindGroup(0, this.blurBindGroupH)
|
|
358
|
+
pass.draw(3)
|
|
359
|
+
pass.end()
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ===== PASS 3: Diagonal blur (bottom-left to top-right) =====
|
|
363
|
+
{
|
|
364
|
+
const pass = commandEncoder.beginRenderPass({
|
|
365
|
+
label: 'Bloom Blur Diag2',
|
|
366
|
+
colorAttachments: [{
|
|
367
|
+
view: this.blurTextureB.view,
|
|
368
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
369
|
+
loadOp: 'clear',
|
|
370
|
+
storeOp: 'store',
|
|
371
|
+
}],
|
|
372
|
+
})
|
|
373
|
+
pass.setPipeline(this.blurPipeline)
|
|
374
|
+
pass.setBindGroup(0, this.blurBindGroupV)
|
|
375
|
+
pass.draw(3)
|
|
376
|
+
pass.end()
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
device.queue.submit([commandEncoder.finish()])
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async _resize(width, height) {
|
|
383
|
+
this.width = width
|
|
384
|
+
this.height = height
|
|
385
|
+
this._needsRebuild = true
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
_destroy() {
|
|
389
|
+
if (this.brightTexture?.texture) this.brightTexture.texture.destroy()
|
|
390
|
+
if (this.blurTextureA?.texture) this.blurTextureA.texture.destroy()
|
|
391
|
+
if (this.blurTextureB?.texture) this.blurTextureB.texture.destroy()
|
|
392
|
+
// Clean up any pending textures in all ring buffer slots
|
|
393
|
+
for (const slot of this._pendingDestroyRing) {
|
|
394
|
+
for (const tex of slot) {
|
|
395
|
+
tex.destroy()
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
this._pendingDestroyRing = [[], [], []]
|
|
399
|
+
this.extractPipeline = null
|
|
400
|
+
this.blurPipeline = null
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Get the final bloom texture (after blur)
|
|
405
|
+
*/
|
|
406
|
+
getOutputTexture() {
|
|
407
|
+
return this.blurTextureB
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Get the bright extraction texture (before blur)
|
|
412
|
+
* Used by SSGITilePass for directional light accumulation
|
|
413
|
+
*/
|
|
414
|
+
getBrightTexture() {
|
|
415
|
+
return this.brightTexture
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
export { BloomPass }
|