topazcube 0.1.30 → 0.1.33
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 +18200 -0
- package/dist/Renderer.cjs.map +1 -0
- package/dist/Renderer.js +18183 -0
- package/dist/Renderer.js.map +1 -0
- package/dist/client.cjs +94 -260
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +71 -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} +173 -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 +572 -0
- package/src/renderer/Geometry.js +1049 -0
- package/src/renderer/Material.js +61 -0
- package/src/renderer/Mesh.js +211 -0
- package/src/renderer/Node.js +112 -0
- package/src/renderer/Pipeline.js +643 -0
- package/src/renderer/Renderer.js +1324 -0
- package/src/renderer/Skin.js +792 -0
- package/src/renderer/Texture.js +584 -0
- package/src/renderer/core/AssetManager.js +359 -0
- package/src/renderer/core/CullingSystem.js +307 -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 +546 -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 +2064 -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 +417 -0
- package/src/renderer/rendering/passes/FogPass.js +419 -0
- package/src/renderer/rendering/passes/GBufferPass.js +706 -0
- package/src/renderer/rendering/passes/HiZPass.js +714 -0
- package/src/renderer/rendering/passes/LightingPass.js +739 -0
- package/src/renderer/rendering/passes/ParticlePass.js +835 -0
- package/src/renderer/rendering/passes/PlanarReflectionPass.js +456 -0
- package/src/renderer/rendering/passes/PostProcessPass.js +282 -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 +265 -0
- package/src/renderer/rendering/passes/SSGITilePass.js +296 -0
- package/src/renderer/rendering/passes/ShadowPass.js +1822 -0
- package/src/renderer/rendering/passes/TransparentPass.js +831 -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/depth_copy.wgsl +17 -0
- package/src/renderer/rendering/shaders/geometry.wgsl +550 -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 +525 -0
- package/src/renderer/rendering/shaders/particle_simulate.wgsl +440 -0
- package/src/renderer/rendering/shaders/postproc.wgsl +272 -0
- package/src/renderer/rendering/shaders/render_post.wgsl +289 -0
- package/src/renderer/rendering/shaders/shadow.wgsl +76 -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/utils/BoundingSphere.js +439 -0
- package/src/renderer/utils/Frustum.js +281 -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,419 @@
|
|
|
1
|
+
import { BasePass } from "./BasePass.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FogPass - Distance-based fog with height fade
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Distance fog based on camera distance (not Z-depth)
|
|
8
|
+
* - Two-gradient system: distance[0]->distance[1] and distance[1]->distance[2]
|
|
9
|
+
* - Height fade: maximum fog at bottomY, zero fog at topY
|
|
10
|
+
* - Emissive/bright colors show through fog more (HDR resistance)
|
|
11
|
+
* - Applied before bloom so bright objects still bloom through fog
|
|
12
|
+
*
|
|
13
|
+
* Input: HDR lighting output, GBuffer (for depth texture)
|
|
14
|
+
* Output: Fog-applied HDR texture
|
|
15
|
+
*/
|
|
16
|
+
class FogPass extends BasePass {
|
|
17
|
+
constructor(engine = null) {
|
|
18
|
+
super('Fog', engine)
|
|
19
|
+
|
|
20
|
+
this.renderPipeline = null
|
|
21
|
+
this.outputTexture = null
|
|
22
|
+
this.inputTexture = null
|
|
23
|
+
this.gbuffer = null
|
|
24
|
+
this.uniformBuffer = null
|
|
25
|
+
this.bindGroup = null
|
|
26
|
+
this.sampler = null
|
|
27
|
+
|
|
28
|
+
// Render dimensions
|
|
29
|
+
this.width = 0
|
|
30
|
+
this.height = 0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fog settings getters
|
|
34
|
+
get fogEnabled() { return this.settings?.environment?.fog?.enabled ?? false }
|
|
35
|
+
get fogColor() { return this.settings?.environment?.fog?.color ?? [0.8, 0.85, 0.9] }
|
|
36
|
+
get fogDistances() { return this.settings?.environment?.fog?.distances ?? [0, 50, 200] }
|
|
37
|
+
get fogAlpha() { return this.settings?.environment?.fog?.alpha ?? [0.0, 0.3, 0.8] }
|
|
38
|
+
get fogHeightFade() { return this.settings?.environment?.fog?.heightFade ?? [-10, 100] }
|
|
39
|
+
get fogBrightResist() { return this.settings?.environment?.fog?.brightResist ?? 0.8 }
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Set the input texture (HDR lighting output)
|
|
43
|
+
*/
|
|
44
|
+
setInputTexture(texture) {
|
|
45
|
+
if (this.inputTexture !== texture) {
|
|
46
|
+
this.inputTexture = texture
|
|
47
|
+
this._needsRebuild = true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Set the GBuffer for depth/normal reconstruction
|
|
53
|
+
*/
|
|
54
|
+
setGBuffer(gbuffer) {
|
|
55
|
+
if (this.gbuffer !== gbuffer) {
|
|
56
|
+
this.gbuffer = gbuffer
|
|
57
|
+
this._needsRebuild = true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async _init() {
|
|
62
|
+
const { device } = this.engine
|
|
63
|
+
|
|
64
|
+
// Create sampler
|
|
65
|
+
this.sampler = device.createSampler({
|
|
66
|
+
label: 'Fog Sampler',
|
|
67
|
+
minFilter: 'linear',
|
|
68
|
+
magFilter: 'linear',
|
|
69
|
+
addressModeU: 'clamp-to-edge',
|
|
70
|
+
addressModeV: 'clamp-to-edge',
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// Create uniform buffer
|
|
74
|
+
// inverseProj (64) + inverseView (64) + cameraPosition (12) + near (4) +
|
|
75
|
+
// far (4) + fogEnabled (4) + fogColor (12) + brightResist (4) +
|
|
76
|
+
// distances (12) + pad (4) + alphas (12) + pad (4) + heightFade (8) + screenSize (8) = 224 bytes
|
|
77
|
+
// Round to 256 for alignment
|
|
78
|
+
this.uniformBuffer = device.createBuffer({
|
|
79
|
+
label: 'Fog Uniforms',
|
|
80
|
+
size: 256,
|
|
81
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async _buildPipeline() {
|
|
86
|
+
if (!this.inputTexture || !this.gbuffer) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const { device } = this.engine
|
|
91
|
+
|
|
92
|
+
// Destroy old output texture before creating new one
|
|
93
|
+
if (this.outputTexture) {
|
|
94
|
+
this.outputTexture.destroy()
|
|
95
|
+
this.outputTexture = null
|
|
96
|
+
this.outputTextureView = null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Create output texture (same format as input)
|
|
100
|
+
// Include COPY_SRC for planar reflection pass which copies fog output
|
|
101
|
+
this.outputTexture = device.createTexture({
|
|
102
|
+
label: 'Fog Output',
|
|
103
|
+
size: { width: this.width || 1, height: this.height || 1, depthOrArrayLayers: 1 },
|
|
104
|
+
format: 'rgba16float',
|
|
105
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC,
|
|
106
|
+
})
|
|
107
|
+
this.outputTextureView = this.outputTexture.createView({ label: 'Fog Output View' })
|
|
108
|
+
|
|
109
|
+
// Create bind group layout
|
|
110
|
+
const bindGroupLayout = device.createBindGroupLayout({
|
|
111
|
+
label: 'Fog BGL',
|
|
112
|
+
entries: [
|
|
113
|
+
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
|
|
114
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
|
|
115
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
|
|
116
|
+
{ binding: 3, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'depth' } },
|
|
117
|
+
],
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const shaderModule = device.createShaderModule({
|
|
121
|
+
label: 'Fog Shader',
|
|
122
|
+
code: `
|
|
123
|
+
// Uniforms packed for proper WGSL alignment (vec3f needs 16-byte alignment)
|
|
124
|
+
struct Uniforms {
|
|
125
|
+
inverseProjection: mat4x4f, // floats 0-15
|
|
126
|
+
inverseView: mat4x4f, // floats 16-31
|
|
127
|
+
cameraPosition: vec3f, // floats 32-34
|
|
128
|
+
near: f32, // float 35
|
|
129
|
+
fogColor: vec3f, // floats 36-38
|
|
130
|
+
far: f32, // float 39
|
|
131
|
+
distances: vec3f, // floats 40-42
|
|
132
|
+
fogEnabled: f32, // float 43
|
|
133
|
+
alphas: vec3f, // floats 44-46
|
|
134
|
+
brightResist: f32, // float 47
|
|
135
|
+
heightFade: vec2f, // floats 48-49
|
|
136
|
+
screenSize: vec2f, // floats 50-51
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
140
|
+
@group(0) @binding(1) var inputTexture: texture_2d<f32>;
|
|
141
|
+
@group(0) @binding(2) var inputSampler: sampler;
|
|
142
|
+
@group(0) @binding(3) var depthTexture: texture_depth_2d;
|
|
143
|
+
|
|
144
|
+
struct VertexOutput {
|
|
145
|
+
@builtin(position) position: vec4f,
|
|
146
|
+
@location(0) uv: vec2f,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@vertex
|
|
150
|
+
fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
151
|
+
var output: VertexOutput;
|
|
152
|
+
// Full-screen triangle
|
|
153
|
+
let x = f32((vertexIndex << 1u) & 2u);
|
|
154
|
+
let y = f32(vertexIndex & 2u);
|
|
155
|
+
output.position = vec4f(x * 2.0 - 1.0, y * 2.0 - 1.0, 0.0, 1.0);
|
|
156
|
+
output.uv = vec2f(x, 1.0 - y);
|
|
157
|
+
return output;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@fragment
|
|
161
|
+
fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
|
|
162
|
+
let color = textureSample(inputTexture, inputSampler, input.uv);
|
|
163
|
+
|
|
164
|
+
// If fog disabled, pass through
|
|
165
|
+
if (uniforms.fogEnabled < 0.5) {
|
|
166
|
+
return color;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Sample depth from GBuffer depth texture (same as lighting shader)
|
|
170
|
+
let pixelCoords = vec2i(input.uv * uniforms.screenSize);
|
|
171
|
+
let depth = textureLoad(depthTexture, pixelCoords, 0);
|
|
172
|
+
|
|
173
|
+
// Check if this is sky (depth = 1 means far plane / cleared depth buffer)
|
|
174
|
+
let isSky = depth >= 0.9999;
|
|
175
|
+
|
|
176
|
+
// Get view ray direction (needed for both sky and geometry)
|
|
177
|
+
var iuv = input.uv;
|
|
178
|
+
iuv.y = 1.0 - iuv.y;
|
|
179
|
+
let ndc = vec4f(iuv * 2.0 - 1.0, 0.0, 1.0);
|
|
180
|
+
let viewRay = uniforms.inverseProjection * ndc;
|
|
181
|
+
let rayDir = normalize(viewRay.xyz / viewRay.w);
|
|
182
|
+
|
|
183
|
+
// For sky, treat as far distance; otherwise reconstruct linear depth
|
|
184
|
+
var cameraDistance: f32;
|
|
185
|
+
var worldPosY: f32;
|
|
186
|
+
|
|
187
|
+
if (isSky) {
|
|
188
|
+
// Sky is at far distance
|
|
189
|
+
cameraDistance = uniforms.far;
|
|
190
|
+
|
|
191
|
+
// Map view ray elevation to effective Y for height fade
|
|
192
|
+
// The fog clear zone in sky depends on topY:
|
|
193
|
+
// - Low topY (near camera): small clear zone, fog only at horizon
|
|
194
|
+
// - Medium topY (25m): clear zone extends to ~60° above horizon
|
|
195
|
+
// - High topY (100m+): almost no clear zone, fog fills sky
|
|
196
|
+
let worldRayDir = normalize((uniforms.inverseView * vec4f(rayDir, 0.0)).xyz);
|
|
197
|
+
let camY = uniforms.cameraPosition.y;
|
|
198
|
+
let topY = uniforms.heightFade.y;
|
|
199
|
+
let bottomY = uniforms.heightFade.x;
|
|
200
|
+
|
|
201
|
+
if (worldRayDir.y > 0.0) {
|
|
202
|
+
// Looking up: map elevation to effective Y
|
|
203
|
+
// Use a reference height to control how much sky can be clear
|
|
204
|
+
// At referenceHeight (50m), zenith reaches topY (fully clear)
|
|
205
|
+
// Above that, zenith stays fogged
|
|
206
|
+
let referenceHeight = 50.0;
|
|
207
|
+
let fogHeight = topY - camY;
|
|
208
|
+
let clearRange = min(fogHeight, referenceHeight);
|
|
209
|
+
worldPosY = camY + worldRayDir.y * clearRange;
|
|
210
|
+
} else {
|
|
211
|
+
// Looking down or at horizon: below camera, full fog
|
|
212
|
+
worldPosY = camY + worldRayDir.y * (camY - bottomY);
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
// Reconstruct linear depth: depth = (z - near) / (far - near), so z = near + depth * (far - near)
|
|
216
|
+
let linearDepth = uniforms.near + depth * (uniforms.far - uniforms.near);
|
|
217
|
+
|
|
218
|
+
// Use linear depth directly as camera distance
|
|
219
|
+
cameraDistance = linearDepth;
|
|
220
|
+
|
|
221
|
+
// Reconstruct world position for height fade
|
|
222
|
+
let viewPos = rayDir * (linearDepth / -rayDir.z);
|
|
223
|
+
let worldPos4 = uniforms.inverseView * vec4f(viewPos, 1.0);
|
|
224
|
+
worldPosY = worldPos4.y;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Distance fog - two gradients
|
|
228
|
+
var distanceFog: f32;
|
|
229
|
+
let d0 = uniforms.distances.x;
|
|
230
|
+
let d1 = uniforms.distances.y;
|
|
231
|
+
let d2 = uniforms.distances.z;
|
|
232
|
+
let a0 = uniforms.alphas.x;
|
|
233
|
+
let a1 = uniforms.alphas.y;
|
|
234
|
+
let a2 = uniforms.alphas.z;
|
|
235
|
+
|
|
236
|
+
if (cameraDistance <= d0) {
|
|
237
|
+
distanceFog = a0;
|
|
238
|
+
} else if (cameraDistance <= d1) {
|
|
239
|
+
let t = (cameraDistance - d0) / max(d1 - d0, 0.001);
|
|
240
|
+
distanceFog = mix(a0, a1, t);
|
|
241
|
+
} else if (cameraDistance <= d2) {
|
|
242
|
+
let t = (cameraDistance - d1) / max(d2 - d1, 0.001);
|
|
243
|
+
distanceFog = mix(a1, a2, t);
|
|
244
|
+
} else {
|
|
245
|
+
distanceFog = a2;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Height fade - full fog at bottomY, zero fog at topY
|
|
249
|
+
let bottomY = uniforms.heightFade.x;
|
|
250
|
+
let topY = uniforms.heightFade.y;
|
|
251
|
+
var heightFactor = clamp((worldPosY - bottomY) / max(topY - bottomY, 0.001), 0.0, 1.0);
|
|
252
|
+
// Below bottomY = maximum fog
|
|
253
|
+
if (worldPosY < bottomY) {
|
|
254
|
+
heightFactor = 0.0;
|
|
255
|
+
}
|
|
256
|
+
var fogAlpha = distanceFog * (1.0 - heightFactor);
|
|
257
|
+
|
|
258
|
+
// Emissive/bright resistance - HDR values show through fog
|
|
259
|
+
// Calculate luminance
|
|
260
|
+
let luminance = dot(color.rgb, vec3f(0.299, 0.587, 0.114));
|
|
261
|
+
// For HDR values > 1.0, reduce fog effect
|
|
262
|
+
let brightnessResist = clamp((luminance - 1.0) / 2.0, 0.0, 1.0);
|
|
263
|
+
fogAlpha *= (1.0 - brightnessResist * uniforms.brightResist);
|
|
264
|
+
|
|
265
|
+
// Apply fog
|
|
266
|
+
let foggedColor = mix(color.rgb, uniforms.fogColor, fogAlpha);
|
|
267
|
+
|
|
268
|
+
return vec4f(foggedColor, color.a);
|
|
269
|
+
}
|
|
270
|
+
`,
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
this.renderPipeline = await device.createRenderPipelineAsync({
|
|
274
|
+
label: 'Fog Pipeline',
|
|
275
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] }),
|
|
276
|
+
vertex: { module: shaderModule, entryPoint: 'vertexMain' },
|
|
277
|
+
fragment: {
|
|
278
|
+
module: shaderModule,
|
|
279
|
+
entryPoint: 'fragmentMain',
|
|
280
|
+
targets: [{ format: 'rgba16float' }],
|
|
281
|
+
},
|
|
282
|
+
primitive: { topology: 'triangle-list' },
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
this.bindGroup = device.createBindGroup({
|
|
286
|
+
label: 'Fog Bind Group',
|
|
287
|
+
layout: bindGroupLayout,
|
|
288
|
+
entries: [
|
|
289
|
+
{ binding: 0, resource: { buffer: this.uniformBuffer } },
|
|
290
|
+
{ binding: 1, resource: this.inputTexture.view },
|
|
291
|
+
{ binding: 2, resource: this.sampler },
|
|
292
|
+
{ binding: 3, resource: this.gbuffer.depth.view },
|
|
293
|
+
],
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
this._needsRebuild = false
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async _execute(context) {
|
|
300
|
+
const { device } = this.engine
|
|
301
|
+
const { camera } = context
|
|
302
|
+
|
|
303
|
+
// Skip if fog disabled
|
|
304
|
+
if (!this.fogEnabled) {
|
|
305
|
+
// Just copy input to output or return input directly
|
|
306
|
+
this.outputTexture = null
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Rebuild pipeline if needed
|
|
311
|
+
if (this._needsRebuild) {
|
|
312
|
+
await this._buildPipeline()
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (!this.renderPipeline || !this.inputTexture || !this.gbuffer || this._needsRebuild) {
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Update uniforms - 256 bytes = 64 floats (matches shader struct layout)
|
|
320
|
+
const uniformData = new Float32Array(64)
|
|
321
|
+
|
|
322
|
+
// inverseProjection (floats 0-15)
|
|
323
|
+
if (camera.iProj) {
|
|
324
|
+
uniformData.set(camera.iProj, 0)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// inverseView (floats 16-31)
|
|
328
|
+
if (camera.iView) {
|
|
329
|
+
uniformData.set(camera.iView, 16)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// cameraPosition (floats 32-34) + near (float 35)
|
|
333
|
+
uniformData[32] = camera.position[0]
|
|
334
|
+
uniformData[33] = camera.position[1]
|
|
335
|
+
uniformData[34] = camera.position[2]
|
|
336
|
+
uniformData[35] = camera.near || 0.1
|
|
337
|
+
|
|
338
|
+
// fogColor (floats 36-38) + far (float 39)
|
|
339
|
+
const fogColor = this.fogColor
|
|
340
|
+
uniformData[36] = fogColor[0]
|
|
341
|
+
uniformData[37] = fogColor[1]
|
|
342
|
+
uniformData[38] = fogColor[2]
|
|
343
|
+
uniformData[39] = camera.far || 1000
|
|
344
|
+
|
|
345
|
+
// distances (floats 40-42) + fogEnabled (float 43)
|
|
346
|
+
const distances = this.fogDistances
|
|
347
|
+
uniformData[40] = distances[0]
|
|
348
|
+
uniformData[41] = distances[1]
|
|
349
|
+
uniformData[42] = distances[2]
|
|
350
|
+
uniformData[43] = this.fogEnabled ? 1.0 : 0.0
|
|
351
|
+
|
|
352
|
+
// alphas (floats 44-46) + brightResist (float 47)
|
|
353
|
+
const alphas = this.fogAlpha
|
|
354
|
+
uniformData[44] = alphas[0]
|
|
355
|
+
uniformData[45] = alphas[1]
|
|
356
|
+
uniformData[46] = alphas[2]
|
|
357
|
+
uniformData[47] = this.fogBrightResist
|
|
358
|
+
|
|
359
|
+
// heightFade (floats 48-49) + screenSize (floats 50-51)
|
|
360
|
+
const heightFade = this.fogHeightFade
|
|
361
|
+
uniformData[48] = heightFade[0] // bottomY
|
|
362
|
+
uniformData[49] = heightFade[1] // topY
|
|
363
|
+
uniformData[50] = this.width
|
|
364
|
+
uniformData[51] = this.height
|
|
365
|
+
|
|
366
|
+
device.queue.writeBuffer(this.uniformBuffer, 0, uniformData)
|
|
367
|
+
|
|
368
|
+
// Render fog pass
|
|
369
|
+
const commandEncoder = device.createCommandEncoder({ label: 'Fog Pass' })
|
|
370
|
+
|
|
371
|
+
const renderPass = commandEncoder.beginRenderPass({
|
|
372
|
+
label: 'Fog Render Pass',
|
|
373
|
+
colorAttachments: [{
|
|
374
|
+
view: this.outputTextureView,
|
|
375
|
+
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
376
|
+
loadOp: 'clear',
|
|
377
|
+
storeOp: 'store',
|
|
378
|
+
}],
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
renderPass.setPipeline(this.renderPipeline)
|
|
382
|
+
renderPass.setBindGroup(0, this.bindGroup)
|
|
383
|
+
renderPass.draw(3)
|
|
384
|
+
renderPass.end()
|
|
385
|
+
|
|
386
|
+
device.queue.submit([commandEncoder.finish()])
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async _resize(width, height) {
|
|
390
|
+
this.width = width
|
|
391
|
+
this.height = height
|
|
392
|
+
this._needsRebuild = true
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
_destroy() {
|
|
396
|
+
if (this.outputTexture) {
|
|
397
|
+
this.outputTexture.destroy()
|
|
398
|
+
this.outputTexture = null
|
|
399
|
+
}
|
|
400
|
+
this.renderPipeline = null
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Get the output texture (fog-applied HDR)
|
|
405
|
+
*/
|
|
406
|
+
getOutputTexture() {
|
|
407
|
+
// If fog is disabled, return input texture
|
|
408
|
+
if (!this.fogEnabled || !this.outputTexture) {
|
|
409
|
+
return this.inputTexture
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
texture: this.outputTexture,
|
|
413
|
+
view: this.outputTextureView,
|
|
414
|
+
sampler: this.sampler,
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export { FogPass }
|