fluid_core 0.1.1

GPU-accelerated fluid simulation core library (wgpu, no winit dependency)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
# fluid_core SDK — 完整 API 文档

> **crate**: `fluid_core` (library)  
> **companion**: `fluid_core_desktop_demo` (example binary)  
> **Rust edition**: 2021  
> **GPU 后端**: wgpu 0.20(Vulkan / Metal / DX12 / OpenGL ES)  
> **窗口系统**: **无依赖** — 接受任何实现 `raw-window-handle` 的窗口

---

## 目录

1. [架构与设计原则]#1-架构与设计原则
2. [快速上手]#2-快速上手
3. [Cargo.toml 集成]#3-cargotoml-集成
4. [类型:`FluidConfig`]#4-类型-fluidconfig
5. [类型:`InputManager`]#5-类型-inputmanager
6. [类型:`RenderContext`]#6-类型-rendercontext
7. [类型:`FluidEngine`]#7-类型-fluidengine
   - [构造]#71-构造
   - [输入 API]#72-输入-api
   - [配置 API]#73-配置-api
   - [参数 Setter 速查表]#74-参数-setter-速查表
   - [驱动 API]#75-驱动-api
   - [查询 API]#76-查询-api
8. [egui 集成模式]#8-egui-集成模式
9. [使用场景示例]#9-使用场景示例
10. [物理管线参考]#10-物理管线参考
11. [FluidConfig 字段完整参考]#11-fluidconfig-字段完整参考

---

## 1. 架构与设计原则

```
┌──────────────────────────────────────────────────────────┐
│  你的应用(fluid_core_desktop_demo / 自定义 crate)                     │
│  ├─ 窗口系统:winit / SDL2 / GLFW / 裸 HWND              │
│  ├─ 事件循环                                              │
│  ├─ egui-winit / 自定义 UI                               │
│  └─ engine.input.* / engine.update / engine.render_*     │
└───────────────────────┬──────────────────────────────────┘
                        │ 仅通过公开 API 访问
┌───────────────────────▼──────────────────────────────────┐
│  fluid_core (SDK crate)                                   │
│  ├─ FluidEngine        ← 门面,唯一入口                  │
│  ├─ InputManager       ← 输入解耦(无窗口依赖)          │
│  ├─ FluidConfig        ← 所有运行时参数                  │
│  ├─ RenderContext      ← GPU 句柄的借用安全快照          │
│  ├─ Renderer (内部)   ← wgpu 设备/管线/交换链           │
│  └─ FluidSim (内部)   ← Navier-Stokes 物理引擎          │
└──────────────────────────────────────────────────────────┘
```

**设计原则**

| 原则 | 实现方式 |
|------|----------|
| **零窗口依赖** | `fluid_core` Cargo.toml 不含 winit,使用 `raw-window-handle` trait |
| **输入解耦** | 引擎不订阅任何系统事件,全部通过 `engine.input.*` 统一驱动 |
| **借用安全** | `render_context()``render_with_ui_split()` 消除 Rust 借用冲突 |
| **配置热修改** | 所有参数运行时可改,分辨率变更自动重建 GPU 纹理 |
| **UI 可叠加** | `render_with_ui_split` 在流体画面上叠加任意 wgpu 渲染 |

---

## 2. 快速上手

```rust
use fluid_core::{FluidEngine, FluidConfig};

// 1. 创建引擎(接受任何实现了 raw-window-handle 的窗口)
let mut engine = FluidEngine::new(
    &window,            // Arc<winit::Window> 或任意窗口包装
    width, height,      // 物理像素尺寸
    &FluidConfig::default(),
).await;

// 2. 每帧循环
loop {
    // 传入输入(来源随意)
    engine.input.pointer_move(0, cursor_x / w, cursor_y / h);

    // 或程序化注入(无需鼠标)
    engine.input.inject(0.5, 0.5, 800.0, -600.0, [0.9, 0.3, 0.1]);

    // 推进物理
    engine.update(dt);

    // 渲染
    engine.render()?;
}
```

---

## 3. Cargo.toml 集成

```toml
# 你的 Cargo.toml
[dependencies]
# 方式 1:在当前仓库内引用(推荐用于本项目 demos)
fluid_core = { path = "crates/fluid_core" }

# 方式 2:跨仓库本地路径引用(按你的目录结构调整)
# fluid_core = { path = "../fluid_core" }

# 方式 3:发布后从 crates.io 引用
# fluid_core = "0.1"

# 窗口系统(SDK 本身不需要,应用层按需添加)
winit = "0.29"
```

`fluid_core` 自身的依赖(无 winit):

```toml
[dependencies]
wgpu             = "0.20"
raw-window-handle = "0.6"
bytemuck         = { version = "1.14", features = ["derive"] }
log              = "0.4"
rand             = "0.8"
egui             = "0.28"   # 仅 egui-wgpu renderer,不含 egui-winit
egui-wgpu        = "0.28"
```

---

## 4. 类型:`FluidConfig`

```rust
use fluid_core::FluidConfig;
```

所有模拟参数的集合,字段均为 `pub`,可直接读写或通过 `engine.config_mut()` 修改。

详细字段见[第 11 节](#11-fluidconfig-字段完整参考)。

```rust
// 使用默认值
let config = FluidConfig::default();

// 自定义初始参数
let config = FluidConfig {
    sim_resolution: 256,
    curl: 50.0,
    bloom: false,
    ..FluidConfig::default()
};
```

---

## 5. 类型:`InputManager`

```rust
// 通过 engine.input 直接访问,无需手动构造
engine.input.pointer_down(id, x, y, color);
```

输入管理器:收集所有外部输入,帧末由引擎一次性消费。
不持有任何 GPU / 窗口句柄,可在任意线程操作。

### `pointer_down(id, x, y, color)`

```rust
pub fn pointer_down(&mut self, id: u32, x: f32, y: f32, color: Option<[f32; 3]>)
```

注册输入点按下(鼠标左键 / 触摸开始)。

| 参数 | 说明 |
|------|------|
| `id` | 唯一标识,鼠标固定传 `0`,多点触摸传系统手指 ID |
| `x` | 归一化 X `[0,1]`,算法:`pixel_x / window_width` |
| `y` | 归一化 Y `[0,1]`,算法:`pixel_y / window_height` |
| `color` | 染料 RGB;`None` = 自动随机颜色 |

### `pointer_move(id, x, y)`

```rust
pub fn pointer_move(&mut self, id: u32, x: f32, y: f32)
```

更新输入点位置。**每帧无条件调用安全**(未激活时不产生流体效果)。

### `pointer_up(id)`

```rust
pub fn pointer_up(&mut self, id: u32)
```

注册输入点抬起(鼠标释放 / 触摸结束)。

### `inject(x, y, dx, dy, color)`

```rust
pub fn inject(&mut self, x: f32, y: f32, dx: f32, dy: f32, color: [f32; 3])
```

**无需鼠标**,直接在指定坐标注入速度+颜色 splat。

| 参数 | 推荐范围 | 说明 |
|------|---------|------|
| `x`, `y` | `[0,1]` | 归一化坐标 |
| `dx`, `dy` | `-8000..8000` | 速度冲量 |
| `color` | `[0,1]` 每分量 | RGB 染料颜色 |

```rust
// 屏幕中心向右上注入橙色
engine.input.inject(0.5, 0.5, 1200.0, -900.0, [1.0, 0.5, 0.0]);

// 沿水平线扫过
for i in 0..20 {
    engine.input.inject(i as f32 / 20.0, 0.5, 300.0, 0.0, [0.2, 0.8, 0.4]);
}
```

### `burst(count)`

```rust
pub fn burst(&mut self, count: u32)
```

在随机位置发射若干随机颜色 splat(等效于按空格键的爆炸效果)。

```rust
engine.input.burst(15);
```

---

## 6. 类型:`RenderContext`

```rust
use fluid_core::RenderContext;
```

```rust
pub struct RenderContext<'a> {
    pub device:         &'a wgpu::Device,
    pub queue:          &'a wgpu::Queue,
    pub surface_format: wgpu::TextureFormat,
    pub width:          u32,
    pub height:         u32,
}
```

通过 `engine.render_context()` 获取,**不持有可变借用**,
专门用于在 `render_with_ui_split` 之前上传 egui 纹理,解决借用冲突。

```rust
// 典型使用模式
let rc = engine.render_context();
for (id, delta) in &textures_delta.set {
    egui_renderer.update_texture(rc.device, rc.queue, *id, delta);
}
// rc 在此释放
engine.render_with_ui_split(|enc, view, dev, q| { ... })?;
```

---

## 7. 类型:`FluidEngine`

```rust
use fluid_core::FluidEngine;
```

SDK 的唯一入口。公开字段:
- **`input`**`pub`):[`InputManager`],直接调用 `engine.input.*`

### 7.1 构造

```rust
pub async fn new<W>(
    window: W,
    width:  u32,
    height: u32,
    config: &FluidConfig,
) -> FluidEngine<'_>
where
    W: HasWindowHandle + HasDisplayHandle + Send + Sync + '_,
```

异步创建引擎实例,初始化 wgpu 设备、交换链、所有 GPU 纹理和 16 个渲染管线。

`window` 可以是任何实现了 `raw-window-handle 0.6` 的类型:

| 窗口系统 | 传入方式 |
|---------|---------|
| winit | `Arc<winit::Window>``&winit::Window` |
| SDL2 | 实现 `HasWindowHandle` 的包装结构 |
| 裸 Win32 | `raw_window_handle::Win32WindowHandle` 包装 |

```rust
// winit
let engine = FluidEngine::new(window.clone(), sz.width, sz.height, &cfg).await;

// 自定义窗口包装
let engine = FluidEngine::new(&my_window_wrapper, 1920, 1080, &cfg).await;
```

---

### 7.2 输入 API

通过公开字段 `engine.input` 直接访问,见[第 5 节](#5-类型-inputmanager)。

```rust
engine.input.pointer_down(0, cx, cy, None);
engine.input.pointer_move(0, cx, cy);
engine.input.pointer_up(0);
engine.input.inject(x, y, dx, dy, color);
engine.input.burst(n);
```

---

### 7.3 配置 API

#### `config() -> &FluidConfig`

返回当前配置的不可变引用。

```rust
if engine.config().bloom { println!("bloom on"); }
```

#### `config_mut() -> &mut FluidConfig`

返回当前配置的可变引用,直接修改字段下一帧生效。

> ⚠️ 修改分辨率字段请用 [`set_config`]#set_config,否则 GPU 纹理不会重建。

```rust
engine.config_mut().curl = 60.0;
```

#### `set_config(config: FluidConfig)` {#set_config}

完整替换配置;若以下字段变化,**自动重建 GPU 纹理**:  
`sim_resolution` / `dye_resolution` / `bloom_resolution` /
`bloom_iterations` / `sunrays_resolution`

```rust
let mut cfg = engine.config().clone();
cfg.sim_resolution = 256;
engine.set_config(cfg);
```

---

### 7.4 参数 Setter 速查表

所有 setter 直接修改对应字段,下一帧 `update()` 生效。

| 方法 | 参数范围 | 说明 |
|------|---------|------|
| `set_density_dissipation(f32)` | `0.0–5.0` | 染料消散速率 |
| `set_velocity_dissipation(f32)` | `0.0–5.0` | 速度衰减速率 |
| `set_curl(f32)` | `0.0–100.0` | 涡度约束(漩涡感) |
| `set_splat_radius(f32)` | `0.001–1.0` | splat 高斯核半径 |
| `set_splat_force(f32)` | `> 0` | 速度冲量乘数 |
| `set_pressure(f32)` | `0.0–1.0` | 压力场每帧保留比例 |
| `set_shading(bool)` || 法线光照 shading |
| `set_bloom(bool)` || Bloom 泛光开关 |
| `set_bloom_intensity(f32)` | `≥ 0` | 泛光亮度乘数 |
| `set_bloom_threshold(f32)` | `0.0–1.0` | 泛光亮度阈值 |
| `set_sunrays(bool)` || Sunrays 体积光开关 |
| `set_sunrays_weight(f32)` | `≥ 0` | 射线强度权重 |
| `set_background_color(r,g,b)` | `[0,1]` | 背景 RGB |
| `set_background_alpha(f32)` | `0.0–1.0` | 背景透明度(0=全透明,1=不透明) |
| `set_paused(bool)` || 暂停状态 |
| `toggle_paused() -> bool` || 切换暂停,返回新状态 |

---

### 7.5 驱动 API

#### `update(dt: f32)`

推进物理模拟一帧,消费 `engine.input` 中所有待处理的输入。

**必须在 `render*` 之前调用。** 暂停时立即返回,不执行任何 GPU 工作。

`dt` — 帧时间(秒),**建议上限夹到 `0.016667`**(约 60fps)防止低帧率数值爆炸。

```rust
let dt = last.elapsed().as_secs_f32().min(0.016667);
engine.update(dt);
```

**帧内执行顺序:**

```
1. 颜色轮转(按 color_update_speed 定期换色)
2. burst splats(随机爆炸)
3. 程序化 splat 队列(inject 的请求)
4. pointer-driven splats(active+moved → 速度冲量)
5. FluidSim::step(dt):curl → vorticity → divergence
   → pressure×N → gradient_subtract → velocity advection → dye advection
6. Bloom 后处理(若开启)
7. Sunrays 后处理(若开启)
```

---

#### `render() -> Result<(), wgpu::SurfaceError>`

将当前帧渲染到窗口表面(无 UI 叠加)。

```rust
match engine.render() {
    Ok(()) => {}
    Err(wgpu::SurfaceError::Lost) => engine.resize(w, h),
    Err(e) => eprintln!("{e:?}"),
}
```

---

#### `render_with_ui<F>(draw_ui: F) -> Result<...>`

```rust
pub fn render_with_ui<F>(&self, draw_ui: F) -> Result<(), wgpu::SurfaceError>
where F: FnOnce(&mut wgpu::CommandEncoder, &wgpu::TextureView)
```

渲染流体画面,并在闭包中叠加自定义 UI。  
闭包在流体 composite 后、帧提交前执行。

**限制**:闭包内无法获取 `device` / `queue`(借用冲突)。  
需要 `device` / `queue` 时,请使用 [`render_with_ui_split`](#render_with_ui_split)。

---

#### `render_with_ui_split<F>(draw_ui: F) -> Result<...>` {#render_with_ui_split}

```rust
pub fn render_with_ui_split<F>(&self, draw_ui: F) -> Result<(), wgpu::SurfaceError>
where F: FnOnce(
    &mut wgpu::CommandEncoder,
    &wgpu::TextureView,
    &wgpu::Device,
    &wgpu::Queue,
)
```

与 `render_with_ui` 相同,但额外传入 `device` 和 `queue`,
使 egui-wgpu 的 `update_buffers` 可在同一闭包内调用。

**标准 egui 集成模式**(见[第 8 节](#8-egui-集成模式)):

```rust
engine.render_with_ui_split(|enc, view, device, queue| {
    egui_renderer.update_buffers(device, queue, enc, &prims, &sd);
    let mut rp = enc.begin_render_pass(&wgpu::RenderPassDescriptor { ... });
    egui_renderer.render(&mut rp, &prims, &sd);
})?;
```

---

#### `resize(width: u32, height: u32)`

通知引擎窗口尺寸变化,自动重建交换链和所有流体纹理。

```rust
WindowEvent::Resized(sz) => engine.resize(sz.width, sz.height),
// 也应在 SurfaceError::Lost 时调用
```

---

### 7.6 查询 API

| 方法 | 返回类型 | 说明 |
|------|---------|------|
| `config()` | `&FluidConfig` | 当前配置不可变引用 |
| `config_mut()` | `&mut FluidConfig` | 当前配置可变引用 |
| `is_paused()` | `bool` | 是否暂停 |
| `size()` | `(u32, u32)` | 渲染目标尺寸 `(width, height)` |
| `device()` | `&wgpu::Device` | wgpu 逻辑设备 |
| `queue()` | `&wgpu::Queue` | wgpu 命令队列 |
| `surface_format()` | `wgpu::TextureFormat` | 交换链纹理格式 |
| `render_context()` | `RenderContext<'_>` | GPU 句柄借用安全快照 |

---

## 8. egui 集成模式

egui 需要在渲染帧内访问 `device` 和 `queue`,这与 `engine` 的不可变借用冲突。
SDK 提供三种工具解决此问题:

```rust
// ─── 初始化(一次性)───────────────────────────────────────────────
let rc = engine.render_context();
let mut egui_renderer = egui_wgpu::Renderer::new(
    rc.device,          // ← 来自 render_context()
    rc.surface_format,  // ← 来自 render_context()
    None, 1,
);
// rc 的借用在此结束

// ─── 每帧渲染(三步)──────────────────────────────────────────────

// 步骤 1:上传纹理变更(字体图集等)
// render_context() 仅不可变借用 engine,与后续调用不冲突。
{
    let rc = engine.render_context();
    for (id, delta) in &egui_out.textures_delta.set {
        egui_renderer.update_texture(rc.device, rc.queue, *id, delta);
    }
} // rc 借用在此释放

// 步骤 2:流体 + egui 一帧合成
// render_with_ui_split 将 device/queue 传入闭包,消除借用冲突。
engine.render_with_ui_split(|enc, view, device, queue| {
    // update_buffers 在闭包内调用
    egui_renderer.update_buffers(device, queue, enc, &prims, &sd);

    // 叠加 egui render pass
    let mut rp = enc.begin_render_pass(&wgpu::RenderPassDescriptor {
        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
            view,
            resolve_target: None,
            ops: wgpu::Operations {
                load:  wgpu::LoadOp::Load,   // Load = 保留流体底层
                store: wgpu::StoreOp::Store,
            },
        })],
        depth_stencil_attachment: None,
        occlusion_query_set:      None,
        timestamp_writes:         None,
        label: Some("egui"),
    });
    egui_renderer.render(&mut rp, &prims, &sd);
})?;

// 步骤 3:释放不再使用的纹理
for id in &egui_out.textures_delta.free {
    egui_renderer.free_texture(id);
}
```

---

## 9. 使用场景示例

### 场景 A:标准鼠标(winit)

```rust
WindowEvent::CursorMoved { position, .. } => {
    let nx = position.x as f32 / window_w as f32;
    let ny = position.y as f32 / window_h as f32;
    engine.input.pointer_move(0, nx, ny); // 每帧无条件调用
}
WindowEvent::MouseInput { state, button: MouseButton::Left, .. } => {
    match state {
        ElementState::Pressed  => engine.input.pointer_down(0, cx, cy, None),
        ElementState::Released => engine.input.pointer_up(0),
    }
}
```

### 场景 B:多点触摸

```rust
match touch.phase {
    TouchPhase::Started   => engine.input.pointer_down(touch.id as u32, nx, ny, None),
    TouchPhase::Moved     => engine.input.pointer_move(touch.id as u32, nx, ny),
    TouchPhase::Ended |
    TouchPhase::Cancelled => engine.input.pointer_up(touch.id as u32),
}
```

### 场景 C:纯程序化动画(无鼠标)

```rust
fn animate_circle(engine: &mut FluidEngine, t: f32) {
    let angle = t * std::f32::consts::TAU;
    let x  = 0.5 + 0.35 * angle.cos();
    let y  = 0.5 + 0.35 * angle.sin();
    let dx = -angle.sin() * 2000.0;
    let dy =  angle.cos() * 2000.0;
    engine.input.inject(x, y, dx, dy, fluid_core::random_color());
}
```

### 场景 D:音频频谱可视化

```rust
fn on_audio_frame(engine: &mut FluidEngine, spectrum: &[f32]) {
    for (i, &amp) in spectrum.iter().enumerate() {
        if amp > 0.05 {
            let x = i as f32 / spectrum.len() as f32;
            let [r, g, b] = fluid_core::hsv_to_rgb(x, 1.0, 1.0);
            engine.input.inject(
                x, 0.85,
                0.0, -amp * 5000.0,
                [r * 0.15, g * 0.15, b * 0.15],
            );
        }
    }
}
```

### 场景 E:网络/UDP 远程坐标驱动

```rust
while let Ok(pkt) = udp_rx.try_recv() {
    engine.input.inject(pkt.x, pkt.y, pkt.dx, pkt.dy, pkt.color);
}
engine.update(dt);
engine.render()?;
```

### 场景 F:螺旋注入(Spiral Demo)

```rust
fn animate_spiral(engine: &mut FluidEngine, t: f32) {
    // 半径随时间线性扩张,并周期性回到中心
    let phase = (t * 0.5).fract();
    let r = 0.05 + 0.35 * phase;
    let theta = t * 7.0;

    let x = (0.5 + r * theta.cos()).clamp(0.02, 0.98);
    let y = (0.5 + r * theta.sin()).clamp(0.02, 0.98);
    let dx = -r * 7.0 * theta.sin() * 1200.0;
    let dy =  r * 7.0 * theta.cos() * 1200.0;

    let color = [
        (t * 1.1).sin().abs(),
        (t * 1.7).sin().abs(),
        (t * 2.3).sin().abs(),
    ];
    engine.input.inject(x, y, dx, dy, color);
}
```

### 场景 G:运行时动态调参

```rust
// 单参数 setter
engine.set_curl(80.0);
engine.set_bloom_intensity(1.5);
engine.set_background_color(0.02, 0.02, 0.08);
engine.set_background_alpha(0.35);

// 直接修改字段(非分辨率字段)
engine.config_mut().velocity_dissipation = 0.1;

// 分辨率变更(自动重建 GPU 纹理)
let mut cfg = engine.config().clone();
cfg.sim_resolution = 256;
cfg.dye_resolution = 2048;
engine.set_config(cfg);
```

### 场景 H:暂停 + 手动单步

```rust
engine.toggle_paused();         // 暂停

// 暂停状态下手动推进一帧
engine.set_paused(false);
engine.update(0.016667);
engine.set_paused(true);
```

---

## 10. 物理管线参考

`engine.update(dt)` 在 GPU 上依次执行:

| 步骤 | 着色器 | 说明 |
|------|--------|------|
| 1 | `curl.wgsl` | 计算旋度 ∇×v → curl 纹理 |
| 2 | `vorticity.wgsl` | 涡度约束力,防止数值耗散 |
| 3 | `divergence.wgsl` | 计算散度 ∇·v |
| 4 | `clear.wgsl` | 压力场预衰减(× `pressure`|
| 5×N | `pressure.wgsl` | Jacobi 迭代求解泊松方程(N = `pressure_iterations`|
| 6 | `gradient_subtract.wgsl` | v -= ∇p,满足不可压条件 |
| 7 | `advection.wgsl` | 速度自平流(半拉格朗日) |
| 8 | `advection.wgsl` | 染料平流 |
| 9* | `bloom.wgsl` | Bloom:prefilter → downsample → upsample |
| 10* | `sunrays.wgsl` + `blur.wgsl` | Sunrays 体积光 + 分离高斯模糊 |
| 11 | `display.wgsl` | 最终合成 → 屏幕 |
| 12 | 调用方提供 | UI 叠加(egui 等) |

*可选,由 `config.bloom` / `config.sunrays` 控制。

纹理格式:速度/染料/Bloom = `Rgba16Float`;旋度/散度/压力/Sunrays = `R16Float`。

---

## 11. FluidConfig 字段完整参考

```rust
pub struct FluidConfig {
    // ── 分辨率(修改时需 set_config() 重建纹理)─────────────────
    pub sim_resolution:      u32,   // 默认 128  — 速度/压力场短边
    pub dye_resolution:      u32,   // 默认 1024 — 染料颜色场短边
    pub bloom_resolution:    u32,   // 默认 256
    pub bloom_iterations:    u32,   // 默认 8    — 下采样层数
    pub sunrays_resolution:  u32,   // 默认 196

    // ── 物理参数(运行时随时修改)───────────────────────────────
    pub density_dissipation:  f32,  // 默认 1.0  — 染料消散
    pub velocity_dissipation: f32,  // 默认 0.2  — 速度衰减
    pub pressure:             f32,  // 默认 0.8  — 压力场保留比例 [0,1]
    pub pressure_iterations:  u32,  // 默认 20   — Jacobi 次数
    pub curl:                 f32,  // 默认 30.0 — 涡度约束强度

    // ── 交互参数 ─────────────────────────────────────────────────
    pub splat_radius:         f32,  // 默认 0.25  — 归一化注入半径
    pub splat_force:          f32,  // 默认 6000.0— 速度冲量乘数
    pub colorful:             bool, // 默认 true  — 启用颜色轮转
    pub color_update_speed:   f32,  // 默认 10.0  — 颜色轮转速度

    // ── 渲染参数 ─────────────────────────────────────────────────
    pub shading:              bool, // 默认 true  — 法线光照
    pub back_color:           [f32;3],// 默认[0,0,0]— 背景 RGB
    pub back_alpha:           f32,  // 默认 1.0  — 背景 Alpha(0 透明,1 不透明)
    pub paused:               bool, // 默认 false

    // ── Bloom ────────────────────────────────────────────────────
    pub bloom:                bool, // 默认 true
    pub bloom_intensity:      f32,  // 默认 0.8
    pub bloom_threshold:      f32,  // 默认 0.6
    pub bloom_soft_knee:      f32,  // 默认 0.7
    pub bloom_color_tint:     [f32; 3], // 默认 [1,1,1] — 光晕偏色
    pub bloom_hdr_power:      f32,  // 默认 1.0 — HDR 指数

    // ── Sunrays ──────────────────────────────────────────────────
    pub sunrays:              bool, // 默认 true
    pub sunrays_weight:       f32,  // 默认 1.0
    pub sunrays_exposure:     f32,  // 默认 0.7
    pub sunrays_decay:        f32,  // 默认 0.95

    // ── Global Tone ──────────────────────────────────────────────
    pub tone_map_exposure:    f32,  // 默认 1.0
}
```

### 字段用途总览(全量)

| 字段 | 类型 | 主要用途 |
|------|------|----------|
| `sim_resolution` | `u32` | 控制速度场/压力场分辨率,影响流体细节与性能。 |
| `dye_resolution` | `u32` | 控制颜色染料分辨率,影响画面清晰度与显存占用。 |
| `density_dissipation` | `f32` | 控制染料随时间衰减速度,越大消散越快。 |
| `velocity_dissipation` | `f32` | 控制速度场衰减速度,越大流动越快停止。 |
| `pressure` | `f32` | 控制压力场每帧保留比例,影响不可压缩稳定性与“回弹感”。 |
| `pressure_iterations` | `u32` | 控制 Jacobi 求解迭代次数,越高散度抑制越好但更耗时。 |
| `curl` | `f32` | 控制涡度约束强度,决定旋涡感和卷曲力度。 |
| `splat_radius` | `f32` | 控制单次注入影响半径,越大涂抹范围越宽。 |
| `splat_force` | `f32` | 控制注入速度冲量倍数,决定拖拽时“推力”强弱。 |
| `shading` | `bool` | 开关法线光照效果,提升体积感。 |
| `colorful` | `bool` | 是否启用输入点自动换色。 |
| `color_update_speed` | `f32` | 控制自动换色频率。 |
| `paused` | `bool` | 暂停/恢复物理推进。 |
| `back_color` | `[f32; 3]` | 控制最终清屏背景 RGB。 |
| `back_alpha` | `f32` | 控制最终清屏背景 Alpha,用于透明窗口合成。 |
| `bloom` | `bool` | 开关 Bloom 泛光后处理。 |
| `bloom_iterations` | `u32` | 控制 Bloom 金字塔层数(下采样/上采样次数)。 |
| `bloom_resolution` | `u32` | 控制 Bloom 缓冲分辨率。 |
| `bloom_intensity` | `f32` | 控制 Bloom 叠加强度。 |
| `bloom_threshold` | `f32` | 控制参与 Bloom 的亮度阈值。 |
| `bloom_soft_knee` | `f32` | 控制 Bloom 阈值过渡柔和度。 |
| `bloom_color_tint` | `[f32; 3]` | 控制 Bloom 颜色乘数,可让光晕偏色。 |
| `bloom_hdr_power` | `f32` | 控制 Bloom HDR 亮度指数,>1 越亮。 |
| `sunrays` | `bool` | 开关 Sunrays 体积光效果。 |
| `sunrays_resolution` | `u32` | 控制 Sunrays 缓冲分辨率。 |
| `sunrays_weight` | `f32` | 控制 Sunrays 强度权重。 |
| `sunrays_exposure` | `f32` | 控制 Sunrays 散射的曝光系数。 |
| `sunrays_decay` | `f32` | 控制 Sunrays 每步衰减率。 |
| `tone_map_exposure` | `f32` | 全局曝光系数,在 Tone Mapping 前应用。 |

> ⚠️ 分辨率字段(前 5 个)修改后,**必须调用 `engine.set_config(cfg)`**> 否则 GPU 纹理不会重建,模拟会以旧尺寸继续运行。

### 参数还原

`engine.reset_to_js_defaults()` 方法可将所有参数一键还原为与原版 WebGL 实现完全一致的值。
这在进行大量参数调整后非常有用。

```rust
// 一键回到初始状态
engine.reset_to_js_defaults();
```