design.md 8.5 KB

Context

本项目里的动态组件需要以 tresjs/three 的方式运行在 canvas 渲染链路中,并通过 src/dcsConfig.ts 暴露默认参数和调参面板。内部流动液体组件相比已有烟雾或矩形跑光组件更复杂,它既包含容器几何,又包含液体主体、液面波动和泡泡粒子等多个视觉层,还需要在矩形、圆柱和壶形预设三类容器之间保持统一的参数体验与实现结构。

用户已确认本次只做“视觉仿真”,不引入真实流体物理模拟,也不支持任意自由形状容器。另一个新增约束是:组件代码不能堆在 src/components 根目录,必须拆分到 src/components/<插件名>/ 目录下;同时 shader 代码仍需遵守项目规则,拆分放在 src/shader/<插件名>/ 目录下。

Goals / Non-Goals

Goals:

  • 提供一个支持矩形容器、圆柱容器和壶形预设容器的三维内部液体组件。
  • 支持统一控制容器规格、液位高度、液面波动、颜色渐变、密度成色和透明表现。
  • 支持独立气泡层,并提供气泡启用、数量、大小随机范围和上浮速度等参数。
  • 支持基于组件局部坐标系的 X 与 Z 方向垂直裁切,用于调节外壳可视化范围,便于观察内部液体。
  • 让组件、shader、默认参数和调参配置完整接入现有动态组件服务,并保持目录结构清晰可维护。

Non-Goals:

  • 不实现真实流体模拟、碰撞反馈、晃动容器后的动态液面守恒等物理行为。
  • 不支持矩形、圆柱、壶形预设之外的任意自定义容器轮廓。
  • 不在本次变更中引入后处理特效、环境折射系统或复杂挂壁/泡沫细节。
  • 不修改 src/views/previewEntry.vue 与 src/views/templateComponet.vue 这类通用预览基础设施。

Decisions

  1. 决策:组件采用“容器外壳 + 液体主体 + 液面层 + 泡泡层”的四层结构,而不是将所有效果塞进单一 mesh 或单一 shader。
  2. Rationale: 这四类视觉元素的几何来源、动画逻辑和调参诉求都不同,拆层后更容易在不同容器形状间复用,也便于分别控制性能和视觉表现。
  3. Alternative considered: 使用一个统一 shader 同时完成外壳、液体体积和泡泡绘制。该方案理论上更集中,但实现复杂度过高,后续维护和调参成本都很大。

  4. 决策:容器形状抽象限制为 rectTank、cylinderTank 和 vaseTank 三种,并让外壳与液体共用同一份形状描述源。

  5. Rationale: 用户已确认只考虑这三种形状。让外壳与液体共享形状参数或轮廓数据,可以避免壶形容器出现外壳和液体内壁不匹配的穿帮问题。

  6. Alternative considered: 允许任意容器形状或分别为外壳和液体维护两套几何。前者范围失控,后者更容易出现显示错位。

  7. 决策:矩形和圆柱容器使用参数化基础几何,壶形预设使用 LatheGeometry 生成外壳与液体轮廓。

  8. Rationale: Box / Cylinder 对规则容器足够直接,壶形预设则天然适合用旋转轮廓生成。这样能在不引入外部依赖的前提下覆盖已确认的形状范围。

  9. Alternative considered: 使用自定义 buffer geometry 完全手写所有容器。该方案灵活,但实现和维护成本明显更高。

  10. 决策:液体主体采用独立 mesh + shader 的“视觉体积感”方案,通过局部高度裁切、纵向颜色渐变和轻微噪声扰动模拟液体,而不是做真实体积流体。

  11. Rationale: 用户接受视觉仿真路线。基于高度、渐变和扰动的 shader 能较稳定地表达液位、密度成色和内部层次,同时比真实流体方案更适合当前仓库的组件范式。

  12. Alternative considered: 真实流体模拟或屏幕空间流体。该方案与本次复杂度和仓库风格都不匹配。

  13. 决策:液面波动单独使用顶部表面 mesh 处理,并按容器形状选择对应的顶部几何,而不是依赖液体主体 shader 同时表现液面。

  14. Rationale: 液面是最容易被用户感知的动效重点,单独处理更容易调节波动速度、振幅和频率,也便于在矩形和平面圆形之间切换。壶形则可根据液位高度映射当前轮廓半径,生成对应的顶部面。

  15. Alternative considered: 只在液体主体里用透明梯度暗示液面。该方案观感偏弱,波动也难以清晰表达。

  16. 决策:泡泡层采用固定粒子池循环上浮,可基于 THREE.Points 或轻量实例化方案实现,并限制活动范围在液体内部。

  17. Rationale: 固定粒子池可以持续复用粒子属性,避免频繁创建销毁;泡泡视觉重点在随机分布、大小差异和上浮节奏,不需要真实流体交互。

  18. Alternative considered: 为每个泡泡创建独立 mesh 并在 CPU 侧频繁重置状态。该方案对象数和更新成本都更高。

  19. 决策:容器外壳可视化范围使用组件局部坐标系中的 clipX 与 clipZ 参数在材质或 shader 中完成裁切,不依赖全局 renderer clipping 配置。

  20. Rationale: 局部裁切只属于该组件自身能力,放在组件内部更符合封装边界,也能避免为了单个组件修改通用预览层或渲染器全局行为。

  21. Alternative considered: 使用 three renderer 的全局 clippingPlanes。该方案会扩大影响范围,且更容易和其他组件或材质行为耦合。

  22. 决策:组件代码入口与辅助逻辑拆分到 src/components/liquidContainerComponent/,主入口使用 index.vue,几何、液面和泡泡逻辑分别拆成独立模块;shader 文件放在 src/shader/liquidContainerComponent/ 中分离管理。

  23. Rationale: 该组件职责较多,单文件实现会快速失控。目录拆分既满足用户新增要求,也便于后续扩展更多容器预设或材质细节。

  24. Alternative considered: 继续沿用单文件组件直接放在 src/components/ 根目录。该方案对简单组件尚可,但不适合本次复杂度。

  25. 决策:预览集成仅通过 src/dcsConfig.ts 新增注册、默认值与配置面板项,不为该组件修改通用预览页面。

  26. Rationale: 这是当前项目的基础规则,也能保证组件能力尽量自包含在组件目录和配置层。

  27. Alternative considered: 为组件在预览视图中添加特殊逻辑。该方案会增加公共层维护成本。

Risks / Trade-offs

  • [Risk] 壶形容器在不同液位下需要同步更新顶部液面半径或截面,几何映射不准确会出现液体穿壳 -> Mitigation: 将壶形预设轮廓数据集中管理,并提供“按高度采样半径”的辅助函数给液体主体和液面层共用。
  • [Risk] 半透明外壳、内部液体和泡泡叠加后可能出现深度排序或混合不自然 -> Mitigation: 分层控制 transparent、depthWrite、renderOrder,优先保证主体观感稳定。
  • [Risk] 过高的泡泡数量、过大的波动幅度或过密的壶形分段会带来性能波动 -> Mitigation: 为关键参数设置合理默认值和上限,并在设计上优先使用固定粒子池与可控几何分段。
  • [Risk] 局部裁切如果同时影响外壳、液体和泡泡,用户可能难以稳定获得“看见内部”的展示效果 -> Mitigation: 先把裁切能力明确聚焦在外壳可视化范围上,液体和泡泡是否联动裁切作为实现时的次级可选项。

Migration Plan

  • 第一步:创建 liquidContainerComponent 变更工件,明确 proposal、design、spec 和 tasks 范围。
  • 第二步:实现按插件目录拆分的组件骨架,建立形状参数、公共类型和几何辅助模块。
  • 第三步:实现容器外壳、液体主体 shader、液面波动层与泡泡层,并在 src/shader/liquidContainerComponent/ 下拆分 shader 文件。
  • 第四步:通过 src/dcsConfig.ts 注册组件默认值、默认 Object3D 与调参面板。
  • 第五步:执行 build 与必要的 lint 验证,确认未误改通用预览层文件。
  • 回滚策略:先从 src/dcsConfig.ts 下线组件,再移除 src/components/liquidContainerComponent/ 与 src/shader/liquidContainerComponent/,恢复到变更前状态。

Open Questions

  • clipX 与 clipZ 是否默认都设为 1 表示不裁切,并按 0 到 1 的局部比例向内收缩外壳可视范围?
  • 壶形预设是否需要首版就提供多个轮廓风格(如“圆肚壶”“细颈瓶”),还是先只做一个主预设加少量规格参数?
  • 液体和泡泡是否要在首版中提供“跟随外壳同步裁切”的开关,还是先固定只裁切外壳以突出内部展示?