第 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+z2−1,我们能看懂每个符号(参见 05-隐式曲面与SDF.md)。神经隐式把这个 fff 换成一个 MLP:
定义(神经隐式场):给定参数 θ\\thetaθ,函数 fθ:R3→Rf_\\theta:\\mathbb{R}^3\\to\\mathbb{R}fθ:R3→R 由神经网络实现;形状定义为它的某个水平集,如 S={x∣fθ(x)=0}S=\\{\\mathbf{x}\\mid f_\\theta(\\mathbf{x})=0\\}S={x∣fθ(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(2L−1πx),cos(2L−1π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)=e−21(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}'\\|∥x−x′∥ 且随距离缓慢衰减——也就是一个“宽核”。
第二步:宽核 = 低通滤波。 核回归结果相当于用核对目标函数做卷积。由卷积定理 k∗f^(ω)=k^(ω)⋅f^(ω)\\widehat{k * f}(\\omega) = \\hat{k}(\\omega)\\cdot\\hat{f}(\\omega)k∗f(ω)=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=0L−1,则内积为 ∑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′)],用积化和差 sinAsinB+cosAcosB=cos(A−B)\\sin A\\sin B+\\cos A\\cos B=\\cos(A-B)sinAsinB+cosAcosB=cos(A−B) 得
γ(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=0∑L−1cos(2lπ(x−x′))
第四步:看这个新核的形状。 新核 kγ=∑l=0L−1cos(2lπδ)k_\\gamma=\\sum_{l=0}^{L-1}\\cos(2^l\\pi\\delta)kγ=∑l=0L−1cos(2lπδ),其中 δ=x−x′\\delta=x-x'δ=x−x′。当 δ=0\\delta=0δ=0 时每项都是 cos0=1\\cos 0=1cos0=1,总和 =L=L=L 取最大值;当 δ\\deltaδ 稍偏离 000 时,各项相位迅速错开(频率呈 1,2,4,…,2L−11,2,4,\\dots,2^{L-1}1,2,4,…,2L−1 的几何级数),正负相消,总和迅速下降到 O(1)O(1)O(1) 甚至更小。也就是说 kγk_\\gammakγ 是一个尖峰核,只在 ∣δ∣≲2−(L−1)|\\delta|\\lesssim 2^{-(L-1)}∣δ∣≲2−(L−1) 内显著。频域上尖峰核对应宽频带:它的谱包含 {1,2,4,…,2L−1}\\{1,2,4,\\dots,2^{L-1}\\}{1,2,4,…,2L−1} 这一整组频率(乘以 π\\piπ),因此目标函数中这些频率的成分都能被有效拟合。
第五步:LLL 的含义与选取。 最高频率是 2L−1π2^{L-1}\\pi2L−1π,对应最小可分辨波长约为 λmin≈2π/(2L−1π)=22−L\\lambda_{\\min}\\approx 2\\pi/(2^{L-1}\\pi) = 2^{2-L}λmin≈2π/(2L−1π)=22−L。坐标一般归一化到 [−1,1][-1,1][−1,1],若要分辨 1/5121/5121/512 的细节,需 22−L≲1/5122^{2-L}\\lesssim 1/51222−L≲1/512 即 L≳11L\\gtrsim 11L≳11。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 积到 ttt 得 lnT(t)−lnT(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(刚出发时什么都没挡),故 lnT(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+1−ti,假设每段内 σ,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=1−exp(−σ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<i∏exp(−σ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=1∑NTiαici,αi=1−e−σ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=∑r∥C^(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=1∑Nciαij<i∏(1−αj),αi=oi⋅e−21Δ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=1−e−σ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αi∏j<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)T←T(1−αi),当 TTT 小于阈值(如 10−410^{-4}10−4)就提前退出,这是重要的加速手段。
第五步:颜色从球谐来。 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)=∑l∑m=−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Σ=RSSTRT,SSS 为对角缩放(存 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\\}Ω={x∣fθ(x)>0.5}。DeepSDF:fθ:R3→Rf_\\theta:\\mathbb{R}^3\\to\\mathbb{R}fθ:R3→R,S={x∣fθ(x)=0}S = \\{\\mathbf{x}\\mid f_\\theta(\\mathbf{x})=0\\}S={x∣fθ(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 神经隐式场的训练与提取
4.2 NeRF 的训练与渲染
4.3 3DGS 的实现
4.4 实现注意事项与避坑
4.5 FreeCAD 的支持现状(要说实话)
FreeCAD 目前对这类表示基本没有原生支持。 这不是遗漏而是定位问题:FreeCAD 的核心是 OCCT 的精确 B-Rep 内核,服务于可标注、可制造的参数化模型;而神经隐式与 3DGS 属于研究性 / 外部工具范畴。现实的协作方式是“外部生成 + 转换后导入”:
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 个独立尺寸交叉验证;上例用单点校准,误差会在长距离上累积,多点最小二乘更可靠。
六、小结与要点
必须记住的四件事
优点:连续、无限分辨率(任意采样密度可求值,无网格离散化痕迹);极度紧凑(一个网络或一组高斯表达复杂形状,远小于等价网格);可微可学习(与深度学习管线无缝,支持生成、补全、超分);新视角合成达照片级(真实场景重建优于传统方法,尤其玻璃、毛发、半透明物体);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)。
练习与思考



