欢迎光临
我们一直在努力

几何表示法研究(二十七)

第 13 章 神经隐式场、NeRF 与 3D 高斯泼溅

摘要:本章介绍三类学习型几何表示——神经隐式场、NeRF 与 3D 高斯泼溅(3DGS)。它们共同的核心思路是"参数 + 可微渲染 + 梯度下降":神经隐式场用 MLP 把形状编码进网络权重,NeRF 把场景表示为可查询的体辐射场以解决新视角合成,3DGS 则用一组显式 3D 高斯图元实现实时渲染。文章从背景动机出发,解释谱偏差与位置编码的原理,逐步推导体渲染方程与 3DGS 的 alpha 混合公式,并给出三类表示的具体实现步骤、避坑要点与 FreeCAD 协作方式。最后通过一个"照片重建金属支架并导入 FreeCAD"的完整示例串联全流程,并总结各自的优缺点与适用场景。

1. 背景与动机

我们先摆一个尴尬的事实:拿手机绕着一辆汽车拍 200 张照片,用传统流程(SfM → 稠密匹配 → 泊松重建 → 网格简化)做出来的模型往往是一团"融化的塑料"——车窗玻璃是空洞,反光的车漆变成坑洼,细铁丝天线直接消失。

问题出在哪?传统重建假设表面是不透明的、漫反射的、有明确边界的。可现实世界里到处是玻璃、毛发、烟雾、镂空栅格,它们根本不满足这个假设。于是有人转换了思路:别急着重建表面,先重建"光在空间中怎么分布"。

再往前追溯一步,还有一个更基础的痛点。前面章节里所有表示——B-Rep(参见 01-BRep边界表示.md)、网格(参见 08-多边形网格.md)、体素(参见 06-体素表示.md)——都是手工设计的数据结构。它们的参数(控制点、顶点坐标、体素值)不是"学出来的",也不天然对损失函数可微,因此很难纳入深度学习管线进行优化、补全和生成。

本章的主角正是回应这两个痛点的学习型(learned)几何表示:几何不再写成显式公式或网格,而是编码进一个神经网络的权重、或一组可学习点的参数里。核心驱动力是"用数据 + 优化取代手工几何建模"。三个代表:

  • 神经隐式场(Neural Implicit):用一个 MLP 把坐标 x\\mathbf{x}x 映射成占用率、有符号距离或颜色——把形状存进网络权重。代表工作:Occupancy Networks(占用率)、DeepSDF(有符号距离)。
  • NeRF(Neural Radiance Fields, Mildenhall et al. 2020):把场景编码为"任意视点可查询的体辐射场",输入 5D 的 (x,y,z,θ,ϕ)(x,y,z,\\theta,\\phi)(x,y,z,θ,ϕ),输出颜色与密度,专门解决新视角合成(novel view synthesis)。
  • 3D 高斯泼溅(3D Gaussian Splatting, Kerbl et al. 2023):干脆放弃网络,用一团各向异性 3D 高斯作为图元,每个高斯带位置、协方差、不透明度、球谐颜色,通过可微光栅化(泼溅)渲染。可看作"点云表示 + 可微优化"的复兴(参见 09-点云.md)。

三者的谱系关系值得先记住:神经隐式(连续、紧凑但不快)→ NeRF(辐射场、照片级新视角、慢)→ 3DGS(显式点团、实时、但存储大)。

2. 核心概念与原理

2.1 把形状“存进”网络是什么意思

传统隐式表示写 f(x)=x2+y2+z2−1f(\\mathbf{x})=x^2+y^2+z^2-1f(x)=x2+y2+z21,我们能看懂每个符号(参见 05-隐式曲面与SDF.md)。神经隐式把这个 fff 换成一个 MLP:

定义(神经隐式场):给定参数 θ\\thetaθ,函数 fθ:R3→Rf_\\theta:\\mathbb{R}^3\\to\\mathbb{R}fθ:R3R 由神经网络实现;形状定义为它的某个水平集,如 S={x∣fθ(x)=0}S=\\{\\mathbf{x}\\mid f_\\theta(\\mathbf{x})=0\\}S={xfθ(x)=0}。形状的全部信息存在权重 θ\\thetaθ 中。

理论依据是通用逼近定理:足够宽的 MLP 能逼近任意连续函数,所以原则上它能逼近任意占用场或距离场。这里有个反直觉之处:模型文件不再是“顶点列表”,而是“几兆的浮点权重”,你无法打开文件看到某个顶点在哪儿——这是它紧凑的原因,也是它“黑箱”特性的来源。

2.2 谱偏差:为什么直接喂坐标不行

实践中人们很快发现:把 (x,y,z)(x,y,z)(x,y,z) 直接送进 MLP,学出来的形状总是模糊的——大轮廓对了,细节全丢。这个现象叫谱偏差(spectral bias):MLP 天生偏爱低频函数,对高频成分收敛极慢。

类比一下:你让一个只会用粗笔刷的人临摹工笔画,他能抓住构图但画不出发丝。解决办法不是换人,而是给他一套细笔——这就是位置编码(positional encoding / Fourier features):

定义(位置编码):把低维坐标通过一组不同频率的正余弦函数升维,
γ(x)=[sin⁡(20πx),cos⁡(20πx),…,sin⁡(2L−1πx),cos⁡(2L−1πx)]\\gamma(\\mathbf{x}) = \\big[\\sin(2^0\\pi\\mathbf{x}),\\cos(2^0\\pi\\mathbf{x}),\\dots,\\sin(2^{L-1}\\pi\\mathbf{x}),\\cos(2^{L-1}\\pi\\mathbf{x})\\big]γ(x)=[sin(20πx),cos(20πx),,sin(2L1πx),cos(2L1πx)]γ(x)\\gamma(\\mathbf{x})γ(x) 而非 x\\mathbf{x}x 作为网络输入。

这是 NeRF 最关键的技巧之一,第三节会一步步说清它为什么有效。

2.3 辐射场与体渲染:从“表面”到“雾”

NeRF 的核心观念转变是:别再假设有一层薄薄的表面,把场景当成一团有颜色的雾。场景中每一点有两个属性:体密度 σ(x)≥0\\sigma(\\mathbf{x})\\ge 0σ(x)0(这一点多不透明,单位是每米的消光率)与视角相关颜色 c(x,d)\\mathbf{c}(\\mathbf{x},\\mathbf{d})c(x,d)(从方向 d\\mathbf{d}d 看这点是什么颜色,这一项让它能表达反光高光)。

渲染一个像素时,从相机出发投一条射线穿过雾,把沿途颜色按“还能透过多少光”加权累积。这个加权因子叫透射率,是理解 NeRF 的关键。

为什么这套框架能处理玻璃和毛发?因为它从不要求 σ\\sigmaσ 是 0/1 二值的。半透明玻璃就是 σ\\sigmaσ 小,细毛发就是 σ\\sigmaσ 在细长区域内偏大——都能连续表达,不需要“边界”这个概念。

2.4 3DGS:把雾换成一堆椭球

NeRF 慢在哪?慢在每条射线上要采样几十上百个点,每点都要跑一次 MLP 前向,一张 800×800 的图就是几千万次网络调用。3DGS 的解法很直接:不用网络,直接用一堆显式的、参数化的雾团拼场景。每个雾团是一个 3D 高斯 G(x)=e−12(x−μ)TΣ−1(x−μ)G(\\mathbf{x})=e^{-\\frac12(\\mathbf{x}-\\mu)^T\\Sigma^{-1}(\\mathbf{x}-\\mu)}G(x)=e21(xμ)TΣ1(xμ)

定义(3D 高斯图元):一个高斯由四组参数描述——中心 μ∈R3\\mu\\in\\mathbb{R}^3μR3、协方差 Σ∈R3×3\\Sigma\\in\\mathbb{R}^{3\\times3}ΣR3×3(决定椭球的朝向与三轴长度,即“各向异性”)、不透明度 o∈[0,1]o\\in[0,1]o[0,1]、球谐系数 SHSHSH(编码视角相关颜色)。整个场景就是 {(μi,Σi,oi,SHi)}i=1N\\{(\\mu_i,\\Sigma_i,o_i,SH_i)\\}_{i=1}^N{(μi,Σi,oi,SHi)}i=1N

渲染时把 3D 高斯投影到图像平面得 2D 高斯,按深度排序,做前后 alpha 混合。这个过程叫泼溅(splatting)——像把一团颜料泼溅到画布上,落点是个椭圆斑。关键在于这整套投影加混合是可微的,所以可以直接对 μ,Σ,o,SH\\mu,\\Sigma,o,SHμ,Σ,o,SH 求梯度做优化,不需要任何神经网络前向。这是它比 NeRF 快两三个数量级的根本原因。

2.5 怎么把它们变回能用的几何

这一步对 CAD 场景至关重要。神经隐式训练完得到一个 SDF 场,可用 Marching Cubes 提取网格(参见 05-隐式曲面与SDF.md)——这是标准做法。3DGS 也可通过"烘焙"转网格(屏幕空间融合,或提取 3D 密度场后再 Marching Cubes),但要清楚:它本质上仍是点-高斯表示,转出来的网格质量通常达不到工程精度。

3. 关键公式与推导

3.1 位置编码:为什么正余弦能救高频

第一步:把 MLP 看成核回归。 对一个宽 MLP,用神经切线核(NTK)理论可证:训练动力学近似等价于用一个固定核 k(x,x′)k(\\mathbf{x},\\mathbf{x}')k(x,x) 做核回归。若输入是原始坐标 x\\mathbf{x}x,这个核只依赖 ∥x−x′∥\\|\\mathbf{x}-\\mathbf{x}'\\|xx 且随距离缓慢衰减——也就是一个“宽核”。

第二步:宽核 = 低通滤波。 核回归结果相当于用核对目标函数做卷积。由卷积定理 k∗f^(ω)=k^(ω)⋅f^(ω)\\widehat{k * f}(\\omega) = \\hat{k}(\\omega)\\cdot\\hat{f}(\\omega)kf(ω)=k^(ω)f^(ω),空间域的宽核对应频域的窄窗,k^\\hat{k}k^ 在高频处趋近 000,于是目标函数的高频成分 f^(ω)\\hat{f}(\\omega)f^(ω)(大 ω\\omegaω)被乘以接近零的数,无法被学习出来。这就是谱偏差的数学根源。

第三步:用频率映射把核变窄。 换输入为 γ(x)\\gamma(\\mathbf{x})γ(x),看内积变成什么。取一维情形 γ(x)=[sin⁡(2lπx),cos⁡(2lπx)]l=0L−1\\gamma(x)=[\\sin(2^l\\pi x),\\cos(2^l\\pi x)]_{l=0}^{L-1}γ(x)=[sin(2lπx),cos(2lπx)]l=0L1,则内积为 ∑l[sin⁡(2lπx)sin⁡(2lπx′)+cos⁡(2lπx)cos⁡(2lπx′)]\\sum_{l}[\\sin(2^l\\pi x)\\sin(2^l\\pi x') + \\cos(2^l\\pi x)\\cos(2^l\\pi x')]l[sin(2lπx)sin(2lπx)+cos(2lπx)cos(2lπx)],用积化和差 sin⁡Asin⁡B+cos⁡Acos⁡B=cos⁡(A−B)\\sin A\\sin B+\\cos A\\cos B=\\cos(A-B)sinAsinB+cosAcosB=cos(AB)

γ(x)⋅γ(x′)=∑l=0L−1cos⁡ ⁣(2lπ(x−x′))
\\gamma(x)\\cdot\\gamma(x') = \\sum_{l=0}^{L-1}\\cos\\!\\big(2^l\\pi (x-x')\\big)
γ(x)γ(x)=l=0L1cos(2lπ(xx))

第四步:看这个新核的形状。 新核 kγ=∑l=0L−1cos⁡(2lπδ)k_\\gamma=\\sum_{l=0}^{L-1}\\cos(2^l\\pi\\delta)kγ=l=0L1cos(2lπδ),其中 δ=x−x′\\delta=x-x'δ=xx。当 δ=0\\delta=0δ=0 时每项都是 cos⁡0=1\\cos 0=1cos0=1,总和 =L=L=L 取最大值;当 δ\\deltaδ 稍偏离 000 时,各项相位迅速错开(频率呈 1,2,4,…,2L−11,2,4,\\dots,2^{L-1}1,2,4,,2L1 的几何级数),正负相消,总和迅速下降到 O(1)O(1)O(1) 甚至更小。也就是说 kγk_\\gammakγ 是一个尖峰核,只在 ∣δ∣≲2−(L−1)|\\delta|\\lesssim 2^{-(L-1)}δ2(L1) 内显著。频域上尖峰核对应宽频带:它的谱包含 {1,2,4,…,2L−1}\\{1,2,4,\\dots,2^{L-1}\\}{1,2,4,,2L1} 这一整组频率(乘以 π\\piπ),因此目标函数中这些频率的成分都能被有效拟合。

第五步:LLL 的含义与选取。 最高频率是 2L−1π2^{L-1}\\pi2L1π,对应最小可分辨波长约为 λmin⁡≈2π/(2L−1π)=22−L\\lambda_{\\min}\\approx 2\\pi/(2^{L-1}\\pi) = 2^{2-L}λmin2π/(2L1π)=22L。坐标一般归一化到 [−1,1][-1,1][1,1],若要分辨 1/5121/5121/512 的细节,需 22−L≲1/5122^{2-L}\\lesssim 1/51222L1/512L≳11L\\gtrsim 11L11。NeRF 原文对位置取 L=10L=10L=10、对方向取 L=4L=4L=4(方向的高频需求低得多),正好符合这个估算。代价是 LLL 太大反而会过拟合噪声,并在训练视角之间产生高频伪影。

3.2 体渲染方程:透射率从哪来

NeRF 的核心公式是

C(r)=∫tntfT(t) σ(r(t)) c(r(t),d) dt,T(t)=exp⁡ ⁣(−∫tntσ(s) ds)
C(\\mathbf{r}) = \\int_{t_n}^{t_f} T(t)\\,\\sigma(\\mathbf{r}(t))\\,\\mathbf{c}(\\mathbf{r}(t),\\mathbf{d})\\,dt,\\qquad T(t)=\\exp\\!\\left(-\\int_{t_n}^{t}\\sigma(s)\\,ds\\right)
C(r)=tntfT(t)σ(r(t))c(r(t),d)dt,T(t)=exp(tntσ(s)ds)

符号先交代:r(t)=o+td\\mathbf{r}(t)=\\mathbf{o}+t\\mathbf{d}r(t)=o+td 是射线(o\\mathbf{o}o 相机中心,d\\mathbf{d}d 单位方向),[tn,tf][t_n,t_f][tn,tf] 是近远裁剪范围,σ\\sigmaσ 是体密度,c\\mathbf{c}c 是视角相关颜色,T(t)T(t)T(t) 是透射率,C(r)C(\\mathbf{r})C(r) 是该射线(该像素)最终的颜色。我们从物理出发一步步推。

第一步:微元的消光。 设 T(t)T(t)T(t) 表示"光从 tnt_ntn 走到 ttt 还没被挡住的概率"。在 [t,t+dt][t,t+dt][t,t+dt] 这段微元里被吸收/散射掉的比例正比于密度与长度,即 dT=−σ(t) T(t) dtdT = -\\sigma(t)\\,T(t)\\,dtdT=σ(t)T(t)dt。理解这个式子:σ(t)dt\\sigma(t)dtσ(t)dt 是这一小段的消光概率,T(t)T(t)T(t) 是"能走到这里"的概率,两者相乘才是"在这里被挡住"的概率。

第二步:解这个微分方程。 分离变量得 dT/T=−σ(t)dtdT/T = -\\sigma(t)dtdT/T=σ(t)dt,两边从 tnt_ntn 积到 tttln⁡T(t)−ln⁡T(tn)=−∫tntσ(s)ds\\ln T(t) – \\ln T(t_n) = -\\int_{t_n}^{t}\\sigma(s)dslnT(t)lnT(tn)=tntσ(s)ds。初始条件 T(tn)=1T(t_n)=1T(tn)=1(刚出发时什么都没挡),故 ln⁡T(tn)=0\\ln T(t_n)=0lnT(tn)=0,于是 T(t)=exp⁡ ⁣(−∫tntσ(s)ds)T(t)=\\exp\\!\\big(-\\int_{t_n}^{t}\\sigma(s)ds\\big)T(t)=exp(tntσ(s)ds)。这就是透射率公式的来源,含义是累积光学厚度的负指数:密度沿途积得越多,透射率越小。

第三步:累积贡献。 [t,t+dt][t,t+dt][t,t+dt] 这段微元贡献多少颜色到像素?三个因子相乘——光能走到这里的概率 T(t)T(t)T(t)、在这里被"截获"的概率 σ(t)dt\\sigma(t)dtσ(t)dt、这里发出的颜色 c(t,d)\\mathbf{c}(t,\\mathbf{d})c(t,d)。积分起来就是体渲染方程。注意 T(t)σ(t)T(t)\\sigma(t)T(t)σ(t) 合起来正好是一个概率密度(光子在 ttt 处终止的概率密度),所以 C(r)C(\\mathbf{r})C(r) 是颜色的期望值。

第四步:离散求积(工程实现形式)。 积分无法直接计算,必须离散化。在 [tn,tf][t_n,t_f][tn,tf] 上采样 NNN 个点 t1<⋯<tNt_1<\\dots<t_Nt1<<tN,令 δi=ti+1−ti\\delta_i=t_{i+1}-t_iδi=ti+1ti,假设每段内 σ,c\\sigma,\\mathbf{c}σ,c 为常数,则段内透射率衰减为 exp⁡(−σiδi)\\exp(-\\sigma_i\\delta_i)exp(σiδi)。于是"在第 iii 段被截获"的概率是"进入这段还没被挡"减去"穿过这段还没被挡":αi=1−exp⁡(−σiδi)\\alpha_i = 1 – \\exp(-\\sigma_i\\delta_i)αi=1exp(σiδi)。而"能进入第 iii 段"的概率是前面各段都没挡住:

Ti=∏j<iexp⁡(−σjδj)=exp⁡ ⁣(−∑j<iσjδj)=∏j<i(1−αj)
T_i = \\prod_{j<i}\\exp(-\\sigma_j\\delta_j) = \\exp\\!\\Big(-\\sum_{j<i}\\sigma_j\\delta_j\\Big) = \\prod_{j<i}(1-\\alpha_j)
Ti=j<iexp(σjδj)=exp(j<iσjδj)=j<i(1αj)

最后一个等号正是把 exp⁡(−σjδj)=1−αj\\exp(-\\sigma_j\\delta_j)=1-\\alpha_jexp(σjδj)=1αj 代回。合起来得到 NeRF 实际用的离散公式:

C^(r)=∑i=1NTi αi ci,αi=1−e−σiδi,Ti=∏j<i(1−αj)
\\hat{C}(\\mathbf{r}) = \\sum_{i=1}^{N} T_i\\,\\alpha_i\\,\\mathbf{c}_i,\\qquad \\alpha_i = 1-e^{-\\sigma_i\\delta_i},\\quad T_i=\\prod_{j<i}(1-\\alpha_j)
C^(r)=i=1NTiαici,αi=1eσiδi,Ti=j<i(1αj)

第五步:为什么这可训练。 C^\\hat{C}C^σi,ci\\sigma_i,\\mathbf{c}_iσi,ci 都可微,而 σi,ci\\sigma_i,\\mathbf{c}_iσi,ci 又是 MLP 的输出。构造损失 L=∑r∥C^(r)−Cgt(r)∥22\\mathcal{L} = \\sum_{\\mathbf{r}}\\|\\hat{C}(\\mathbf{r}) – C_{\\text{gt}}(\\mathbf{r})\\|_2^2L=rC^(r)Cgt(r)22 反向传播即可把梯度一路传回权重。整个"可微渲染"链条的可微性,全靠上面这个求积公式。

3.3 3DGS 的 alpha 混合

3DGS 的渲染方程形式上与 NeRF 离散式同源,但 αi\\alpha_iαi 的来源完全不同:

C=∑i=1Nci αi∏j<i(1−αj),αi=oi⋅e−12ΔTΣ′−1Δ
C = \\sum_{i=1}^{N} c_i\\,\\alpha_i \\prod_{j<i}(1-\\alpha_j),\\qquad \\alpha_i = o_i\\cdot e^{-\\frac12 \\Delta^T \\Sigma'^{-1} \\Delta}
C=i=1Nciαij<i(1αj),αi=oie21ΔTΣ1Δ

第一步:3D 高斯投影成 2D 高斯。 一个 3D 高斯经视图变换 WWW 与投影的局部线性化(雅可比 JJJ)后,协方差按 Σ′=J W Σ WTJT\\Sigma' = J\\,W\\,\\Sigma\\,W^T J^TΣ=JWΣWTJT 变换,取其 2×22\\times22×2 的图像平面部分得到一个 2D 高斯(椭圆斑)。这一步是“泼溅”的几何核心:3D 椭球在屏幕上留下的印记是 2D 椭圆。

第二步:算像素处的高斯响应。 设当前像素坐标 p\\mathbf{p}p、该 2D 高斯中心 μ′\\mu'μ,令偏移 Δ=p−μ′\\Delta = \\mathbf{p} – \\mu'Δ=pμ,则高斯在该像素的响应(马氏距离的高斯衰减)是 G′(p)=exp⁡(−12ΔTΣ′−1Δ)G'(\\mathbf{p}) = \\exp(-\\tfrac12\\Delta^T\\Sigma'^{-1}\\Delta)G(p)=exp(21ΔTΣ1Δ)。含义是像素离斑中心越远(按椭圆度量)贡献越小,Σ′\\Sigma'Σ 控制斑的大小与朝向。第三步:乘上不透明度。 高斯自身有一个可学习的整体不透明度 oi∈[0,1]o_i\\in[0,1]oi[0,1],于是 αi=oiexp⁡(−12ΔTΣ′−1Δ)\\alpha_i = o_i\\exp(-\\tfrac12\\Delta^T\\Sigma'^{-1}\\Delta)αi=oiexp(21ΔTΣ1Δ)。对比 NeRF 的 αi=1−e−σiδi\\alpha_i=1-e^{-\\sigma_i\\delta_i}αi=1eσiδi:NeRF 的 α\\alphaα 来自“密度 × 步长”的物理消光,3DGS 的 α\\alphaα 来自“显式图元的空间衰减 × 不透明度”。两者数学地位相同,物理来源不同。

第四步:前后混合。 把落在该像素上的所有高斯按深度从近到远排序(必须做,混合不可交换),然后按 C=∑ciαi∏j<i(1−αj)C = \\sum c_i\\alpha_i\\prod_{j<i}(1-\\alpha_j)C=ciαij<i(1αj) 累积。其中 ∏j<i(1−αj)\\prod_{j<i}(1-\\alpha_j)j<i(1αj) 就是 3.2 节的 TiT_iTi——剩余透射率。实现上一边遍历一边维护 T←T(1−αi)T \\leftarrow T(1-\\alpha_i)TT(1αi),当 TTT 小于阈值(如 10−410^{-4}104)就提前退出,这是重要的加速手段。

第五步:颜色从球谐来。 cic_ici 不是常数,而是由球谐系数按观察方向求值 ci(d)=∑l∑m=−llklmiYlm(d)c_i(\\mathbf{d}) = \\sum_{l}\\sum_{m=-l}^{l} k^i_{lm}Y_{lm}(\\mathbf{d})ci(d)=lm=llklmiYlm(d),这让高斯能表现镜面高光随视角变化。第六步:梯度回传。 对 CCC 分别求偏导,梯度流向 cic_ici(→ SH 系数)、oio_ioi、以及 Δ\\DeltaΔΣ′\\Sigma'Σ(经 Σ′=JWΣWTJT\\Sigma'=JW\\Sigma W^TJ^TΣ=JWΣWTJT 链式回到 μ,Σ\\mu,\\Sigmaμ,Σ)。全程没有神经网络,只是一组参数的直接优化——这就是可微光栅化。为保证 Σ\\SigmaΣ 始终正定,实现上不直接优化 Σ\\SigmaΣ,而是参数化为 Σ=R S ST RT\\Sigma = R\\,S\\,S^T\\,R^TΣ=RSSTRTSSS 为对角缩放(存 3 个 log-scale),RRR 由四元数给出,这样任意参数值都对应合法协方差。

3.4 三种表示的数学形式汇总

占用网络:fθ(x)∈[0,1]f_\\theta(\\mathbf{x})\\in[0,1]fθ(x)[0,1]Ω={x∣fθ(x)>0.5}\\Omega=\\{\\mathbf{x}\\mid f_\\theta(\\mathbf{x}) > 0.5\\}Ω={xfθ(x)>0.5}。DeepSDF:fθ:R3→Rf_\\theta:\\mathbb{R}^3\\to\\mathbb{R}fθ:R3RS={x∣fθ(x)=0}S = \\{\\mathbf{x}\\mid f_\\theta(\\mathbf{x})=0\\}S={xfθ(x)=0},内负外正。NeRF 辐射场:(σ,c)=Fθ(γ(x),d)(\\sigma,\\mathbf{c}) = F_\\theta(\\gamma(\\mathbf{x}),\\mathbf{d})(σ,c)=Fθ(γ(x),d)C(r)=∫T(t)σ(t)c(t,d)dtC(\\mathbf{r})=\\int T(t)\\sigma(t)\\mathbf{c}(t,\\mathbf{d})dtC(r)=T(t)σ(t)c(t,d)dt。3DGS 场景:Scene={(μi,Σi,oi,SHi)}i=1N\\text{Scene}=\\{(\\mu_i,\\Sigma_i,o_i,SH_i)\\}_{i=1}^NScene={(μi,Σi,oi,SHi)}i=1N

4. 具体实现步骤

4.1 神经隐式场的训练与提取

  • 准备监督信号,三种来源任选或混合:多视角图像 + 相机参数(用可微渲染做一致性监督);点云或体素(直接回归占用率/距离值);已有网格采样出 (x,SDF)(\\mathbf{x},\\text{SDF})(x,SDF) 样本对。
  • 设计网络:典型是 8 层、每层 256 宽的 MLP,中间做一次 skip connection 把 γ(x)\\gamma(\\mathbf{x})γ(x) 再接进来(缓解深层信息衰减)。
  • 构造损失:占用/距离回归项 + 可微渲染一致性项(如空间轮廓约束)。对 SDF 还常加 Eikonal 正则 (∥∇fθ∥−1)2(\\|\\nabla f_\\theta\\|-1)^2(∥∇fθ1)2,逼它学成真正的距离场(参见 05-隐式曲面与SDF.md)。
  • 采样策略:不要均匀采空间,近表面加密,常见比例是 60% 近表面(表面点加高斯扰动)+ 40% 全域均匀。潜在码(latent code):给每个形状配一个可学习向量 z\\mathbf{z}z,网络变成 fθ(x,z)f_\\theta(\\mathbf{x},\\mathbf{z})fθ(x,z),一个网络可存一类形状,且在 z\\mathbf{z}z 空间插值 = 形状插值,这是 DeepSDF 的招牌能力。
  • 提取网格:在 [−1,1]3[-1,1]^3[1,1]3 上按分辨率 RRR(如 2563256^32563)批量查询 fθf_\\thetafθ,跑 Marching Cubes 取零水平集。分辨率直接决定细节,但显存与时间按 R3R^3R3 增长——先用 64364^3643 粗查询、再对跨越零集的格子细分(八叉树式)能省一个数量级。
  • 4.2 NeRF 的训练与渲染

  • 求相机位姿:用 COLMAP 跑 SfM 得内参、外参、稀疏点云与场景包围盒。这一步精度决定上限,位姿错误会导致后续全部失效。
  • 生成射线:每个像素反投影成 (o,d)(\\mathbf{o},\\mathbf{d})(o,d),随机打乱后按批次(如 4096 条射线)输入。
  • 沿射线采样,两级策略:粗采样在 [tn,tf][t_n,t_f][tn,tf] 分层随机取 64 点(分层避免规则采样的条带伪影);重要性采样用粗网络输出的权重 TiαiT_i\\alpha_iTiαi 作为分布,再抽取 128 点集中在真正有物体的区间。
  • 前向与积分:MLP 输出每点 (σi,ci)(\\sigma_i,\\mathbf{c}_i)(σi,ci),按 3.2 节离散公式合成 C^\\hat{C}C^σ\\sigmaσ 用 ReLU 或 softplus 保证非负,c\\mathbf{c}c 用 sigmoid 限到 [0,1][0,1][0,1]
  • 损失与优化:photometric MSE(粗、细两网络都算),Adam,学习率 5×10−45\\times10^{-4}5×104 指数衰减。
  • 加速方案(实用化的关键):Instant-NGP 把位置编码换成多分辨率哈希编码——预设 LLL 个分辨率层级,每层用哈希表存可学习特征向量,查询时对 8 个角点特征做三线性插值再拼接;它把“频率靠正余弦硬编码”改为“特征靠数据学习”,MLP 可缩到 2 层 64 宽,训练从天级降到秒/分钟级,哈希冲突不显式解决,而是依靠梯度使重要位置占优。Mip-NeRF 用锥体而非射线采样做抗锯齿,解决多尺度模糊。TensoRF 把辐射场张量做低秩分解(VM/CP),显式存储加少量 MLP。
  • 注意:NeRF 的几何常常是"糊"的(密度场在空中弥散),若下游要几何,必须加深度监督或法向正则,否则提出来的网格不可用。
  • 4.3 3DGS 的实现

  • 初始化:COLMAP 的 SfM 稀疏点云作为种子高斯——每点给一个高斯,μ\\muμ 取点位置,初始 scale 取到最近邻点的距离,ooo 初始化为 0.1,SH 只启用 0 阶(常数色)。
  • 每次迭代:视锥剔除并把高斯投影为 2D(Σ′=JWΣWTJT\\Sigma'=JW\\Sigma W^TJ^TΣ=JWΣWTJT);屏幕分 16×1616\\times1616×16 tile,按 tile 做深度排序(GPU 上用基数排序,性能关键);tile 内逐像素前后 alpha 混合得渲染图;与真值算损失 L=(1−λ)L1+λLD-SSIM\\mathcal{L}=(1-\\lambda)\\mathcal{L}_1 + \\lambda\\mathcal{L}_{\\text{D-SSIM}}L=(1λ)L1+λLD-SSIM(常取 λ=0.2\\lambda=0.2λ=0.2);反传梯度到 μ,Σ(R,S),o,SH\\mu,\\Sigma(R,S),o,SHμ,Σ(R,S),o,SH
  • 自适应密度控制(每约 100 次迭代做一次,是 3DGS 质量的核心):克隆(clone)——位置梯度大且尺度小的高斯说明该区域欠重建,复制一个并沿梯度方向偏移;分裂(split)——位置梯度大且尺度大的高斯说明一个大椭球在勉强支撑细节,拆成两个更小的;剪枝(prune)——ooo 低于阈值(如 0.005)的透明高斯与屏幕空间过大的高斯直接删除;另需定期把所有 ooo 重置为小值(opacity reset),迫使无用高斯被剪掉,抑制“漂浮物”。
  • SH 渐进升阶:训练早期只用 0 阶,每 1000 步升一阶到 3 阶。先学几何后学视角效果,避免用高光去拟合错误几何。
  • 事实标准的开源实现:diff-gaussian-rasterization(CUDA 可微光栅化内核);神经场一侧则是 tiny-cuda-nn 这类高性能 MLP + 哈希编码库。
  • 4.4 实现注意事项与避坑

  • 可微渲染是瓶颈:NeRF 慢在 MLP 调用量,3DGS 需定制 CUDA 光栅化(纯 PyTorch 实现慢得不可用),没有 NVIDIA GPU 基本无法复现。
  • 泛化 vs 过拟合:原版 NeRF/3DGS 是每场景训一个模型,通用性几乎为零;要实现泛化需依赖潜在码或引入扩散先验(Zero-1-to-3 这类)。
  • 几何精度有限:NeRF 密度场弥散、3DGS 是离散椭球云,都没有精确边界;若不添加深度/法向监督,几何结果不可用于 CAD。无精确边界与拓扑保证意味着无法进行布尔运算、无法标注尺寸、无法保证水密性,不适合直接进制造链路(参见 14-综合对比与选型指南.md)。
  • 存储膨胀:3DGS 一个场景常有数百万高斯 → 数百 MB,需压缩(Scaffold-GS 结构化锚点、训练后量化、SH 系数裁剪)。
  • 常见问题点:COLMAP 位姿失败(纹理不足的场景);曝光不一致导致"雾状漂浮物"(需曝光补偿);背景无穷远处的高斯发散(需设场景包围盒或背景模型)。
  • 4.5 FreeCAD 的支持现状(要说实话)

    FreeCAD 目前对这类表示基本没有原生支持。 这不是遗漏而是定位问题:FreeCAD 的核心是 OCCT 的精确 B-Rep 内核,服务于可标注、可制造的参数化模型;而神经隐式与 3DGS 属于研究性 / 外部工具范畴。现实的协作方式是“外部生成 + 转换后导入”:

  • 外部重建:用 COLMAP + nerfstudio 或官方 3DGS 实现,从照片重建场景。提取几何:神经隐式路线训练 SDF(NeuS、Neuralangelo)→ Marching Cubes → 网格;3DGS 路线先转点云(取高斯中心,用 ooo 与 scale 过滤)或用 SuGaR 之类方法烘焙网格。
  • 导入 FreeCAD:网格走 Mesh 模块(Mesh.insert("model.ply"))可做修补简化;点云走 Points 模块(Points.insert("cloud.ply"))。
  • 转回 B-Rep:用 ReverseEngineering 模块做曲面拟合(平面/柱面/球面/B 样条),把网格片段拟合成可编辑的 B-Rep 面(参见 14-综合对比与选型指南.md)。这一步是信息重建而非无损转换,需要人工介入指定特征。
  • 务实建议:把这类表示当作视觉参考与测量底图而非几何源。把重建网格导入作为“现场扫描背景”,在其上用标准 Part/PartDesign 工具重新建出精确模型——这是逆向工程的通行做法。
  • 5. 实际应用示例

    做一个完整的小例子:用手机拍的一组照片重建一个金属支架,并把结果导入 FreeCAD 作为逆向建模参考。输入是环绕拍摄的 120 张照片(1080p,光照均匀,避免逆光和反光高亮),硬件为一张 8GB 显存的 NVIDIA GPU。

    第 1 步:求相机位姿(COLMAP)

    colmap feature_extractor –database_path db.db –image_path ./images \\
    –ImageReader.single_camera 1
    colmap exhaustive_matcher –database_path db.db
    mkdir sparse && colmap mapper –database_path db.db –image_path ./images \\
    –output_path ./sparse
    # 检查点:sparse/0/ 下应有 cameras.bin / images.bin / points3D.bin
    # 若注册图像数 < 100,说明纹理不足或重叠不够,需补拍

    第 2 步:训练 3DGS(求外观与快速预览)

    python train.py -s ./ -m ./output/bracket –iterations 30000 \\
    –densify_until_iter 15000 –opacity_reset_interval 3000
    # 关注 L1 loss 与 PSNR;PSNR > 28 dB 通常视觉已足够

    第 3 步:并行训练 SDF 型神经隐式(求可用几何)。3DGS 出图漂亮,但几何是椭球云,做逆向不够用:

    ns-train neus-facto –data ./ –pipeline.model.sdf-field.use-grid-feature True \\
    –pipeline.model.eikonal-loss-mult 0.1
    # eikonal-loss-mult 是关键:施加 (‖∇f‖-1)² 正则,让 f 成为真正的距离场

    第 4 步:Marching Cubes 提取网格

    import numpy as np, torch, trimesh
    from skimage.measure import marching_cubes

    R = 256 # 分辨率,显存不够降到 128
    lin = np.linspace(1, 1, R, dtype=np.float32)
    gx, gy, gz = np.meshgrid(lin, lin, lin, indexing="ij")
    pts = np.stack([gx, gy, gz], 1).reshape(1, 3)
    sdf = [] # 分块查询,避免 1677 万点爆显存
    for chunk in np.array_split(pts, 64):
    with torch.no_grad():
    sdf.append(model.sdf(torch.from_numpy(chunk).cuda()).cpu().numpy())
    verts, faces, normals, _ = marching_cubes(
    np.concatenate(sdf).reshape(R, R, R), level=0.0, spacing=(2/R,)*3)
    mesh = trimesh.Trimesh(verts 1.0, faces, vertex_normals=normals)
    mesh.update_faces(mesh.nondegenerate_faces()) # 清理:去退化面、去孤岛、简化
    mesh = max(mesh.split(only_watertight=False), key=lambda m: len(m.faces))
    mesh.simplify_quadric_decimation(50000).export("bracket.ply")

    第 5 步:导入 FreeCAD 并拟合

    import Mesh, Part, FreeCAD as App
    doc = App.newDocument("Bracket")
    Mesh.insert("/path/to/bracket.ply", doc.Name)
    mesh_obj = doc.Objects[1]
    scale = 84.0 / mesh_obj.Mesh.BoundBox.XLength # 尺度校准:84mm=卡尺实测最长边
    mesh_obj.Mesh.transform(App.Matrix().scale(scale, scale, scale))
    shape = Part.Shape() # 粗查询 B-Rep(形状级,无特征语义)
    shape.makeShapeFromMesh(mesh_obj.Mesh.Topology, 0.05) # 0.05 为缝合容差
    Part.show(Part.makeSolid(shape), "RawSolid"); doc.recompute()

    输出与解读

    产物内容能干什么不能干什么
    point_cloud.ply(3DGS) 约 90 万高斯,约 210 MB 实时环绕预览、外观展示、汇报 不能测量、不能布尔
    bracket.ply(SDF 提取网格) 约 5 万三角面,水密 尺寸参考、碰撞检查、打印粗件 精度约 ±0.3 mm,不达装配公差
    RawSolid(FreeCAD) 由网格缝合的实体 作为底图重建精确模型 无特征、无参数、面数巨大不可编辑

    关键结论:RawSolid 不是终点,是底图。正确的下一步是打开 PartDesign,在这个实体上做草图截面、量取关键尺寸(孔径、孔距、板厚、圆角半径),然后用标准参数化特征重新建一遍,这样得到的模型才有尺寸、有约束、可修改、可出图。还有个校验技巧:重建结果的尺度是不确定的(SfM 只能定形,不能定尺),稳妥做法是拍摄时把一把已知长度的标定尺放进场景,重建后用它校准,并在成品上量 2–3 个独立尺寸交叉验证;上例用单点校准,误差会在长距离上累积,多点最小二乘更可靠。

    六、小结与要点

    必须记住的四件事

  • 三条技术路线,一个共同基因:都是“参数 + 可微渲染 + 梯度下降”。神经隐式把形状存进权重,NeRF 把场景存成辐射场,3DGS 把场景存成一团椭球。
  • 位置编码的作用是把 NTK 核变窄:γ(x)⋅γ(x′)=∑lcos⁡(2lπδ)\\gamma(x)\\cdot\\gamma(x')=\\sum_l\\cos(2^l\\pi\\delta)γ(x)γ(x)=lcos(2lπδ) 是尖峰核,对应宽频带,于是高频学得出来;LLL 决定最小可分辨波长约 22−L2^{2-L}22L
  • 透射率来自一个一阶微分方程:dT=−σTdt⇒T=e−∫σdT=-\\sigma T dt \\Rightarrow T=e^{-\\int\\sigma}dT=σTdtT=eσ;离散化后 αi=1−e−σiδi\\alpha_i=1-e^{-\\sigma_i\\delta_i}αi=1eσiδiTi=∏j<i(1−αj)T_i=\\prod_{j<i}(1-\\alpha_j)Ti=j<i(1αj)——这个求积公式的可微性是整个 NeRF 可训练的根基。
  • 3DGS 的 αi=oie−12ΔTΣ′−1Δ\\alpha_i=o_i e^{-\\frac12\\Delta^T\\Sigma'^{-1}\\Delta}αi=oie21ΔTΣ1Δ 与 NeRF 的 αi\\alpha_iαi 数学地位相同、物理来源不同:前者是显式图元的空间衰减,后者是密度积分的消光;混合公式 C=∑ciαi∏j<i(1−αj)C=\\sum c_i\\alpha_i\\prod_{j<i}(1-\\alpha_j)C=ciαij<i(1αj) 两者共用,且必须先按深度排序。此外协方差要参数化为 Σ=RSSTRT\\Sigma=RSS^TR^TΣ=RSSTRT,否则优化过程中会变成非正定矩阵。
  • 优点:连续、无限分辨率(任意采样密度可求值,无网格离散化痕迹);极度紧凑(一个网络或一组高斯表达复杂形状,远小于等价网格);可微可学习(与深度学习管线无缝,支持生成、补全、超分);新视角合成达照片级(真实场景重建优于传统方法,尤其玻璃、毛发、半透明物体);3DGS 原生实时(交互帧率,工程落地迅速);处理不完整数据能力强(稀疏多视角或稀疏点云即可重建连续场)。

    七个坑:其一几何精度不足(无精确边界、拓扑难保证,距离制造级 CAD 仍有较大差距);其二训练成本高(NeRF)(虽被 Instant-NGP 大幅加速,本质仍是优化而非解析求交);其三存储膨胀(3DGS)(百万高斯占数百 MB,必须压缩);其四不可编辑、无参数(无法标注尺寸、无法做布尔、进不了工程链路);其五泛化弱(单场景过拟合,通用 3D 生成仍靠扩散大模型补足);其六无标准交换格式(STEP / 网格工具链不认,工业落地尚早期);其七黑箱不可解释(权重难以形式化验证,安全关键领域慎用)。

    适用场景一览

    领域用途
    新视角合成 / 影视 虚拟拍摄、数字资产(NeRF/3DGS 重建真实场景)
    SLAM / 机器人 实时 3D 重建与定位(3DGS-SLAM)
    数字人 / 虚拟主播 头部与全身辐射场、实时驱动
    文生 3D / 图生 3D 扩散模型 + 隐式场(Shap-E、DreamFusion、Instant3D)
    医学 神经隐式器官重建(连续、可从稀疏切片重建)
    AR/VR / 元宇宙 实景快速数字化、可交互场景
    CAD 探索 隐式-显式混合、生成式造型(仍属早期)

    一句话选型:要看得漂亮选 3DGS,要几何能用选带 Eikonal 正则的神经 SDF,要制造级精度请回到 B-Rep(参见 01-BRep边界表示.md)。

    练习与思考

  • 位置编码最高频率是 2L−1π2^{L-1}\\pi2L1π。若把 LLL 从 10 增到 16,训练视角上的 PSNR 通常上升,但新视角上反而下降。请从 3.1 节“尖峰核”的角度解释这个现象(提示:核越尖,训练点之间的插值行为如何变化?这与经典过拟合有何异同?),并说明 Instant-NGP 的哈希编码为什么在同等表达力下不易出现该问题。
  • 3.2 节推导中假设每段内 σ\\sigmaσ 为常数才得到 αi=1−e−σiδi\\alpha_i=1-e^{-\\sigma_i\\delta_i}αi=1eσiδi。若改设段内线性变化 σ(t)=σi+k(t−ti)\\sigma(t)=\\sigma_i + k(t-t_i)σ(t)=σi+k(tti),请重新推导该段的透射率衰减因子与 αi\\alpha_iαi 表达式,并讨论这样做在什么情形下值得(提示:采样点稀疏、密度梯度大的边界处)。
  • 3DGS 的 alpha 混合要求按深度排序,但一个 3D 高斯有空间延展,"它的深度"并无唯一定义(实现中常取中心的视空间 zzz)。请构造一个会因此产生错误渲染的场景(提示:两个又长又扁、互相穿插的高斯),说明会出现什么视觉伪影,并提出至少两种缓解思路(可从图元分裂、逐 tile 局部排序、改用无序混合近似等角度考虑)。
  • 赞(0)
    未经允许不得转载:171主机测评 » 几何表示法研究(二十七)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址