3D Vision(客座讲师:Jiajun Wu 吴佳俊)—— 先把"怎么表示一个 3D 物体"这件事掰开揉碎:点云、多边形网格、参数曲面这些显式表示,与约束函数、距离函数、水平集、体素这些隐式表示各自擅长什么、又各自差在哪;再看深度学习如何一步步接管 3D 视觉:从"渲染回 2D"的取巧办法,到体素卷积与体素 GAN、八叉树,到直接吃点云的 PointNet,到用神经网络学习参数曲面的 AtlasNet,再到 2019 年"深度网络本身就是隐式函数"的思想跃迁;最后是这条线的两大高潮——用可微体渲染从 2D 照片直接训练的 NeRF,与把隐式查询和显式高斯团嫁接起来、快上千倍的 3D Gaussian Splatting,以及"结构、对称、层次、程序与 LLM"的收尾展望
常驻讲师首先介绍客座讲者:Jiajun Wu 是 Stanford 计算机科学系助理教授、Stanford Vision and Learning Lab(SVL)成员,研究方向包括面向多模态感知与机器人/具身智能的场景理解、视觉生成与推理,以及 3D 理解——正是今天的题目。老师开场也提到,自己几年前曾参与合讲这门课,而今年恰逢课程十周年,因此请来了各地的客座讲者。
本讲的定位与前几周很不一样:过去几周讲的是卷积神经网络、Transformer、视觉语言模型,还有 Justin 的两讲生成模型——这些内容有一个共同点:全都活在2D 像素世界里。今天则要先讲一段"离深度学习相当远"的基础——3D 表示(3D representations):一个 3D 物体到底可以怎么写进计算机?然后再看深度学习与 AI 如何改变了 3D 视觉、二者如何在不同的表示上结合,最后落到 3D 生成、重建等具体应用。
问题的起点非常朴素:在 2D 里表示一张图太简单了——加载一个 PNG 或 JPEG 文件,\( 200 \times 200 \) 个像素而已。但 3D 物体呢?它们极其多样、尺度悬殊:既有大楼、树木这样的大型复杂结构,放大之后又满是精细细节。而且 3D 物体不只有几何(geometry),还有纹理(texture)与材质(material)——本讲先只把几何讲透。
即便只看几何,表示方法也多得惊人,大体可以分成两大类:
每种表示都有它适合的任务与几何类型;更重要的是,在深度学习语境下,它们与神经网络的兼容性也各不相同。老师在选表示时给出了一个"考核清单":好不好存储(像素就是一个矩阵,点云却很不规则,"一个函数"又该怎么存?);能不能支持创建新形状(输入可能是一张图,也可能是一段语言描述);支不支持各种操作——编辑、简化(simplification)、平滑、滤波、修复,这些在图像上常做(用语言编辑、用笔刷编辑),在 3D 上如何完成;还要看渲染(rendering)——怎么把 3D 物体变成 2D 像素,以及反过来,3D 视觉的本质就是把渲染过程倒过来:从 2D 图像重建出 3D 物体;最后还有动画(建模 3D 人或动物并让它们动起来),以及贯穿一切的与深度学习方法的整合(形状编辑、渲染、逆渲染 inverse rendering、动画)。
点云大概是最简单的 3D 表示:我只有一堆 3D 点,没有任何连接性(connectivity)。图像是 \( n \times n \) 的像素值矩阵,点云则是一个 \( 3 \times n \) 矩阵:3 是每个点的 \( x \)、\( y \)、\( z \) 坐标,\( n \) 是点的数量。有时还会附带每个点的表面法线(surface normal),这样不仅知道点在 3D 空间的哪里,还知道它朝哪个方向——这种"带朝向的点"有时被称为面元(surfels)。
为什么需要法线?因为一旦要渲染——想看看物体长什么样——你就必须指定光源的位置;而要让渲染看起来真实,就必须考虑从某个方向射来的光如何与这个点交互,这正是表面法线的用武之地。
点从哪来?点云的一大好处是:它是绝大多数 3D 传感器的原始输出格式——深度传感器、3D 扫描仪,甚至现在的 iPhone(通过 ARKit 之类的软件就能扫描 3D 物体)——原始输出仍然是一团点云,之后才需要做配准(register)、融合(fuse)等处理,把它们变成带纹理的物体。也因此点云往往来自扫描仪、常常很吵:要对不同视角的扫描结果做配准、合并、修复,才能得到一份共享的点云。
点云的优点:极其灵活——点可以任意挪动,不受拓扑约束,几乎任何几何都能表示;也适合大型、多样的数据集。但缺点同样明显:
于是自然有人问:怎么捕捉更多信息、把两个环状物区分开?答案顺理成章——记录点与点之间的连接,这就到了多边形网格。
网格仍然把物体表示为一堆点,但同时记录了这些点如何连接:不仅有顶点,还有面(faces)、表面(surfaces)。可以说,这是图形引擎与电子游戏中最广泛使用的 3D 表示——你在游戏里看到的一切,基本都是多边形网格。
但表示"面"让事情复杂了不少:看原始网格的话,每个面的点数可能不一样——三个点、四个点、五个点的面混在一起。这种不规则性给与神经网络的整合带来了大麻烦,尤其在早期大家手里只有卷积网络的时候——CNN 天生假设固定分辨率的规则输入,而这里是一个维度都可变的原始数据。"3D 深度学习起步晚,正是因为大家要先想清楚:怎么让深度学习方法适配这些远不如图像统一的复杂表示。"
尽管如此,网格用得极广,而且可以做到非常复杂:扫描仪取点、融合、跑一遍算法,就能得到巨大的网格——课件上的例子是一尊雕塑:5,600 万个三角形、2,800 万个顶点;更大的还有 Google Earth:万亿(trillions)级三角形,试图表示地球上的所有建筑。
网格的好处还在于它原生支持一整套操作:细分(subdivision)——想要更多细节时,用更多的面去逼近形状;简化(simplification)——需要快速处理时砍掉多余的网格;正则化(regularization)——把不规则的网格整理成每个面都是三角形、大小大致相同的规整形式,便于后续处理。这些性质让网格能很好地支撑各种图形学算法。老师还顺带提到,针对前面点云的均匀采样问题,图形学界同样发展了保证不同区域均匀采样的算法。
第三类表示来自一个观察:物体并非全无规律——看看你的椅子和桌子,到处是直线。设计师在设计它们时,用的往往正是参数化表示(parametric representations):把形状写成一个函数。一条曲线的内在自由度其实只有一维——所以能用 \( y = f(x) \) 表示;更一般地,用一组函数把少数几个变量(物体的内在维数,常常是 2 甚至 1)映射进 3D 空间。
最简单的例子是圆:一种办法是采一堆点(甚至连成线,像网格那样);另一种办法是直接写出参数方程,只需变动一个变量 \( t \)(可以理解为角度),就能扫出圆上所有的点:
3D 同理:球面只需两个自由度 \( u \)、\( v \),经过一组函数就能映射到球面上的每一个点:
再往上,人们设计了更复杂的参数化表示——贝塞尔曲线与贝塞尔曲面(BézIER curves / surfaces):只用少数几个控制点(control points)就能表示非常灵活、光滑的 3D 曲面,捕捉曲面背后的低维结构,还支持细分以获得更细的粒度。
小结一下目前的版图:可以用非参数的方式表示 3D 物体(无序的点集,或它们的连接即网格),也可以用参数化的方式(一个函数,变动少数几个真正对应内在自由度的参数,映射出复杂形状)。而无论点云、网格还是参数曲面,它们都属于显式表示——点直接落在物体上,参数曲面直接映射到物体上的点。
显式表示的优点在"采样":在物体表面上取点极其容易。给我一个用函数 \( f \) 表示的圆环面,想在其表面随机采几个点?简单到夸张——随机丢几组 \( u \)、\( v \) 进函数,算出来的 3D 点保证落在表面上。
但显式表示有一个硬伤:很难判断某个点在物体的内部还是外部。把球写成参数式,采点很容易;可现在我有个查询——点 \( (3/4,\, 1/2,\, 1/4) \) 在 3D 空间的这个位置,它在球内还是球外?老师直言:"这个我都不确定。"而这恰恰是现代方法最需要的操作:神经渲染(neural rendering)类方法要做大量此类查询——这个点在不在物体内?该点的几何或密度(density)是多少?材质是什么?该点的辐射(radiance)或颜色是什么?显式表示对这些查询并不友好。
于是很自然地,人们提出了另一种思路——隐式表示:不直接给点,而是假设物体表面上的点满足某个约束关系。单位球面上的点满足什么约束?\( x^2 + y^2 + z^2 = 1 \)。更一般地写成:
对球来说,\( f \) 就是 \( x^2 + y^2 + z^2 - 1 \)。而对复杂形状——比如一头牛——这个函数可能复杂到根本写不出闭式解(closed form)。那怎么办?老师的回答干脆利落:"我就把它写成一个神经网络。我的希望是神经网络能把它表示出来。"这就是隐式表示与深度学习的第一次握手:用一个网络去充当那个约束函数 \( f \)。(这个想法从几何出发,后来被扩展到纹理、材质、外观的表示。)
用老师课上的数字算一遍"内外测试"。隐式表示的强项正是显式表示的软肋:查询变得极其容易——把查询点送进函数,看输出值的符号。老师现场演示:对单位球的 \( f = x^2 + y^2 + z^2 - 1 \),查询点 \( (3/4,\, 1/2,\, 1/4) \):
输出是 \( -1/8 \),小于零——约定函数值为负则在物体内部、为正则在外部——所以这个点在球内。同一个问题,在参数化表示下让人抓耳挠腮,在隐式表示下一次函数求值就解决了。当然隐式表示也有自己的代价:采样表面点变难了——要找出满足 \( f = 0 \) 的所有 \( (x, y, z) \),等于要"解"这个方程;球的方程高中数学还能解,复杂函数就很难求根了。
如果说内外测试只是"扳回一城",隐式表示还有更漂亮的性质:极易组合(compose)。函数输出毕竟只是数值——把 \( x \)、\( y \)、\( z \) 送进这个函数得到一个值,送进那个函数得到另一个值,然后直接对这些值做算术,就能算出两个物体的并(union)、交(intersection)、差(difference)。套一层逻辑运算就能组合出复杂形状。这正是大量工业设计的底层机制:制造业要加工复杂零件,很多设计正是通过CAD(Computer-Aided Design,计算机辅助设计)模型用简单逻辑运算组合隐式函数完成的。
还能玩出超越逻辑运算的花样,尤其当你用的是距离函数(distance function)时——此时每个点的函数值本身有含义:它表示该点离物体表面有多远(带符号的正负即内外)。距离函数可以直接相加,从而实现形状的平滑混合(blend)。老师给了一个一维的例子:用两个函数分别表示两条竖线(值负的在左边、值正的在右边),把两个函数加起来,得到的自然就是两条线之间的插值;同样的道理推广到 3D,就能把不同形状光滑地融合在一起。距离函数可以任意组合,最终能搭出细节极其丰富的"世界"——老师展示了只用组合距离函数构建的复杂场景:这件事并不容易,但如果你是高手,它的表达能力惊人。
不过组合也有代价:如果一头牛的形状要靠几百个函数做与/或/加减才能算出内外,那每次查询都要重跑几百个函数,太慢了。怎么办?
老师的答案是:预查询(pre-query)。取一个 3D 空间,撒一个 \( 100 \times 100 \times 100 \) 的网格——现在有了 100 万个采样点;对这 100 万个点预先算好它们在物体内还是外、到复杂形状表面的距离是多少,然后把这些值统统存进一个 3D 矩阵。从本质上看它仍然是隐式表示(存的是函数值),但因为已经预查询过了,它被"烘焙"成了一种非参数表示——这就是所谓的水平集方法(level set methods)的思路。物体的边界在哪?找相邻的两个值一正一负的地方——两者之间必有一点满足 \( f(x) = 0 \),那就是表面点。这种方法在 CT、MRI 等医学数据中被大量使用;预查询出的矩阵也便于可视化与显式控制。
再进一步:如果连距离数值都懒得存、只关心内外呢?那就把预查询的结果二值化——函数值为正记 1、为负(在物体内)记 0。由此得到的表示大概是所有 3D 表示里最好理解的一个:体素(voxel)——一个 3D 矩阵,比如 \( 100 \times 100 \times 100 \),每格一个 0/1。像素是 2D 矩阵,体素就是 3D 矩阵,类比天然成立。老师笑称自己绕了一个大圈子才引出体素,但这条路恰恰展示了体素与其他一切表示之间的血缘关系:它是隐式函数查询的离散化、二值化产物。
表示法讲完(老师看了眼表:"我保证这部分不超过五分钟,结果用了二十五分钟"),终于可以谈深度学习了。先对时间线:现代深度学习从 2010 年算起——Geoff Hinton 先在语音识别上做深度学习;2012 年AlexNet 横扫 ImageNet。而这一切都是 2D 的。很自然的想法:3D 怎么做?注意 2012 年还没有 Transformer——手里唯一的武器是卷积网络,如何把它用到 3D 数据上?
一个关键的历史事实:最早把深度学习搬进 3D 数据的,是计算机视觉圈的人,而不是图形学圈的人。视觉研究者的本能是:"我一直跟像素打交道,那最简单的办法就是升维——不处理 2D 矩阵,改处理 3D 矩阵。"于是有了体积卷积神经网络(volumetric CNN):把 2D 卷积换成 3D 卷积。哪种表示支持它?体素——这几乎是最容易想象的做法。
图形学界对此相当不屑:"体素这东西又慢又丑——计算慢、质量比起网格和点云差得远,你们为什么从它开始?"老师的解释很实在:因为像素与体素的类比实在太容易,容易到"只需要改一行代码"——把 2D 卷积改成 3D 卷积,剩下的整套深度学习流水线原样照搬。3D 深度学习就这样从体素起步了。
方法之外,数据集同样重要——正如 ImageNet 成就了 AlexNet,3D 也需要大规模收集数据。
深度学习时代之前,流行的是 Princeton Shape Benchmark(PSB):约 1,800 个模型、180 个类别——类别不少,但平均下来每类只有约 10 个模型,规模实在太小;可在当时这已算"挺大",而且那时几乎没有什么机器学习的成分。2014 年之前的数据集大体如此:模型数至多九千、一万个,却分出极多的类别,每类往往只有 10 个或不到 100 个模型,什么也训练不动。
转折点是ShapeNet——主要由 Stanford 领头(Leonidas Guibas 与 Silvio Savarese 两位老师主导)的大型数据集,收录约 300 万个 3D 模型;实践中大家常用其精简版 ShapeNetCore:约 50,000 个模型、55 个类别,平均每类约 1,000 个(实际并不均衡,椅子远超平均)。老师回忆道:"大家终于可以说,我有几千把椅子了,可以训练深度网络了——以前只有 10 把,什么都做不了。"此后好几年里,大量论文的结果都发表在椅子与汽车上——它们正是 ShapeNet 里最大的类别。
数据规模的下一跃来自 AI2(西雅图的 Allen Institute):Objaverse 与 Objaverse-XL,分别约有 100 万与 1,000 万个 3D 资产,类别丰富得多,平均质量更高,且带纹理。不过要注意:这些是合成数据集。真实数据的收集则艰难得多:早年(2016 年前后)有 Redwood 一类的扫描数据集,约 10,000 次真实世界扫描;更近的工作由 Meta 与 Oxford 牵头(即 CO3D 一类项目):他们付钱请大众采集——把物体放在桌上,用 iPhone 绕着它拍一圈 360° 视频,一条给 1 美元。第一版收集了 19,000 段物体视频,后续版本扩到约 90,000 段视频/物体,仍未突破 10 万。妙处在于:借助 3D 视觉算法的发展,可以从这些 360° 视频重建出物体的 3D 几何与纹理——于是得到了"图像/视频 + 3D"的成对数据。
把 2D 与 3D 的数据鸿沟量化一下。真实 3D 物体数据目前约 10 万量级;而 2D 图像那边,公开的 LAION-5B 有 50 亿张图像——Google 和 OpenAI 内部的数据集必然更大。也就是说,2D 与 3D 可用数据量差着四到五个数量级。老师明确指出:这是 3D 视觉继续前行的最大挑战之一——大家有不同思路,但差距真实存在;好在现在的数据量已经"勉强够"训练一些深度模型了。场景级数据更紧张:ScanNet 一类工作靠人扛着 3D 扫描仪进家、进办公室(甚至来过他们系里)扫描并标注,第一版约 1,500 个房间,第二版(++)也只有约 2,000–3,000 个房间——比物体数据又小一个量级,因为自己扫描永远受限于时间与人力(如今用 iPhone 也能做了,但规模仍上不去)。部件级则有 Stanford 的部件标注(含对应关系与层次)与 PartNet:不仅标部件与语义,还标注可动性(mobility)——比如笔记本电脑可以打开、合上。
有了方法与数据,深度学习要在 3D 视觉上解决哪些任务?老师列了一张版图:
把深度学习用于 3D 的第一个想法甚至比体素更"偷懒":干脆别动 3D 表示——手里有个 3D 形状(网格也好、体素也好),要认出它是什么?把它渲染成图像。在物体周围的不同位置放置相机,渲染出多个视角的图像,于是任务变成了 2D 问题:每个视图各过一个卷积网络,再用某种池化(pooling)方式融合——本质就是图像分类,唯一区别是输入有多个视图。
为什么走这条路?因为在当年,ImageNet 预训练模型已经非常强,而 ImageNet 远大于任何 3D 数据集——任何在 ImageNet 上预训练的模型都有很好的性能。解决 3D 识别问题最省力的方式,就是先渲染回 2D。这套方法在形状分类上确实做得非常好,还能大量借用 2D 预训练模型的文献积累。
但它有软肋:需要投影,而输入可能非常噪声——点云质量不佳时,渲染出来的图也很糟。于是大家开始追求3D 原生(3D-native)的方法:直接在 3D 数据上做深度学习。有意思的是,老师认为"渲染回 2D"的思潮如今又在回潮:图像与视频基础模型强到离谱(老师问台下:你们看到昨天刚发布的 VL3 了吗?),它们的训练数据是 3D 数据的一千倍、一万倍甚至上百万倍——既然如此,也许又该更多依靠图像视频基础模型了。
3D 原生方法的第一站当然是体素:把 2D 卷积网络直接升级成体积卷积网络。课件上第一个例子是普林斯顿 2015 年的工作(即 3D ShapeNets 一类):一个深度信念网络(Deep Belief Network)——生成式网络——内部带 3D 卷积滤波器,能以相对较低的分辨率合成 3D 体素形状。放在十年后的今天看当然粗糙,但在当年已相当惊艳;它还支持条件生成(床、桌子等语义标签条件),而作为生成式网络也顺带能做形状分类。
接下来是老师自己八九年前的作品:既然 GAN 能生成 2D 像素,没有任何理由不能生成 3D 体素——于是直接把 GAN 套在体素上,3D 物体的生成效果居然相当不错。再往后(2018 年,与 CMU 的合作者)是更精巧的扩展:GAN 不仅生成 3D 形状,还把它渲染到 2D——投影出 3D 物体的深度图(depth map),再用一个 CycleGAN 把深度图转换成彩色图像。如此一来,对抗损失同时架在两个维度上:既要让 3D 形状与真实 3D 物体数据不可区分,也要让 2D 图片与真实汽车照片不可区分——3D 生成与 2D 生成同时进行。而且形状、视点、纹理各有独立的潜向量,带来了可控性:可以换视角、换纹理、做插值,甚至把一辆车的纹理迁移到另一辆车的形状上。
体素最大的抱怨是慢:要预采样整个空间,而大量采样点落在空区域或物体内部——全无信息,纯属浪费。改进思路是八叉树(octree)(2019 年前后):仍然是非参数(或者说"非参数化的隐式")表示,但不再以统一分辨率划分空间的每一点,而是递归地、自适应地划分——靠近物体表面的区域用极细的体素,空旷区域或物体内部这种"不关心细节"的地方则用巨大的体素。效果立竿见影:直接用体素时受 GPU 显存限制大约只能做到 \( 64 \times 64 \),而八叉树能做到 256 这个量级——还能用于生成:生成结果看起来仍像体素,但分辨率高得多,因为空间表示得更高效。(算一笔账:从 \( 64^3 \) 到 \( 256^3 \),体素数相差 64 倍,靠均匀网格硬堆是堆不动的。)
体素和八叉树的走红让图形学界坐不住了:"你们全搞错了——为什么要用这种低效又难看的表示?我们明明有点云、网格、样条这些好东西,为什么不用?"挑战也很现实:点是散落各处的,怎么对点做卷积?一点也不显然。于是人们开始认真开发直接作用于点云的深度学习方法——其中最重要的就是 Stanford、Leonidas Guibas 组的 PointNet。
处理点云,网络必须满足两个不变性:
PointNet 的核心思想(老师:这大概是全文最重要的一点,而且简单到惊人):对点的嵌入施加一个对称函数(symmetric function)。先像对图像的每个窗口提特征一样,对每个点单独计算嵌入;然后因为要保证排列不变,融合时用一个对称函数——比如逐维取最大值(max),或者求和(sum);得到聚合嵌入后,再过几层全连接网络,输出分类结果:这团点代表的是椅子还是桌子?写成公式:
其中 \( h \) 是逐点特征提取,MAX 是逐维最大池化,\( g \) 是后续分类头。max 和 sum 天生与输入顺序无关,排列不变性就自动满足了。"就这么简单,却出奇地强大。"后续改进层出不穷:PointNet++;以及把点当作图的节点、把点与点的邻近关系当作边,用图神经网络(graph neural network)处理点云的一大家子方法。
分类任务的损失很好办(交叉熵即可),但生成任务带来一个新问题:怎么比较两个点云?像素/体素都容易——输出图像与真值图像逐像素求差即可;体素生成也就是对 \( 100 \times 100 \times 100 \) 网格做逐格交叉熵。可如果你的输出是 100 个点、真值也是 100 个点,怎么算损失?必须专门设计距离度量。常用的有两个:
这是 Chamfer 距离(Chamfer distance):两堆点(一堆红点、一堆蓝点)——对每个红点,在蓝点集里找最近邻;对每个蓝点,也在红点集里找最近邻;然后最小化所有这些"到另一集合最近点"的距离。第二个是 Earth Mover 距离(推土机距离):对两个点集做二部图匹配(bipartite matching),建立一对一的配对,再最小化所有配对的距离之和:
其中 \( \phi \) 是一一对应的配对映射。这两个度量都可以做成可微的——于是能求梯度、能把点云生成网络直接优化到位。
能处理点、能输出点之后,图形学界又追问:我们还有样条(splines)这么美的表示,特别擅长表达物体表面——而用神经网络生成的体素或点云"总是很丑",永远没有光滑表面。能不能让网络既输出/理解物体、又保住漂亮曲面?这条线的代表是 AtlasNet。
思路:不直接输出一坨点云,而是学习一个变换函数。回想参数化表示:把 2D 的 \( (u, v) \) 空间映射到 3D 空间(比如映射成球面)。球这种简单形状映射函数可以直接写出来(正弦余弦);但复杂物体写不出闭式解——那正好,用神经网络来表示这个函数 \( f \):把 \( u \)、\( v \) 作为输入送进一个 MLP,它执行 \( f \) 的计算、输出一个 3D 空间中的点。而单个变换往往覆盖不了整个物体,就用若干个小网络各学一小块——想象一张纸,可以折很多次,所有折好的纸片拼在一起构成最终的形状。潜变量(latent shape representation)编码是哪个物体,各 patch 网络负责把各自的 2D 参数域折叠贴合到物体表面的一块。
课件对比了同一输入图像下三种表示的重建效果:体素——确实能出个大概,但被有限分辨率卡死;点云——不再受分辨率限制、细节更多,但点是无序的、得不到任何光滑表面;AtlasNet——表面明显更光滑。它的本质是:用神经网络表示"从低维参数空间到高维空间的映射",学多个这样的映射再组合起来,且以 2D 图像为条件。
讲到这儿,老师停下来做了一次很漂亮的反思:深度网络到底在干什么?以 ImageNet 分类为例:它在学一个非常复杂的函数,把输入图像(\( 500 \times 500 \approx 250{,}000 \) 维的像素空间)映射到一个类别标签——输出空间只有 1,000 维,小得很。输入空间大、输出空间小、函数复杂到写不出任何闭式公式(谁也没法用几条公式算出这是猫还是狗)——正因为写不出来,才需要深度网络。
再回头看我们一路做过的 3D 工作——体素、点云、样条——会发现它们并不怎么契合"大输入、小输出、学一个复杂函数"这个范式。那么什么表示最契合?2019 年前后,大家意识到:深度网络本身就是一个隐式函数——那为什么不直接用它来表示 3D 几何的隐式函数?
于是不再"预查询整个空间得到体素、再上 3D 卷积",而是让深度网络替你执行查询:输入一个 3D 坐标 \( (x, y, z) \),输出一个一维结果——该点在物体内部还是外部。这就是从 AtlasNet(学 2D→3D 变换)到深度隐式函数(deep implicit functions)的最后一步。2019 年有四篇论文做了几乎一模一样的事,共同主张是:体素、点云、网格各有优劣,而正途是把查询直接送进深度网络。直到 2025 年的今天,大量工作仍在用同一个想法:网络吃 \( (x, y, z) \)、吐内外判断。
2019 年那"四篇论文"是谁?课件上没有逐一列出名字,但学界公认的同题四作是:DeepSDF(Facebook,学符号距离函数的连续零水平集)、Occupancy Networks(学占据概率)、IM-NET(隐式解码器)与 Deep Level Sets。它们与老师在上一节给出的判据完全一致:以查询 \( (x,y,z) \) 为输入、以标量为输出,绕开一切固定分辨率的离散化。
而且查询的对象不必局限于二值的内外判断:可以问符号距离函数(signed distance function, SDF)——该点离表面多远;可以问密度值(density);再往后,还可以问颜色、辐射(radiance)。你会发现这形式像极了分类:输入 3D 空间中的点,用隐式函数查询它在 3D 空间中的各种性质。人们也尝试过用一组小神经网络表示物体的多个隐式部件,再组合成复杂形状——呼应了前面隐式函数可组合的性质。
一两年之后,故事迎来大跳跃:NeRF(Neural Radiance Fields,神经辐射场)。相对深度隐式函数,NeRF 做了两处关键改变:
体渲染公式来自计算机图形学,几乎没做任何改动——因为它天然(在近似意义下)全流程可微:网络给出每个采样点的密度(可以理解为该点的不透明度 opacity)与颜色,就能算出沿光线方向、该点前方被挡掉了多少光、该点又向这条光线贡献了多少光。
把老师的口头描述翻译成体渲染方程。从相机发出一条光线 \( r \),在其上采一串点,每点由网络得到颜色 \( c_i \) 与密度 \( \sigma_i \);令 \( \delta_i \) 为相邻采样点的间距,则该光线最终看到的颜色是所有采样点颜色的加权求和:
逐项读它:\( 1 - \exp(-\sigma_{i}\delta_{i}) \) 是第 \( i \) 个小段自己"拦下"多少光的概率(密度越高、段越长,拦得越多);\( T_{i} \) 是透射率——光从起点走到第 \( i \) 段之前、一路没被前面所有点拦住的比例,正是老师说的"前方采样点挡掉了多少光";两者相乘再乘上该点的颜色,就是它对像素的贡献。训练时对每个像素把渲染出的 \( C(r) \) 与真实照片比对求重建误差,梯度穿过这整条求和公式回传到网络参数——整个流程里只有近似、没有不可微的环节,于是一组带位姿的 2D 照片就能学出一个 3D 辐射场:几何与外观一并到手。
接下来的自然组合是生成式 NeRF:老师组与一作 Eric Chan、以及 Gordon Wetzstein 组合作的工作(π-GAN,字幕音记为"pigeon"):用一个生成式的神经网络捕捉隐式辐射场与密度,再套上之前"3D GAN + 渲染"的框架——既能渲染 3D 物体、也能生成 2D 图片;可控性一样不缺:换相机视角、换物体身份(保持视角不变)、做插值。而且因为直接从图像学习,不再局限于车、椅这类 3D 数据充足的类别,输出真实感也大幅提升。
NeRF 的后续生态。原版 NeRF 每个场景训一个 MLP、采样成千上万条光线上的点,慢是最大痛点。后续名作包括:Instant-NGP 用多分辨率哈希特征网格把训练从数小时压到数秒;Mip-NeRF / Zip-NeRF 处理反走样与多尺度采样;Ref-NeRF 改善镜面反射;NeuS、VolSDF 把 SDF 表示接进体渲染、让表面重建更准;Pixel-NeRF、Mip-NeRF 360 等扩展少样本与无界场景。老师课上展示的 π-GAN(周期隐式生成对抗网络)则属于"NeRF + GAN"的 3D 感知图像生成一支,同族还有 GRAF、GIRAFFE、EG3D 等。
NeRF 很伟大,但有个老大难:它必须在 3D 空间里采样海量点。它确实不再像体素那样预采样后跑 3D 卷积,但仍像水平集一样要不断撒点、不断查询神经网络——而其中大量查询落在空无一物的空间:网络只会还你一个密度 0,时间却实打实花掉了。这非常慢。
图形学界再次贡献直觉:点与网格这些东西自由漂浮在空间里、极其高效——能不能把两边嫁接起来?既保留隐式表示,又不要固定的采样网格、不要无休止的全空间采样。答案就是 3D Gaussian Splatting(3D 高斯泼溅 / 高斯泼溅,Gaussian splats):
渲染由此变得高效得多。效果如何?老师展示了对比:质量上与 NeRF 相当——PSNR、SSIM 等渲染指标两条线非常接近(老师还专门提醒:图里 \( y \) 轴不是从 0 开始的,有点误导性,但数字确实贴得很近);速度上则是碾压:3DGS 能以约 150 FPS 实时渲染,而 NeRF 渲染一张图片约需 20 秒——按论文的说法,快了约 1,000 倍。原因无他:不再把算力浪费在对空空间的采样与神经网络查询上。
3DGS 的最新进展(截至 2025)。自 2023 年问世以来,3DGS 已长成一棵大树:质量与保真上有 Mip-Splatting(反走样)、2DGS / 3DGS-MCMC 等改进;表示与压缩上有自组织高斯、码本量化,把数 GB 的 splat 场压到几十 MB;几何提取上有 SuGaR、GOF 等把高斯转成可用网格;动态场景有 4DGS、Deformable-GS;规模化有 Street Gaussians、CityGaussian 等街景/城市级重建;工程上它已进入手机端(部分厂商的"空间视频/3D 扫描"管线)并与 SLAM、机器人、自动驾驶感知深度耦合。当前趋势与老师的判断一致:显式(可编辑、高效)与隐式(可查询、可组合)正在持续融合,而生成式 3D(文生 3D:DreamFusion 的 SDS 蒸馏、Shap-E / Point-E 的直接生成 3D 扩散、LRM / Instant3D 的前馈重建、TRELLIS 等原生 3D 潜伏扩散,以及"视频模型生成多视图 + 快速重建"的路线)大量以 3DGS 或潜伏表示作为输出格式——这正是本讲"数据不够 2D 来凑、渲染做桥梁"主线的自然延伸。
收尾处老师用两分钟打开另一个维度:物体的几何不只有基元层面的细节(某个部件的表面长什么样),还有结构(structure)——重复(repetition)与对称(symmetry)(椅子往往是对称的)。参数曲面能带来一点对称性,但直到今天,点云、网格、隐式函数本质上都在表示"各个部件的几何细节",没有一个直接捕捉对称与重复这类规律性。怎么补上?多来自图形学界的几类尝试:
最后是老师眼中正在发生的新趋势(过去一两年):大语言模型强到对语义与约束的理解相当可靠——它懂"椅子应该长什么样、要满足什么约束"。那么,能不能直接让 LLM(比如 GPT)输出这些程序?程序负责结构与语义,隐式函数负责部件的具体几何细节——两种能力拼装起来,正是当下正在涌现的研究方向。老师以这句话收束全讲:"这就是我今天想讲的全部,谢谢大家。"
LLM + 程序化 3D 生成的前沿。这一趋势已有具体化身:CAD 领域的 CAD-Recode、Text2CAD 让 LLM 直接吐出 CAD 建模脚本;ShapeScript、3D-GPT 一类工作用 LLM 编排程序化生成管线;结合本讲的层次/部件视角,"LLM 写结构 + 神经隐式函数补细节"与近期用 LLM 控制可组合 SDF/部件图的工作(如 CLAY、TRELLIS 的结构化条件)一脉相承。它与上一节的生成式 3D 也在合流——文生 3D 系统普遍用 LLM 扩写提示词/生成条件,再由 3D 生成模型产出资产。