机器仿真技能 是做什么的?
你构建机器的方式,如同产品工作室的摄影师和工程师的讲解:真实尺寸、真实材质、洁净的摄影棚布景,由真实的方程驱动。一句话输入,一个HTML文件输出,一条命令生成短片。
输出始终是:
<name>.html:一个完全自包含的页面(three.js 0.170 来自 jsDelivr,工具包内联)。可直接从磁盘、任意静态主机或 iframe 内打开。- 短片:
node scripts/record.mjs <name>.html out/<name>.mp4 --music freepd:<Category>/<Name>。
首先打开 examples/ 目录。每个示例均由页面顶部的那一句话生成。examples/turbofan.html 是参考示例:开始编写任何代码前,请从头到尾阅读它。
标准
开始之前,请设想观众在看到画面三秒后的感受。它必须已经看起来像真实机器的产品摄影照片:正确的轮廓和比例、能识别为金属的材质、纸张地板上的柔和阴影,无漂浮、无裁剪、无占位符。然后它开始运动,屏幕上的每个数字都是实时计算得出的。
如果以下任意条件为真,则结果未完成:
- 缺少机械师预期会有的零件(燃烧室没有燃油喷嘴,手表没有擒纵机构,发动机没有气门)
- 有部件漂浮、交叉、或在连接处有明显接缝或间隙
- 材质看起来像塑料却应该是金属,或者全部是灰色
- 屏幕上的数字是手动输入的,而非由求解器计算
- 剖切显示的是空心外壳,而非实心截面
- 短片的第一帧不美观
工作流程
1. 研究真实机器(5分钟,不写代码)
在机器脚本顶部的注释块中,根据已知信息(如有需要可查阅网络)记录:
- 真实尺寸(以米为单位):构成轮廓的5到10个主要零件(例如涡轮风扇:风扇直径 2.85 米,长度 4.9 米,短舱最大直径 3.44 米)。
- 零件清单:按装配顺序列出,每个零件附带其材质。目标15到40个命名零件;重复零件(叶片、齿、螺栓、线圈等)单独计数。
- 物理原理:3到6个你将实际计算的方程,包含真实常数和工作点(例如涡轮风扇:布雷顿循环,起飞时N1 100%,OPR 42,T4 1,750 K,约 330 kN)。
- 观众关心的一个核心数值:关键指标(推力、扭矩、节拍次数/小时、流量、功率)。它将显示为大号衬线数字和细条形图。
- 观众想要操控的一个输入:一个滑块(油门、风速、转速、泵速、研磨度)。
- 故障模式:工程师担心的失效及其对求解器的影响(风扇叶片脱落及不平衡载荷、红线转速以上气门浮动、泵失速、过压)。这必须是计算得出的后果,而非动画。
2. 基于模板构建页面
将 kit/template.html 复制为 <name>.html。它包含 import map(固定 [email protected])、字体、一个标记的块(工具包将放入此处),以及你需要填充的四个部分:
const { THREE, Lab, MAT, GEO, heatGlow, clamp, lerp, approach, fmt } = window.LabKit
const lab = new Lab({ prompt, no, title, sub, size, view, cut }) // 1. lab
// 2. 零件 lab.mesh(GEO..., MAT..., { name, pos, explode, xray, count })
// 3. 求解器 lab.hero(...); lab.slider(...); lab.onTick((dt, lab) => { ... lab.setHero(...) })
// 4. 破坏、解释、短片 lab.breakIt(...); lab.how(...); lab.autoFilm(...); lab.start()
复制后立即运行 node <skill dir>/scripts/inline.mjs <name>.html:它会将工具包粘贴到标记的块中,使页面成为完全自包含的文件(模板中的 src="../kit/lab-kit.js" 仅在此仓库内有效)。然后在第二个脚本块中编写你的机器。
3. 几何:真实尺寸的实心体
在机器自身框架内以米为单位工作,Y 轴向上,地板位于 y = 0(除非你设置了 ground)。将 size 设置为最长的真实尺寸;工具包会自动缩放显示,使每台机器在布景中大小一致,比例尺显示真实尺寸。
- 圆形零件(轴、盘、壳体、喷嘴、锥体、整流罩、活塞、表圈):使用
GEO.lathe([[r, y], ...])并闭合轮廓。壁:GEO.shell(profile, thickness),管:GEO.tube(rOut, rIn, h),带倒角的盘:GEO.disc(r, h)。边角保持清晰,曲线自动平滑着色。 - 叶片和导叶:
GEO.rotor({ count, axis, r0, r1, chord, thick, camber, twist, pitch, sweep })构建真实的带弯度和扭转的翼型(NACA 厚度),作为闭合实体并环形排列。使用[root, tip]数组指定弦长、厚度和弯度。 - 齿轮:
GEO.gear({ teeth, module, width, bore, helix, spokes, rim })。在中心距(z1 + z2) * module / 2处啮合两个齿轮,按齿数反比驱动,初始偏移半个齿。 - 管道、软管、导线:
GEO.pipe(points, r)(闭合、加盖)。沿着结构从接口到接口布线,切勿穿过其他零件。 - 紧固件:每个法兰上使用
GEO.bolts(n, R, size, h)。弹簧:GEO.spring(R, wire, turns, len)。盒子:GEO.rbox(w, h, d, r)(始终圆角,不尖锐)。带孔板:GEO.extrude(shape, depth)。 - 质量和惯量来自几何体:
GEO.volume(geo)返回体积和质心,乘以实际密度。
保持整洁的规则:
- 每个零件都是闭合实体(lathe 轮廓闭合,shell 带厚度,pipe 加盖)。这是剖切能绘制实心截面的基础。开放表面在剖切时显示为空心。
- 无可见交叉,无漂浮。 每个零件与支撑它的零件接触(法兰贴合在面上,轴在轴承中运转,立柱接触地面)。近距离检查连接处。
- 细节集中在视线焦点:法兰上的螺栓孔、倒角、边缘、铭牌、燃油管、线束。大平面区域添加接缝、面板线或纹理饰面。
- 为机器提供底座:支架、托架、基座、工作台或带脚的安装座,放置在地板上。现实中机器很少悬浮。
- 预算:最多约 70 万个三角形和 200 个网格。使用
GEO.merge/GEO.ring合并重复的静态零件;保持运动部件分离。
4. 材质:绝非单一灰色
使用 MAT.* 预设;它们携带实测反射率,因此钢、钛、铝、黄铜和铜并排时外观各异。每个预设都接受覆盖参数(color、roughness、clearcoat 等任何 MeshPhysicalMaterial 属性)和 finish(微粗糙度纹理:车削件上的 turned 环纹、brushed 拉丝、cast 铸造斑点、smudge 污渍、knurl 滚花)。
| 用途 | 预设 |
|---|---|
| 轴、盘、机加工件 | MAT.steel、MAT.stainless、MAT.chrome |
| 外壳、铸件 | MAT.aluminium、MAT.castIron、MAT.steelDark |
| 叶片、航空航天 | MAT.titanium、MAT.inconel (+ heatGlow) |
| 接头、导线 | MAT.brass、MAT.copper、MAT.gold |
| 涂漆外壳 | MAT.paint({ color })、MAT.satin、MAT.anodised({ color }) |
| 聚合物 | MAT.plastic、MAT.rubber、MAT.carbon |
| 窗口、水晶 | MAT.glass、MAT.sapphire、MAT.ruby、MAT.enamel |
| 光、火焰、热气 | MAT.glow、heatGlow(material, kelvin) |
像工业设计师一样选择调色板:以金属为主,搭配一两种涂漆颜色,最多一种强调色。热部件根据求解器温度发光(material.userData.heat.value = T)。
5. 求解器:只接受真实数字
编写一个纯函数,根据输入和状态计算你显示的每个数字,使用真实单位和真实常数。使用 approach(一阶滞后:加速、热质量)或 rk4(弹簧、擒纵机构、钟摆)进行动力学积分。然后根据求解器驱动运动:来自求解器的转速驱动轴旋转,流动驱动粒子移动,温度加热金属。
- 一个核心数值:
lab.hero({ label, unit, max })然后每帧调用lab.setHero(value, { state, sub, bad })。state是两三个词(加速中、起飞、壅塞流动)。sub是一行文字,包含2到3个额外数字。 - 一个滑块:
lab.slider({ id, label, min, max, value, unit, ends })。仅当机器确实需要时才添加第二个滑块。 - 标签挂在剖切面上的静态零件点:
lab.label(obj, 'HP turbine', { at: [x, y, z], value: () => '12,979 rpm' })。 lab.how(words, equations)填充“计算方法”:用通俗语言描述模型和代码中的方程。说明哪些部分被简化了。- 如果运动过快无法看清(如 2,700 转/分的风扇),则减慢显示速度并在
how中说明(“转速显示比例为 1:60”)。切勿减慢数字本身的更新。 - 将求解器的工作点与步骤1中记录的真实公开数据核对。如果偏差超过约 20%,则修正模型,而不是强行调整数字。
6. 流动、热量和效果
lab.flow({ paths, count, length, color, parent }):沿真实流道(空气通过管道、血液通过泵、水通过滤芯)的流动线条。根据求解器设置其.speed并根据lab.state.cut设置其.opacity,使其在剖切视图中可见。根据计算量(温度、压力)着色。lab.plume({ radius, length, dir, color: 'rocket' | 'jet', shocks }):带马赫盘的排焰排气流;根据求解器设置其.power。lab.sparks.emit(worldPos, n, speed)和lab.shed(mesh, v, w)用于破坏模式:火花受重力下落并反弹;脱落的零件成为刚体,落在地板上。
7. 模式(工具包自动实现,你负责配置)
- 爆炸:每个
lab.add/lab.mesh接受explode: [dx, dy, dz](相对于父级框架,单位为米)。沿装配轴移动零件,外壳向外移动最远,核心保持在原位。移动路径上不得有任何交叉。 - 剖切:在 Lab 上设置
cut: { a, b, center, reach }。a和b是移除区域的方向:例如a = [0,0,1], b = [0,1,0]移除朝向观察者和上方的四分之一(适用于有轴的零件);b = a移除一半。每个闭合实体自动绘制截面,并带有浅色剖面线。 - X光透视:在外壳上设置
xray: 'shell'使其变为半透明玻璃状并带有细边线,从而在运行时显示内部核心。 - 慢放、标签、暂停自动启用(按键 S、L、Space)。交互部分包括鼠标轨道控制、滑块和按钮。
- 破坏模式:
lab.breakIt({ label, run(lab), reset(lab) })。run改变求解器状态(叶片脱落、阀门卡滞、密封件破损),求解器计算后果。在核心数值上显示bad: true,并伴有火花、碎片或震动。
8. 像评论家一样审视(必需)
渲染并通读你创建的每一张图像。使用以下脚本:
node scripts/shot.mjs <name>.html out/review.png # 第一印象
node scripts/shot.mjs <name>.html out/cut.png --js "lab.toggle('cut', true); lab.toggle('labels', true)"
node scripts/shot.mjs <name>.html out/near.png --js "lab.setView({ az: 70, el: 8, dist: 0.8, target: [x, y, z] })"
node scripts/shot.mjs <name>.html out/film --film 1,4,8,12,17,22,26 # 电影静帧
至少进行四轮审查。每轮包括:整体视图、带标签的剖切视图、爆炸视图、两个连接处特写以及电影静帧。与记忆中真实机器的产品摄影照片进行比较:比例、零件数量、材质、光线效果。列出所有问题,修复问题,再次渲染。仅在列表为空时停止。shot脚本会输出帧率、零件数量和三角形数;保持交互帧率高于30。
9. 短片
lab.autoFilm({ runBeat, runSub, breakBeat, breakSub, sliderTo }) 根据你的机器编写标准的28秒故事,包含编号镜头和编辑字幕:
- 一句话概括整个需求(提示栏自动输入)。
- Claude构建机器(零件按装配顺序飞入,计数器攀升:“84/3,597个零件”)。
- 机器运转(光标拖动滑块;核心数值根据求解器攀升)。
- 剖切(截面、带实时数值的标签)。
- 分解(爆炸视图)。
- 破坏(光标点击破坏按钮;求解器决定失效方式)。
循环播放:最后一个相机关键帧回到第一个。如需自定义短片,使用 lab.film('name', { len, assemble, shots, events }):shots 是相机关键帧 { t, az, el, dist, target, fov },events 是事件 { t, beat: ['07', '粗体句。', '灰色句。'], set: { cut: 1 }, slider: { id, to, dur }, cursor: { to: 'slider:id:0.8' | 'button:break' | [x, y], click: true }, do(lab) {} }。
录制(需要 Node 18+、带 Chromium 的 Playwright、ffmpeg):
node scripts/record.mjs <name>.html out/<name>.mp4 --music freepd:Classical/Easy_Lemon
node scripts/record.mjs <name>.html out/<name>-portrait.mp4 --w 1080 --h 1350
页面以电影模式运行(?film=1):其时钟仅在录制器步进时移动,因此每一帧都是完整的,且每次录制短片完全相同。录制器会检查每张截图,防止无头 GPU 返回的品红色帧,并扫描完成的短片;如果有任何帧为粉色,则以非零状态退出。来自 FreePD 的音乐为 CC0 许可;仍需注明曲目来源。
屏幕上的文字
页面呈现编辑风格,而非仪表板:提示栏、衬线标题(带斜体副标题)、一个大号数字(带斜体单位和细条形图)、一行辅助数字、一个滑块。像优秀图注一样写作:简短、具体、无夸张、带空格的真实单位(338 kN、1,750 K、4.2 L/min),千位分隔。镜头字幕包含粗体句和灰色句。
交付时说明
提供HTML文件路径、短片命令、求解器达到的工作点(与公开数据对比),以及你简化了哪些部分。提供下一台机器的构建选项。