公司官网要展示 PCB 板卡的 3D 模型,让客户在下单前能转着看板子。硬件同事交来的是 立创EDA 专业版导出的 STEP 文件,而网页端要的是能在浏览器里流畅渲染的 GLB。 这篇文章记录整条流水线和每一步的取舍。
总体链路
立创EDA STEP → step2obj.py (OCCT 三角化) → obj2glb.mjs (GLB)
→ gltf-transform 量化 → public/models/*.glb → <model-viewer>分四步,每步一个独立脚本,串起来跑。
第一步:STEP 三角化,为什么不用 WASM
STEP 是 B-Rep 边界表示,浏览器渲染不了,先要三角化成网格。现成方案里 occt-import-js 提供 WASM 版的 OCCT,看似最省事,但实测对这种多层板 STEP 会输出空网格,问题出在它的 WASM 构建裁剪了部分几何内核能力。
最后用的本机 Python + cadquery-ocp(完整 OCCT 绑定)做三角化,
顺带解决另一个需求:按层名配色。
# step2obj.py 核心逻辑(节选)
from OCP.STEPControl import STEPControl_Reader
from OCP.BRepMesh import BRepMesh_IncrementalMesh
reader = STEPControl_Reader()
reader.ReadFile("board.step")
reader.TransferRoots()
shape = reader.OneShape()
BRepMesh_IncrementalMesh(shape, 0.3, False, 0.5, True)PCB 的丝印层、阻焊层、基板在 STEP 里是不同颜色的实体,三角化后按颜色 拆分材质写入 OBJ,后面 glTF 转换时会自然映射成多个 primitive。
第二步:OBJ → GLB
这步没有技术含量,obj2gltf 一行命令,写成脚本只是因为要处理贴图路径和
缺省参数。GLB 比 OBJ+MTL 好在单文件、二进制、带 PBR 材质。
第三步:量化,模型从 40MB 到 4MB
原始 GLB 转出来 40MB 左右,网页加载不能接受。gltf-transform 三连:
npx gltf-transform weld board.glb t.glb
npx gltf-transform prune t.glb t2.glb
npx gltf-transform quantize t2.glb board-q.glbquantize 使用 KHR_mesh_quantization 扩展,把顶点属性从 float32 压到
int16,配合 weld 去重和 prune 清理,体积降到 4MB,视觉上几乎无差别。
<model-viewer> 原生支持这个扩展,不需要额外解码库。
另一个经验:导出前在 EDA 里隐藏小元件(0402 电阻电容之类)的 3D 模型, 源文件体积直接砍半——这些元件在客户视角下本来也看不清。
第四步:网页端
展示组件直接用 @google/model-viewer,自托管它编译好的单文件,
不依赖运行时 CDN:
<model-viewer
src="/models/board.glb"
camera-controls
auto-rotate
shadow-intensity="1"
style="width:100%;height:480px">
</model-viewer>相比自己写 Three.js,model-viewer 把相机惯性、AR 入口、无障碍这些都
处理好了。如果后续要做「点击元件高亮 BOM」这类深度交互,再换 Three.js
也不迟——模型和管线都是复用的。
小结
| 坑 | 解法 |
|---|---|
| occt-import-js WASM 三角化空网格 | 本机 Python + cadquery-ocp |
| GLB 体积过大 | weld + prune + quantize 三连 |
| 源文件含设计 IP | STEP 留在私有目录,只有 GLB 进 public |
| CDN 依赖 | model-viewer 自托管 |
整条管线写成三个脚本,换新板子就是「导出 STEP → 跑脚本 → 加一条记录」, 非前端同事也能自己发新版。