在三维视觉领域,如何用神经网络表示一个连续的三维表面,一直是核心问题之一。传统的网格表示依赖显式顶点和面片,拓扑固定、分辨率受限,而以符号距离函数和占用网络为代表的隐式表示方法,把表面编码为神经网络的一个连续函数输出,从根本上突破了这些限制。本文围绕神经表面渲染中的这两种关键技术展开,从原理、训练到工程实践进行详细讲解。

一、SDF的基本原理与网络设计
符号距离函数的中文全称是符号距离函数,它把空间中任意一点映射为该点到目标表面的最短有向距离:表面外部为正值,内部为负值,表面本身恰好为零。因此在数学上,表面就是函数的零水平集。这种表示方式的优势在于几何意义明确,法向量可以直接通过对坐标求梯度得到,而且天然支持布尔运算,比如两个SDF相加即可实现形体的并集运算。
在DeepSDF这一经典工作中,网络输入是一个三维坐标加上一个隐编码向量,输出一个标量距离值。隐编码充当形状的条件信息,使得一个网络可以表示一个形状类别中的无数实例。训练时通常从目标表面采样大量点,计算其真实有向距离作为监督信号,并使用截断策略:把距离值截断在一个较小范围内,比如负0.1到正0.1之间,让网络集中精力学习表面附近的精细结构,而不是远处无关的空间区域。
下面是一个简化版的SDF网络PyTorch实现,使用多层感知机配合正弦激活函数,这也是深度隐式表示中非常常见的做法:
import torch
import torch.nn as nn
class SIRENLayer(nn.Module):
def __init__(self, in_features, out_features, w0=30.0):
super().__init__()
self.linear = nn.Linear(in_features, out_features)
self.w0 = w0
def forward(self, x):
return torch.sin(self.w0 * self.linear(x))
class SDFNetwork(nn.Module):
def __init__(self, hidden=256, latent_dim=256):
super().__init__()
self.latent = nn.Parameter(torch.zeros(1, latent_dim))
layers = []
dims = [3 + latent_dim] + [hidden] * 7 + [1]
for i in range(len(dims) - 1):
act = SIRENLayer if i < len(dims) - 2 else nn.Identity
layers.append(act(dims[i], dims[i + 1]))
self.mlp = nn.Sequential(*layers)
def forward(self, pts):
# pts: (N, 3) 采样点坐标
latent = self.latent.expand(pts.shape[0], -1)
return self.mlp(torch.cat([pts, latent], dim=-1))
代码中的隐编码作为可学习参数存在,训练过程中会与网络权重一起被优化。正弦激活相比ReLU更能保留高频几何细节,这对于恢复物体表面的尖锐棱角尤为重要。此外,SDF网络在推理阶段要提取表面,常用手段是Marching Cubes算法:在一个规则网格上查询SDF值,根据正负分布三角化出等值面。网格分辨率直接决定了重建精度,通常从64起步,最终提升到512甚至1024,显存占用会随分辨率三次方增长,需要权衡。
二、占用网络的思路与对比分析
占用网络的核心思想更加直接:不再预测距离,而是让网络输出空间点位于表面内部的概率,取值介于零到一之间,0.5的等值面即为重建表面。这种方式的优势是学习目标简单,本质上是一个二分类问题,不需要构造距离场,数据准备阶段只需判断采样点在网格内外,这对水密性模型来说非常容易。而SDF需要计算精确距离,若网格不水密,距离计算本身就可能出错。
不过占用网络的缺点也很明显。第一,它只在表面附近提供有效的梯度信号,距离表面较远的点概率趋近饱和,梯度消失导致学习缓慢;而SDF在整个空间中都有平滑变化的距离值,梯度场更加友好。第二,占用网络无法像SDF那样直接解析求法向,需要借助有限差分近似。第三,从等值面提取角度看,概率场的过渡带宽由网络容量决定,不像SDF那样可以显式控制带宽,重建的表面往往偏软,细节丢失更严重。
两者的差异可以用一张表来概括:
| 对比维度 | SDF | 占用网络 |
|---|---|---|
| 输出含义 | 有向距离,无界标量 | 内部概率,零到一之间 |
| 表面定义 | 零水平集 | 0.5等值面 |
| 法向获取 | 解析梯度 | 有限差分近似 |
| 数据准备 | 需计算精确距离 | 只需内外判断 |
| 梯度特性 | 全空间平滑 | 表面外梯度易饱和 |
| 细节恢复 | 较锐利 | 偏软,易糊 |
实践建议是:如果训练数据是高质量水密网格,SDF通常能取得更好的细节;如果数据来源复杂、拓扑不干净,占用网络的数据准备更省心。另外还有一个折中方案叫无符号距离函数,只预测距离不区分正负,适合处理薄壁、开放曲面等无法定义内外的几何。
三、从几何监督走向可微体积渲染
上述两种方法都属于纯几何监督路线,即训练时必须知道采样点的真实几何标签。而在没有真值几何、只有多视角图像的场景下,如何利用隐式表示进行表面重建?答案是把渲染过程本身做成可微的,让图像重建成误差反传到隐式表面网络上,这就是神经表面渲染的核心。NeuS和VolSDF是这个方向的代表工作,它们都以SDF作为表面表示,再通过体渲染公式把SDF转换为体密度。
VolSDF的做法是把SDF经过一个拉普拉斯累积分布函数映射为密度,转换带宽度由可学习的尺度参数控制。NeuS则指出这种映射在权重函数上存在偏差,提出了新的归一化权重形式,使得沿射线积分时表面位置的第一性估计保持无偏,这一点对重建精度影响很大。无论哪种方案,Eikonal正则项几乎都是标配,其形式如下:
def eikonal_loss(sdf_net, pts):
# 对输入坐标开启梯度
pts = pts.detach().requires_grad_(True)
sdf = sdf_net(pts)
# 求SDF对坐标的梯度,理想情况下模长应为1
grads = torch.autograd.grad(
outputs=sdf, inputs=pts,
grad_outputs=torch.ones_like(sdf),
create_graph=True, retain_graph=True
)[0]
return ((grads.norm(dim=-1) - 1.0) ** 2).mean()
这个正则项的几何含义是:距离函数的梯度模长处处为一,这是距离场的本质性质。加上这一约束后,即便没有显式的距离监督,网络学出的场也更接近真正的SDF,等值面提取结果更干净、更少漂浮伪影。训练流程上,先沿相机射线进行粗采样,根据粗网络输出的密度分布再做层级采样,把更多采样点集中到表面附近,最后用体渲染公式加权合成颜色,与真实图像计算光度损失。
四、工程实践中的关键细节
第一个细节是采样策略。表面附近区域的采样密度直接决定几何细节的学习效果,常用做法是按距离表面一定范围内均匀采点加表面直接抖动采点的组合,比例大约在1比1,再加少量均匀空间采样防止过拟合到局部。第二个细节是隐编码的规则化,对隐向量施加高斯先验可以支持生成任务,而加噪扰动则能提升形状补全的鲁棒性。第三个细节是等值面提取时阈值的选取,对于占用网络要扫一遍0.5附近的多个阈值做对比,因为概率场饱和区宽度受训练程度影响,固定阈值有时并非最优。
第四个细节是数值稳定性。SDF的截断值设置过小会导致有效监督点稀疏,设置过大又会稀释表面附近的高质量信号,经验上取场景包围盒尺寸的百分之一到百分之五之间比较稳妥。使用正弦激活的网络初始化也有讲究,第一层权重需要乘以一个约30的缩放因子,否则信号频率不足,网络难以表达高频细节,这是SIREN论文中反复强调的要点。
综合来看,SDF与占用网络代表了隐式表面表示的两条路线,前者几何性质优越、细节表现出色,后者训练简单、数据门槛低。而可微体积渲染的引入,让这两类方法摆脱了对真值几何的依赖,仅凭多视角照片就能重建高质量表面,这也是近年来三维重建方向最重要的技术演进。掌握这些原理与细节,无论是做单物体重建、大场景扫描还是生成式三维内容,都能打下扎实的基础。