AI辅助将Mac 3D鱼缸屏保搬到Windows:跨平台渲染与鱼群模拟
发布时间:2026/10/19 2:42:12 锦皓数字建站

前阵子我把主力开发机从 Mac 切到了 Windows。系统、快捷键、终端这些两天就适应了真正让我别扭了很久的是桌面那块区域——Mac 上那个 3D 鱼缸屏保一群灯鱼在深色水面下游动偶尔追尾、顶气泡看习惯了之后Windows 这边放什么静态壁纸都像白墙。我花了一个周末找替代品结果不太理想。不是收费贵就是鱼的种类少得可怜或者干脆是录好的视频循环播放——鱼永远按同一条路径游看五分钟就穿帮。于是我冒出一个念头既然现在 AI 编程助手这么能打我能不能自己把 Mac 下的 3D 鱼缸搬到 Windows 来这个项目没什么商业目的纯粹是为了让工作机桌面活过来。但它涉及的问题一点也不简单跨平台渲染差异、水族箱氛围搭建、鱼群行为模拟、性能调优每一步都有坑。这篇就完整记录一下从选型、搭建到踩坑的全过程。如果你手上也有某个平台独占的小应用、小效果想搬到另一个平台的需求这篇的迁移思路和 AI 辅助开发的方法论应该能直接用。1. 起因这台 Windows 工作机上一直缺一块活的水族箱1.1 替代品为什么全部失败可能有人会说直接去找个 Windows 版的 3D 鱼缸屏保不就行了我试了一圈问题集中在三类收费的做得确实精细但 UI 老旧订阅制想关个水草密度要去翻英文配置文件体验像是在用 2010 年的共享软件。免费的要么鱼少要么玻璃缸体反射是贴死图鱼游过去能从画面反光里看出抠图边缘非常出戏。所谓动态壁纸直接就是视频循环鱼永远走同一条路线而且视频 4K 片源动辄几个 GB纯粹是拿硬盘换氛围。这里有个关键问题我要的不是会动的画面而是能交互、每时每刻都在随机变化的水族箱。鱼要自己选择游动路线偶尔聚群偶尔散开鱼尾巴摆动的频率要跟游速对得上水面和缸底的光斑得跟着水的扰动走。视频循环永远做不到这些。1.2 项目验收标准在动手之前我给自己定了几个硬指标避免做成一坨看上去像那么回事但处处穿帮的半成品指标目标值说明同时活跃鱼数20 条以上低于这个数缸里会显得很空帧率60 FPS 稳定4K 分辨率下窗口化运行GPU 占用不超过 30%不能让鱼缸吃掉游戏份额鱼群行为自然可信随机聚散路径不重复环境细节气泡、水草摆动、焦散光斑缺了这些就是个塑料水箱安装包体量200 MB 以内独立应用不要让我去装运行时这个验收表后来帮了大忙。每次性能调优或者功能迭代我都会拿它过一遍有一条不满足就继续改。1.3 为什么敢让 AI 参与这个项目说实话我的图形学基础停留在会用 Three.js 摆几个物体的水平。真要自己从零写一套鱼缸渲染光是水面折射和鱼群行为就够啃两三个月。但 AI 辅助开发的模式这几年已经很成熟了我给自己定的用法是AI 负责搭结构和翻译伪代码我负责人体理解和对最终效果负责。换句话说AI 是经验丰富的实习生我依然是那个签字上岗的负责人。遇到拿不准的参数我让它给出为什么是这个值的推导过程再拿实时画面去验证而不是无脑接受。这个分工在后续开发中反复被验证是对的——AI 帮我省掉的 70% 时间是查文档和调试编译而我花掉的 30% 时间几乎全在调看起来自然的参数上。2. 方案选型三条路走下来只有一条值得继续2.1 直译原始代码最先被否决的方案说到搬到 Windows我的第一反应是去翻 Mac 上原版鱼缸用的渲染引擎把 OpenGL/Metal 代码直译成 DirectX。这个想法刚冒头就被自己按死了。原因很实在一份生产级别的渲染代码背后往往挂着大量平台特性调用Metal 的 Tessellation、macOS 的图层合成、MetalFX 画质增强这类搬运过程中任何一环对不上排错都是按天计算的。我自己查了一下午资料发现光是纹理格式兼容这一项就能写一篇论文果断放弃。提示跨平台移植的第一原则是行为等价优先代码等价靠后。不要纠结原项目用了什么要盯着最终画面看起来是什么。2.2 游戏引擎导出学习成本比想象中大第二条路是用现成的跨平台游戏引擎把鱼缸做成一个场景导出 Windows 包。Unity 和虚幻都不缺水面和鱼类资源商店理论上扔进去拼一拼就行。实际试了才发现另一座山引擎编辑器本身的复杂度。我要的鱼缸效果很轻但为了导出我需要的轻薄桌面窗口得先学习项目管理流程、场景烘焙、打包配置。一套流程下来学习成本远超做一个鱼缸本身。更麻烦的是引擎默认的水面 Shader 往往是为大场景设计的体积和效果都偏重塞进鱼缸这种尺度里反而不自然。2.3 最终选择Web 技术栈 桌面壳最后我选了 Electron Three.js整个渲染跑在 WebGL2 上。这个选择不是因为它多高级而是因为它和 AI 辅助开发的配合度最高Three.js 在开源社区的存在感极强AI 训练数据里相关代码量巨大生成出来的代码很少出现看似合理但 API 不存在的情况。浏览器渲染和桌面平台的差别被框架抹平了大半我写一遍渲染逻辑Mac 和 Windows 都能跑。调试的时候甚至可以先用浏览器直接打开页面调 Shader确认效果再拷贝进桌面壳。鱼缸本质上是小场景、少物体、高感官密度WebGL2 的负载完全扛得住没有必要动用游戏引擎这种重武器。如果你也想去移植一个视觉效果类应用我给的建议是先问这个效果的核心是渲染逻辑还是平台集成能力如果是前者Web 技术栈几乎是当前时代性价比最高的答案。3. 先把水缸立起来水体、灯光、底床的搭建细节3.1 水体不是蓝色半透明盒子很多新手做水域第一反应是拉一个半透明蓝色球体或者立方体。事实证明鱼缸里的水感不是靠颜色而是靠折射 散射 衰减三层叠出来的。我最终的水体方案是一面覆盖视口的透明平面 一套自定义材质。透明平面上实时计算菲涅尔效果垂直看的时候透明度高一点侧着看的时候反射强一点。水的颜色则交给深度雾来控制——我写了一个简单的雾化函数让水体深处的颜色自动从浅蓝过渡到深蓝黑。这样 40 厘米深的缸底看起来自然幽暗而不是整缸水一个颜色。这个效果如果用一句话概括水的颜色取决于你看多深而不是水的整体透明度。这跟真实鱼缸的视觉逻辑是一致的。3.2 灯光方案克制是美德一开始我脑子热给缸里挂了 5 个点光源想让水草和鱼鳞都闪闪发亮。结果开起来画面一片惨白——多重光源叠加导致亮部过曝鱼的轮廓被光晕吃掉了。后来我看着 Mac 原版的效果反思人家缸里其实只感觉到一处主光光源位置在斜上方模拟的是房间顶部灯具打下来的感觉。我照样抄作业改成一个方向光作为主光从斜上方 45 度打下来负责在鱼和底砂上产生清晰的阴影。一个很弱的环境色补充保证鱼肚子的暗部不至于死黑。一个强度极低的背光用来在水草边缘勾出轮廓让画面有深度。这个改动带来的画面提升立竿见影缸里立刻分出明暗层次鱼游过时身体的受光面、阴影面都清晰可见。所以别迷信光源数量缸内有限空间里1 个主光 2 个辅助光的组合已经是上限了。3.3 底砂、石头与水草都是程序化生成的这段最能体现 AI 辅助的效率。我在对话式 AI 工具里描述需求生成一个 Three.js 函数创建圆盘状底床顶点加入 Perlin 噪声扰动从中心向边缘颜色渐变形成沙地效果。它十几秒就给出了完整代码我拿过来一跑沙地的随机起伏和颜色过渡就都是对的了。石头用了另一套逻辑随机生成 6 到 9 个顶点的不规则多面体再往上叠加两次细分平滑得到圆润但不规则的卵石形状。水草则是一根根贝塞尔曲线配合顶点着色器让每根草叶在时间里做低频来回摆动模拟水流带动。程序化生成的优势是所有元素都不是美术画出来的每次启动位置略有不同而且包体几乎不占空间。这一部分把安装包控制在 200MB 以内起到了决定性作用。4. 让鱼游得像鱼路径、群体行为与尾巴动画的结合4.1 为什么不给鱼一个终点让它直游鱼缸里最违和的画面就是鱼不停的直线折返——像遥控车。真实的鱼在水族箱里不是这样它们沿着平滑的弧线转弯游到某个区域后会停顿、悬停、再出发。我给每条鱼动态生成了一条 Catmull-Rom 曲线鱼沿着曲线每一帧向前挪动一段距离。曲线不生硬的关键在于控制点的密度转弯处放密一点直线段放疏一点。鱼的朝向则跟速度方向做插值——转向时头部会微微延迟而不是瞬间对齐路径切线。这三行左右的代码效果上撑起了 90% 的像鱼感// 鱼沿着 Catmull-Rom 曲线前进速度固定 const pos swimCurve.getPointAt(swimDistance) const tangent swimCurve.getTangentAt(swimDistance) // 朝向并不直接等于路径切线而是每帧插值逼近 fish.quaternion.slerp( new THREE.Quaternion().setFromUnitVectors(fishForward, tangent), 0.08 )4.2 简化版 Boids让鱼群有聚散感鱼群行为我用了一个轻量化的 Boids 模型分离、对齐、聚合三力模型但和教科书写法做了两点改动全局管理者我称为引导点沿着一条大范围路径缓慢移动鱼群在常规路径之外会去靠拢引导点于是天然形成了鲨鱼游过小鱼跟随的群落感。分离力只在鱼间距小于某个阈值时才激活避免鱼群动不动就弹开破坏行云流水的视觉。实际调参过程中鱼碰到缸壁如何响应是最磨人的。物理反射会让鱼像弹球一样弹回来极其生硬。我的解决方案是鱼在接近缸壁前约四分之一身位处就触发预判转向——路径规划算法里增加一个墙前偏移量让曲线自动绕开边界。4.3 尾巴摆动的相位同步这是最容易被忽略但最影响观感的细节。鱼游动时尾巴摆动频率和游泳速度是正相关的游得快尾巴扇得快紧急转弯时尾巴还会猛地甩一下。我实现的逻辑很朴素尾巴的摆动角频率 游动速度 / 当前身长系数摆动相位则跟路径距离挂钩。每帧将相位值传给鱼尾骨骼的 shader 属性让尾巴做正弦摆动。转弯时额外的摆动幅度叠加到角落变量里产生甩尾视觉。有一点必须提醒鱼的摆动幅度和频率不是越大越好。刚调完时我把摆动幅度设得很大鱼缸里每条鱼都像溺水抽搐后来把幅度压到原来的 40% 才看着正常。这个 40% 的经验值直接记录在了参数表里AI 生成的初始值几乎都是偏大的。4.4 AI 在这个模块里到底干了什么鱼群行为这个模块是我整个项目里和 AI 协作最有价值的环节。我负责描述鱼要有追逐感、要成群但不死板、转弯要平滑它负责把这些描述拆成可执行的函数结构曲线生成器、Boids 力累积器、转向插值器。在此基础上我再微调参数和边界条件。如果没有 AI我大概会在把抽象想法变成代码骨架这一步卡上一周有了它我一天内就拿到了能跑的最小实现。但同样地AI 给的四五版转向算法全都存在壁面穿透问题一个能直接用的都没有。它擅长的是把话说全不擅长的是在真实场景中验收——所以这笔账我个人觉得很划算。5. 水面、焦散与气泡氛围细节决定鱼缸真假5.1 水面波浪顶点挪一挪法线算一算水面我用了 128 x 128 的平面网格每一帧在顶点着色器里叠加两组正弦波频率和相位错开形成两个方向的水波干涉。关键是每帧必须根据位移重新计算法线方向否则光照打在水面上会是死的。法线计算如果放在 CPU 做每帧消耗巨大。正确做法是在着色器里求相邻顶点的偏移差值来恢复法线方向。Three.js 的面片材质默认没做这件事我花了点时间在 onBeforeCompile 回调里注入自定义顶点代码。严格说这算不上高级水面渲染但配合前文提到的菲涅尔透明层画面已经很像那么回事了。有水波、有折射、有反射水面的动态感直接翻倍。5.2 反射不做实时 CubeMap 的真实原因很多教程讲水面必然推实时反射把场景渲染到一个立方体贴图然后水面采样反射。我在 Mac 原版上仔细观察了很久发现它的反射其实非常有限——主要反射的是顶部光照和缸内物体的模糊身影。这意味着实时 CubeMap 是严重的性能浪费。鱼缸里物体的数量很少而且视觉上一团模糊更接近真实。我最终选择了预烘焙环境贴图加模糊采样反射只用来增加水体表面的层次感不做精确倒影。实测在 4K 分辨率下这个选择让 GPU 占用低了 8% 左右画面效果几乎没有折扣。5.3 焦散光斑用低分辨率贴图骗眼睛焦散Caustics是鱼缸氛围的灵魂。阳光穿过水面在水底和鱼身上投下亮斑这些亮斑会随着水面波动不断扭曲变形。最省的做法是使用一张动态无缝噪声纹理通过投影映射的方式投到地面和鱼身上纹理坐标加上时间偏移让光斑流动。为了避免高分辨率画这种噪声烧 GPU我把焦散贴图降低到了 256 x 256 分辨率再用双线性插值放大。因为焦散本身就是模糊光斑降低分辨率后视觉差异可以忽略。这个光斑一定不能只投在底砂上必须同步投给鱼的身体。我一开始只做了底砂焦散鱼游过去就像一束光照穿了它极其穿帮。把焦散材质单独抽出来对鱼做第二层投影后终于实现了鱼从光斑下游过身体被水纹光影掠过的自然感。5.4 气泡与残影细节里的生命力气泡系统用粒子实现每秒钟在 4 到 8 个随机位置发射粒子束。气泡的模拟逻辑包括从缸底竖直上升、上升途中做正弦横向偏移、到水面附近变大然后淡出、消失时留一点涟漪效果。为了避免气泡看起来像помехи到满天星我把同一时刻活跃的气泡总量限制在 50 颗以内并且让发射位置集中在假想过滤器出水口那一侧——多数真实鱼缸的气泡本来就集中在一角。水草摆动再补充一句这个动效不是骨骼动画而是顶点着色器按时间偏移幅度从根部向叶尖递增。AI 给了我一个巨大的初始摆动幅度我调到 0.2 倍才算自然。这类数值看起来不突兀、但实际差很多的经历多了之后我养成了新习惯凡是 AI 给的幅度类参数一律先除以十再观察。6. 搬到 Windows 之后踩到的坑GPU 调度、DPI、打包6.1 双显卡电脑默认用核显水面噪点告急最开始的版本在 Mac 上一路顺畅信心满满地打包到 Windows 一跑水面全是明显的噪点和锯齿。查了半天发现Windows 这台机器虽然独显性能不差但系统默认让 Electron 应用跑在核显上WebGL 的硬件能力被大幅削弱。解决办法有两步在应用启动命令里加入 switch 参数强制 Electron 使用高性能 GPU。在 Windows 系统的显卡设置里把应用手动指定为高性能模式。做完之后再开水面噪点立刻消失帧率也从浮动 35 左右稳定到了 58 以上。注意即便强制指定了独显也要留一个环境变量开关方便在出现兼容性问题的机器上回退到自动模式。别把性能优化写成死配置。6.2 Windows 缩放 125%/150% 下画面发糊macOS 的 Retina 缩放和 Windows 的 DPI 缩放机制不同导致同一套 Canvas 在 Windows 上存在发糊问题。核心原因是 Electron 窗口的 DPI 感知没有正确打开渲染尺寸被系统当成了缩放后的物理尺寸逻辑像素和物理像素对不上。修复方式是配置窗口选项中的高 DPI 支持同时在窗口内容加载后强制设置缩放因子为 1.0保证渲染粒度跟物理像素一一对应。这个坑其实不大但排查起来特别费时间因为画面是处处模糊你不会第一时间联想到 DPI 而不是渲染错误。6.3 electron-builder 打包安装包被杀软误报Windows 下打包首选 electron-builder但在实际使用中有一个非常讨厌的坑NSIS 生成的安装包经常被 Windows Defender 和其他杀软标记为风险文件。原因是 Electron 应用的结构比较特殊加上没有代码签名证书极容易被误判。我的处理方式有三个去掉不必要的 NSIS 配置改用更紧凑的打包模式在 GitHub 上提了 issue 但没抱指望最终主用 portable 免安装版本只有需要开机启动时才跟用户说明手动加入信任区。如果后续真的要做分发一个代码签名证书几乎是绕不开的。6.4 WebGL2 的深度精度问题z-fighting 维修鱼缸的底砂是噪声扰动过的沙地模型表面高低起伏较小当相机离得比较近时沙地和石头的深度精度经常性冲突出现画面闪烁。这个在 Mac 上几乎没有出现过但在 Windows 上用非最佳 GPU 管线时暴露得特别明显。解法是启用 Three.js 的对数深度缓冲区。这个选项在绝大多数显卡上都能保证充足的深度精度代价是少量性能损耗。在鱼缸这个规模的应用里性能损耗可以忽略我直接全局开启闪烁问题从此消失。6.5 全屏切窗口时帧率被锁死后的相位跳跃Windows 下切换显示器模式比如全屏和窗口化切换会导致渲染上下文重建帧率被短暂锁在 30。更麻烦的是水草摆动、鱼尾巴摆动这些依赖时间增量的动画在我用帧数计数而非真实时间差做驱动时切换后会出现一瞬间的大跃迁。修复方式很简单所有动画的时间驱动统一改为 performance.now() 计算真实时间增量乘以速度系数而不是累计帧数。这样无论渲染中断多久恢复时动画都能连续不再产生跳跃感。这个教训也在参数表里单列了一条防止未来其他模块再犯。问题根因处理方式水面噪点默认使用核显强制独显 高性能模式画面模糊DPI 感知未开启窗口高 DPI 配置 SetZoomFactor(1.0)安装包误报无代码签名主推 portable 版备用 NSISz-fighting深度精度不足开启对数深度缓冲区动画跳变帧数驱动而非时间驱动统一改用真实时间差驱动7. 一个多月的调参心得哪些参数最影响像不像鱼缸7.1 参数影响等级表整个项目做完后我对着参数表复盘了一遍哪些参数对最终效果影响最大哪些调了很久其实帮助有限我的判断如下参数影响等级备注转向插值系数极高决定鱼是否像遥控车的关键尾巴摆动幅度极高幅度偏大 溺水抽搐水体雾化深度范围极高决定水缸是否有纵深感焦散贴图分辨率中256 到 512 视觉差异很小气泡上限数量中超过 60 颗画面凌乱水草摆动幅度中需要比 AI 初始小 80%相机晃动幅度低加太多反而头晕7.2 鱼缸体积与鱼数量的经验公式如果你的目标也是做鱼缸我提供一个经验公式可以直接用鱼缸模型体积单位标准瓶大约需要 300 毫升水体积空间每增加 1 条鱼至少增加 50 毫升空间否则画面会拥堵。这是拿我自己项目实测出来的20 条鱼对应 1.2 升以上空间时视觉效果最佳。这里的体积是指鱼可活动的自由空间减去石头、水草占用的体积。空间太挤时Boids 分离力会频繁触发鱼群一直保持弹开状态完全来不及聚散行为就显得机械。7.3 性能账单最终跑起来在 4K 分辨率窗口化场景下性能数据给一个参考20 条同时活跃的鱼整缸三角形预算约 18 万。帧生成时间稳定在 16.8 毫秒附近60FPS 上下。GPU 占用率约 22%CPU 占用约 6%。显存占用约 200MB。安装包 portable 版本约 85MB。最贵的两个部分是水面顶点网格的波浪计算和鱼鳞高光的逐像素采样。如果你希望进一步提高帧率优先把水面网格从 128 x 128 降到 96 x 96视觉损失很小性能提升显著。7.4 我对 AI 辅助开发的最后一个判断整个项目做下来我对AI 帮我把 Mac 下的 3D 鱼缸搬到 Windows这个标题有了更具体的体会。AI 在这里的角色像一个特别勤奋、特别了解开源库的实习生它能帮我把想法变成可运行的代码在搭骨架和查 API两个环节省下大量时间。它能提供多种方案选择比如水面反射技术就给了实时反射、静态贴图、环境贴图三种路线并且附上各自的优劣分析。但它给的所有美术感参数几乎都不靠谱。AI 没有经历过看着屏幕判断画面自然度的过程它无法为一幅画面负责。所以整个项目真正让我花时间的地方不是代码写不出来而是看着屏幕一遍遍调参数直到眼睛觉得舒服。这部分没有任何捷径AI 也很难模仿。它可以把效率提升到 300%但最终那个按下回车、看到鱼缸里游过一群自然生动的鱼的满足感还是得你亲自盯着屏幕验收才会发生。最后分享一个小技巧每次调完一组参数别急着改下一组。截一张图隔 30 分钟再回来看一眼你的眼睛会告诉你过了还是不够。这种延迟反馈比连续盯着屏幕硬调要准确得多。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。