AI 3D建模工具: Tripo 3D | Meshy AI

大多数 3DGS 管线从一台相机开始,最后得到一个供你观看的场景。我这条从网格开始,最后得到一个供你射碎的 boss。以下是差别所在。

三个月前(Steam demo 上线前)我还不知道 gsplat 是什么。我一个人做游戏,不会画画,需要一种 boss:它受的伤要能从外壳本身上读出来,而不是从血条上读。高斯泼溅正好是答案——只是不是这个领域通常的用法。

AsterMaster 中的无人机与 boss:每个都是由 6,000 个高斯组成的点云

在 AsterMaster 里——一款用 Godot 4.6 做的、六边形网格上的节奏驱动 roguelite——玩家的无人机和 boss 都不是网格。每一个都是约 6,000 个高斯组成的点云,从 Blender 渲染图训练而来,作为单个实例化网格绘制。boss 被打中时,高斯被向内推、形成弹坑,同一个位置一层层加深,整块碎片作为刚体撕离——全部在顶点着色器里完成。Radiance Fields 十月报道过这款游戏;这篇文章讲的是预告片不会展示的那部分:当地面真值是一张渲染图、预算是事先定死的、结果还必须活在游戏引擎里时,3DGS 会发生什么。

1. 一个「模型形状的粒子系统」

屏幕上的无人机 72 像素高,boss 大约 112 像素。照片级真实不是目标,可读性才是。这个尺寸下的网格死得很无聊:播个动画,溶解,没了。我想要一个凹进去的侧面来讲这场战斗的故事。

高斯点云直接当粒子系统用:位置、朝向、尺寸、颜色都已在模型形状上

从游戏开发者的视角看,一份训练好的高斯点云是非常具体的东西:几千个实例,每个都有自己的位置、朝向、尺寸和颜色,而且已经排成了模型的形状。那就是一个粒子系统。同一份数据结构既画外壳、又承受伤害、还变成碎片。而且它很小:源网格每个 25–55 MB,训练后的无人机约 0.5 MB、boss 约 0.4 MB。

被击中的高斯向内塌陷成弹坑(原文动图)

2. 当地面真值是一张渲染图

从合成渲染图训练,省掉了采集里最难的部分。位姿是精确的,所以不需要运动恢复结构那一步。Alpha 遮罩免费得到。你可以按任意光照渲染任意多的视角。一个无头的 Blender 脚本以 1024² 带 alpha 渲染 120–200 个视角,每个模型大约 30 秒。

它还让你能把相机分布塑造成「这个资产将被看到的方式」。我的游戏相机俯视 32–50°,模型只绕竖直轴旋转,所以训练视角都落在那个仰角带内的一条黄金螺旋上。底面在游戏里永远看不到——这后来让我直接删掉了约 10% 的高斯。

但渲染图有它自己的失效模式,而且很容易漏掉,因为渲染图看上去总是很干净:

高学会了你的光照。 我用三种材质变体训练过同一架无人机:Blender 里烘焙的卡通着色、纯反照率、以及白色穹顶下的漫反射着色。卡通版本与游戏自己的赛璐璐色阶重复叠加;纯反照率出来是一张薄饼;穹顶光照版本保住了体积,又不跟运行时光照打架。在采集里,光照是给定条件;在合成里,它是一个有下游后果的设计决定。

三种材质变体:卡通着色、纯反照率、穹顶漫反射

数据集的上限等于你渲染的那个文件。 有几周时间,boss 一直「缺细节」。我怪高斯预算,怪训练器。真正的原因:数据集是从「游戏就绪」文件渲染的,而那些文件的贴图被一轮旧的优化压到了 128²。2048² 的源文件就在隔壁文件夹里。改用它们重训之后,面板线和牙齿回来了——那是任何预算提升都换不回来的。

128² 贴图与 2048² 源文件重训后的细节差异

完美位姿杀不掉浮点。 在黑色背景下,一根挂在枪管下方的暗色高斯在损失里几乎不花钱,甚至还帮忙给外壳做明暗——一个「代理阴影」。每一步都合成到随机灰底上,就会让任何跑到轮廓外的高斯在每个视角下都很贵。最有效的办法又落在数据集上:把相机带向下延伸到 14°,并把训练视角从 120 提到 200,这样低掠射视角会从下方监督轮廓。轮廓外的高斯从 18 降到重训后的 9,导出时剔除后降到 7。有一课比这些都贵:暗色浮点在黑色展示台上是看不见的。我现在只在灰底上、只按游戏尺寸判断轮廓。

轮廓外高斯数的变化

3. 预算是一个输入,不透明度是二值的

游戏需要每个模型恰好 N 个高斯,而不是「致密化启发式觉得多少就多少」。gsplat 的 MCMCStrategy 正好给这个:它不克隆高斯,而是把「死」的高斯搬到「活」的高斯上,所以数量永远不会超过 cap_max。boss 和无人机给 6,000。一个复制玩家无人机全息诱饵用另一份单独重训、cap_max=1800 的点云——从 6,000 的模型里取前 1,800 个会留下洞;让 MCMC 去分配一个更小的预算就不会。

训练器是在 gsplat 之上约 150 行,用直接 RGB 而不是球谐(风格来自数据)。一次运行是 400² 下 6,000 步,在 RTX 3060 上 21–22 秒;每个 boss 重训一次是几分钟。正是这个短循环,让几十次针对数据集、预算与惩罚项的试验成为可能。

不透明度分布与熵惩罚开启时机的对比

最有影响的技巧是对不透明度的熵惩罚 o·(1−o),只在训练最后 40% 打开。它把不透明度推向 0 或 1,约 98% 的高斯最终完全不透明。一开始就打开,它会跟柔和的轮廓边缘打架,形状永远定不下来。

二值不透明度才是运行时便宜的原因。不透明度高于 0.9 的高斯构成一个不透明内核,按每个高斯可见圆盘拟合出的八边形绘制,用普通 z-buffer:不排序、不 discard、MSAA 可用。剩下约 200 个高斯构成一层薄的半透明外壳,确实需要从后到前的顺序——但因为相机俯角固定、模型只绕 yaw 转,顺序只取决于一个角度,所以我在导出时预计算 64 种顺序。整套东西跑在 Godot 的 Compatibility 渲染器上,不需要计算着色器,一个带 boss 和一群敌人的场景在 RTX 3060 上中位帧时间 4.6–5.4 ms。代价是明确的:你放弃了采集方案那种柔和、视角相关的外观,换回了一个深度缓冲。

4. 把相机看不到的东西烘进去

在采集里,照片是唯一的真值。在游戏管线里,高斯是更丰富来源——网格——的一层缓存,任何能从网格算出来的东西,都可以在训练后烘进高斯:

  • 法线,从最近的面吸附而来。它们不参与损失、不花钱,却能让赛璐璐光照和枪口点光生效。
  • 部件 ID(在网格孤岛上聚类,或手工标注):刚体部件动画与按部件伤害。
  • 环境光遮蔽,用 24 条射线的半球:一张「深度」遮罩,决定缝里透出什么、外壳内部是什么。
  • 自发光,按稀有度挑选。「饱和且亮」在一个红色 boss 上点亮了 4,200 个里的 2,572 个;「远离模型中位颜色」只留下 877 个——眼睛、闪电、下颚。
  • 材质类与分块 ID,给破坏用(见下)。

所有东西都塞进每实例数据的一个 vec4 加颜色 alpha 里。有一个引擎细节值得知道:在 Godot 的 Compatibility 渲染器里,那个每实例数据以 half-float 存储,所以大于 2048 的整数会被静默取整(4482 变成 4480)。项目里每个烘焙通道现在都保持在 255 及以下。

从网格烘焙也意味着继承它的 bug。生成的源网格由上百块开放「板」拼成——Crimson Skull 有 558 块——其中约 40% 绕序朝内。吸附来的法线继承了翻转,弹坑就往外鼓。基于体积的修复对开放壳无效;一段二十行的、按连通分量做面积加权投票的办法有效,翻转了 Skull 56% 的高斯法线。

5. 破坏是高斯回本的地方

破坏把不透明内核当作一个槽位数组。一次命中按攻击类型挑候选——枪击是窄带,爆炸是宽带,轨道炮弹是一列。被打掉的高斯不是被删除:它被向内推,这给弹坑一个暗底,而不是一个能看穿的洞。再打同一个点,这个槽位就在更深一层重生,穿过程序化的分层(底衬、框架、内脏、余烬核心)。这些层是虚构的——生成的网格对它的内部没有真值——对一款游戏来说这没问题。

高斯被逐层推入的破坏效果(原文动图)

加载时建一次的邻接图会抓住与外壳断开的孤岛并把它们撕下来。让破坏变得可读的那一步,是从「高斯」走到「块」:按材质与部件做 k-means++,每块面积相等,于是一块板就按板的方式碎。尺寸是关键数字:25 个高斯(屏幕上约 10 像素)读起来是纸屑;约 100 个高斯(20 像素)才读起来是一块碎片。

按材质与部件分块后的碎裂

动画同样简单。每个模型最多八个刚体部件,用 uniform 数组驱动——每个部件一根骨骼,权重取 0 或 1。训练好的高斯做刚体旋转是精确的,所以翅膀能扇、炮管能后坐,不掉质量、也不需要蒙皮。

6. 高斯没回本的地方

不是所有东西都该用高斯。有一周时间每个敌人都是高斯点云;然后普通敌人改回了网格,配上匹配的卡通着色器。在 60–90 像素上,高斯破坏既不读得出、也不值它的内容成本。高斯留在了伤害可见的地方:玩家的无人机、boss,以及最新构建里的较大炮塔。

两条研究路线也输给了朴素高斯。一个自研的可微超椭球光栅化器做出了可爱的 500 基元模型,但每步 0.7 秒,对比 gsplat 每次运行 21 秒,损失还更差(0.0137 对 0.008),运行时它要么光线步进,要么从片元着色器输出深度。一个稀疏体素格随时间更稳定,但它的 SDF 版本在渲染器里崩了——那里一条射线会穿过 15–20 个重叠单元。它们教给我的东西有一半以烘焙数据的形式活了下来:解析法线变成网格法线,「面板即基元」变成部件 ID,免排序的精确深度变成二值化不透明度。

7. 把高斯当成游戏的交付格式

一份训练好的点云现在供三个消费端使用。游戏加载一个小的自定义二进制,含不透明内核、外壳、64 种排序和烘焙通道。游戏的网站用 Spark 在 three.js 之上在浏览器里渲染同样的模型:那里的无人机是打包进 SPZ 的 16k 高斯点云、229 KB,图鉴敌人每个约 39 KB,Crimson Skull 是 85 KB——而那些「游戏就绪」网格带着它们的 128² 贴图,在近景下根本撑不住。全息诱饵是第三个消费端,有它自己的预算。

这段经历让我对 3DGS 周围的工具留下一个短短的愿望清单:

  1. 把每高斯的自定义属性当一等公民。 KHR_gaussian_splatting 发布候选版标准化了核心。glTF 本来就允许下划线开头的应用专属属性;给部件、遮蔽、材质这些东西一个共享约定,游戏就能用 glTF 交付高斯,而不是像我这样用私有二进制。
  2. 把合成训练当一等路径。 大多数工具默认照片 + 运动恢复结构。从 DCC 工具用精确位姿、alpha 和目标视角分布来渲染,值得拥有自己的预设。
  3. 把预算与不透明度正则做成标准旋钮。 对游戏来说,精确的高斯数量和不透明高斯,比球谐更重要。
  4. 一条不需要计算着色器的引擎路径。 我找到的大多数引擎集成都只是围绕基于计算的排序做的查看器。不透明高斯加一层小的预排序外壳,能跑在朴素得多的渲染路径上。

如果你正在做这个方向的工具,我很乐意交换笔记。

AsterMaster 将于 1 月 12 日在 Steam 上线,demo 现在免费。完整的技术写作——带代码和上面每一个数字的来历——在游戏的 devlog。

透明性声明(与 Steam 页面一致):部分宣传美术与物件图标、少量源 3D 网格、背景音乐以及一些风味文本由生成式 AI 工具产出、经作者筛选;运行时没有任何生成内容。本文描述的高斯管线、着色器与破坏系统是作者自己的工作,其中部分代码是在与 AI 编程 agent 结对下写成的。

落到工具上:这套管线里我们能替你验什么

原文的每一步都在「离线算出更好的一份数据」:先定预算,再从渲染图训,最后把网格能算的东西烘进高斯。这也意味着验收点其实只有两个——源文件对不对,导出结果能不能被看到。

  1. 源网格先体检。 作者踩的坑几乎都在源文件上:128² 贴图、40% 绕序朝内的板、558 块开放壳。你要跑同一条管线,先确认绕序一致、贴图分辨率、壳体是否开放,比调训练参数省时间。
  2. 导出物要能被独立工具打开。 浏览器里的 three.js 预览(作者的第三个消费端)就是这种独立检查:换一个渲染器还能不能看,能看出带法线与不带法线的差别。

导出的 GLB 想快速看一眼,可以把文件拖进 3D 模型查看器:它直接在浏览器里打开,不用装引擎,适合当「交付给别人之前」的那道目视检查。

同主题的历史文章,按顺序读正好是一条线:

专题入口:AI × 3D / 生成式 AI 完全指南。

来源:Training Gaussian Splats From Renders, Not Photos(zakons,Radiance Fields 客座文章,2026-11-11)。

本文由 BimAnt 编译整理,转载请注明出处。

本文属于「AI × 3D」专题。查看专题全部文章 →