AI 3D建模工具: Tripo 3D | Meshy AI
一句话结论:Bambu 3MF 里的多色并没有丢,只是它不按多数 3D 软件期望的方式存——一部分是"对象 → 丝材槽"的分配,另一部分是直接写在三角形上的 paint_color,而后者是一个十六进制序列化的细分树,要从最后一个字符往前读。看懂这个编码,浏览器里也能把多色模型正确渲染出来。
想直接看模型效果?用 3D 模型在线查看器 在浏览器里打开 3MF / STL / OBJ / PLY / glTF / FBX 等格式,核对几何、朝向与颜色。
本文完整编译自 dev.to 文章《How Bambu Studio stores multi-color painting in a 3MF — decoding paint_color in the browser》(2026-10-15),并做中文整理。
一、现象:颜色在,但显示不出来
从 MakerWorld 下载一个多色模型,用 Bambu Studio 以外的几乎任何查看器打开 .3mf,你都会得到一个灰色(或单色)块。但颜色确实在文件里——只是不是多数 3D 软件期望的存法。
背景:Bambu 3MF 的几何用 3MF production extension 分散在 3D/Objects/*.model 里。要先把它 flatten(摊平)、拿到三角形,才谈得上上色。(这也是 PrusaSlicer 打不开 Bambu 3MF 的原因。)
二、Bambu 3MF 里的两种颜色
1)逐对象丝材(Per-object filament)
每个对象(或部件)被分配到一个丝材槽,记录在 Metadata/model_settings.config:
<object id="2">
<metadata key="name" value="Cat"/>
<metadata key="extruder" value="3"/>
</object>
实际颜色在 Metadata/project_settings.config——它虽然叫 .config 其实是 JSON:
"filament_colour": ["#202020", "#C12E1F", "#FF6A13", ...],
"filament_type": ["PLA", "PLA", "PLA", ...]
所以 extruder = 3 表示"用 filament_colour[2]"(注意是 0 起始)。当每种颜色是独立部件时,只做这一步就能得到正确着色的模型。
2)逐三角面涂色(Per-triangle painting)
当有人在 Bambu Studio(或 Orca)里用画笔画时,颜色被写到三角形本身上,作为一个额外属性:
<triangle v1="10" v2="11" v3="12" paint_color="4"/>
<triangle v1="12" v2="13" v3="10" paint_color="8"/>
<triangle v1="14" v2="15" v3="16" paint_color="4C"/>
<triangle v1="16" v2="17" v3="18"/>
不带该属性的三角形,使用对象自己的丝材。 PrusaSlicer 用完全相同的编码,只是换了名字:slic3rpe:mmu_segmentation——因为 Bambu Studio 的这套逻辑是从 PrusaSlicer 的 TriangleSelector fork 出来的。
三、解码那串十六进制
paint_color 不是颜色,也不只是一个槽号。它是一个序列化的细分树(subdivision tree)。
原理:当你在一个三角形的一部分上涂色时,切片器会把该三角形切成更小的三角形并给它们上色。这串字符存下整棵树,每个节点一个十六进制半字节(nibble)。
有两处不寻常:
- 读字符串要从最后一个字符往前读;
- 每个 nibble 打包两个字段:低 2 位是分裂边数,高 2 位是颜色(对叶子节点而言)。
nibble = 0b SS CC (从右往左逐个读)
CC = 低 2 位 = split 边数
SS = 高 2 位 = state(当该节点是叶子时)
split == 0 → 叶子
SS < 3 → state = SS
SS == 3 → state = (下一个 nibble) + 3 // 槽位 ≥ 3 的转义
split > 0 → 该节点有 split + 1 个子节点;递归解码
state 0 表示"未涂色,用对象自己的丝材";state n 表示丝材槽 n。
一些来自真实 MakerWorld 模型的取值:
paint_color |
读作 | 槽位 |
|---|---|---|
4 |
0100:叶子,SS=1 |
1 |
8 |
1000:叶子,SS=2 |
2 |
0C |
C=1100:叶子,SS=3,转义 → 下一 nibble 0 + 3 |
3 |
4C |
转义 → 4 + 3 |
7 |
44043 |
3:分裂成 4 个子节点 → 4,0,4,4 |
混合:1,0,1,1 |
对应的 TypeScript 解码函数:
export function decodePaintState(hex: string): number {
let pos = hex.length - 1;
const next = () => (pos < 0 ? 0 : parseInt(hex[pos--], 16) || 0);
const weight = new Map<number, number>();
const visit = (w: number, depth: number) => {
const code = next();
const split = code & 0b11;
if (split === 0 || depth > 16) {
const state = (code & 0b1100) === 0b1100 ? next() + 3 : code >> 2;
weight.set(state, (weight.get(state) ?? 0) + w);
return;
}
for (let i = 0; i < split + 1; i++) visit(w / (split + 1), depth + 1);
};
visit(1, 0);
// 占比最大的颜色胜出
let best = 0, bestW = -1;
for (const [s, w] of weight) if (w > bestW) { best = s; bestW = w; }
return best;
}
四、作者做的简化(以及为什么)
忠实的渲染器会重建细分后的三角形并逐个上色。但对一个查看器来说,不需要这么做。作者的做法是:给每个原始三角形,取覆盖它最多的那个叶子状态的颜色,每级分裂时子节点等权。
这是近似,但数据支持它够用:
- 在测试的模型上(221,124 个三角形、7 种丝材),只有少部分三角形被分裂,大多数是
4/8这样的单一叶子; - 整个文件解码在浏览器里约 60 ms;
- 视觉上只在颜色边界丢掉子三角形细节,因此边界可能略显锯齿。
五、接入 three.js
ThreeMFLoader 会丢弃未知属性,所以作者自己用正则扫 XML(在这个体量下足够快),收集每个对象的 Uint8Array 状态,再把它和 loader 生成的 mesh 对上:
- 按三角形数量 + 首个顶点位置匹配(loader 保持顶点顺序);
- 把几何转成 non-indexed,让每个三角形拥有自己的 3 个顶点;
- 写
color属性:state 0 → 对象丝材,state n →filament_colour[n-1]; - 用白色材质 +
vertexColors: true。
一个坑:three.js 期望 color 属性在线性空间,但丝材颜色是 sRGB 十六进制。new THREE.Color("#C12E1F") 会替你完成转换(自 r152 起颜色管理默认开启);如果你自己解析 hex 字节再除以 255,就跳过了这一步,颜色会发白。
六、给做 3D 打印与查看器的团队
- 多色 3MF 的"单一灰模"多半是查看器方言问题,不是文件损坏:先确认它是否支持 production extension 与 paint_color;
extruder与paint_color是两层机制:前者是对象级,后者是面级;只实现前者,画笔涂的区域会全丢;- 从后往前读、每 nibble 拆两段是这套编码最容易写错的地方;有真实取值表可以对照自测;
- 近似解码对查看器足够,但如果要做切片或统计用丝量,就必须做忠实重建;
- 颜色空间是浏览器 3D 的常见坑:sRGB ↔ 线性转换别自己手写。
常见问题
Q:为什么 PrusaSlicer 打不开 Bambu 的 3MF?几何分散在 3D/Objects/*.model(production extension),需要先 flatten。本编码与涂色机制则与 PrusaSlicer 同源。
Q:paint_color 是颜色值吗?不是。它是细分树的序列化,代表"哪些子三角形属于哪个丝材槽"。
Q:为什么我只看到灰模,但 Bambu Studio 里是彩色的?你的查看器没实现 paint_color(面级涂色),或没读取 filament_colour。颜色数据一直都在。
Q:解码一定要重建细分三角形吗?查看器不必;做切片/用量统计则需要。
来源: - How Bambu Studio stores multi-color painting in a 3MF — decoding paint_color in the browser(dev.to,2026-10-15) - 前作:Why PrusaSlicer can't open your Bambu 3MF…- 3MF 规范:https://3mf.io/
相关阅读:STL 模型格式详解 | STL 模型修复权威指南 | STEP 模型在线查看 | CAD / 制造 / G-Code 完全指南
本文由 BimAnt 编译整理,转载请注明出处。最后更新:2026-10-15