news 2026/8/30 14:40:28

【三维重建】【3DGS系列】【深度学习】从泰勒展开到球谐函数:3DGS颜色表达背后的数学直觉

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【三维重建】【3DGS系列】【深度学习】从泰勒展开到球谐函数:3DGS颜色表达背后的数学直觉

1. 引言:从“猜数字”到“猜颜色”,理解3DGS的数学直觉

想象一下,你正在玩一个“猜数字”游戏。我心中想好了一个数字,比如是3.14159,然后让你来猜。你第一次猜1,我说“小了”;你第二次猜5,我说“大了”;你第三次猜3,我说“接近了,但还差一点”……通过不断猜测和我的反馈,你最终能逼近我心中的那个数字。这个过程,本质上就是一种函数逼近——你用一个简单的猜测(一个数字),去逼近一个复杂的、未知的目标(我心中的数字)。

现在,把这个游戏升级一下。在三维重建的世界里,比如3D高斯泼溅(3DGS)技术中,我们面对的不是一个简单的数字,而是一个会随着观察角度变化而改变颜色的物体表面。想象一个光滑的陶瓷花瓶,你从正面看,它反射着窗户的光,呈现亮白色;你从侧面看,它可能映出窗帘的蓝色;从顶部俯视,又带点天花板的灰色。对于3DGS中的每一个“高斯椭球”(你可以把它想象成一个微小的、有形状和方向的彩色小泡泡),我们需要回答一个更复杂的问题:“从任意一个角度看过去,这个泡泡应该是什么颜色?”

这就不再是猜一个固定数字了,而是猜一个函数——一个输入是观察方向(两个角度),输出是RGB颜色的函数。这个函数可能非常复杂、不规则。3DGS解决这个难题的“法宝”,就是球谐函数。但别怕,球谐函数听起来高大上,其核心思想和“猜数字”游戏,以及我们更熟悉的泰勒展开、傅里叶级数,是一脉相承的:用一组简单的、已知的“基函数”进行线性组合,去表达或逼近一个复杂的、未知的函数。

这篇文章,我就带你抛开那些令人望而生畏的数学符号,用最直观的类比和可视化的方式,一步步建立起从泰勒展开、傅里叶级数到球谐函数的认知阶梯。你会发现,3DGS中那看似神秘的颜色表达,背后其实是一种优雅而实用的数学思想。无论你是刚入门三维重建的开发者,还是对深度学习渲染感兴趣的研究者,理解这套“基函数组合”的直觉,都将让你对3DGS乃至更广泛的神经渲染技术,有更深刻、更通透的认识。

2. 数学工具箱:三种“以简驭繁”的逼近思想

在深入球谐函数之前,我们必须先打好基础,理解数学中“用简单构建复杂”的通用策略。泰勒展开、傅里叶级数和球谐函数,正是这一策略在不同战场上的三位杰出代表。

2.1 泰勒展开:用“多项式”在一点附近展开函数

泰勒展开可能是我们最熟悉的一位。它的目标很明确:在一个特定的点(比如x=0)附近,用一个多项式来无限逼近某个复杂函数。

生活类比:这就像用乐高积木搭建一条复杂的曲线。最初你只用一块最长的直积木(0阶,常数项),只能搭出一条水平线,误差很大。然后你加入一些带角度的积木(1阶,一次项),能让这条线开始有斜率,更贴近目标曲线在起点处的走向。接着,你加入更小的、带有弧度的特殊积木(2阶、3阶等高阶项),就能让搭出来的线条开始弯曲,越来越贴合目标曲线的每一个细微起伏。你用的特殊形状积木越多(阶数越高),最终拼出来的线条就越像原来的复杂曲线。

数学核心:泰勒展开的“基函数”是幂函数:1,(x-a),(x-a)^2,(x-a)^3, ...。每个基函数前面都有一个系数,这个系数由原函数在该点a的各阶导数决定。所以,学习(或者说“拟合”)的过程,就是确定这些系数。

让我们用代码直观感受一下,如何用不同阶数的泰勒多项式去逼近自然指数函数e^x

import numpy as np import matplotlib.pyplot as plt from math import factorial # 定义原函数 e^x def original_func(x): return np.exp(x) # 计算e^x在x=0处的n阶泰勒展开 def taylor_expansion(x, n): """计算e^x在x=0处的n阶泰勒展开""" result = np.zeros_like(x) for k in range(n+1): result += x**k / factorial(k) # 这就是基函数 (x**k) 和系数 (1/k!) 的组合 return result # 生成数据点 x = np.linspace(-2, 2, 400) y_true = original_func(x) # 计算不同阶数的逼近 orders = [0, 1, 2, 3, 5] approximations = {f'{order}阶': taylor_expansion(x, order) for order in orders} # 绘图对比 plt.figure(figsize=(10, 6)) plt.plot(x, y_true, 'k-', linewidth=3, label='原函数: $e^x$') colors = ['blue', 'green', 'orange', 'red', 'purple'] for (label, y_approx), color in zip(approximations.items(), colors): plt.plot(x, y_approx, '--', linewidth=1.5, alpha=0.8, color=color, label=label) plt.axvline(x=0, color='gray', linestyle=':', alpha=0.5, label='展开点 x=0') plt.xlabel('x') plt.ylabel('f(x)') plt.title('泰勒展开逼近 $e^x$:阶数越高,逼近范围越广') plt.legend() plt.grid(True, alpha=0.3) plt.show()

运行这段代码,你会清晰地看到:在x=0附近,即使只有3阶多项式,也已经和e^x几乎重合。但一旦x远离0点,低阶多项式的误差就会迅速拉大,而5阶多项式则能在更大的范围内保持较好的拟合。这揭示了一个关键点:泰勒展开是一种“局部”逼近,它在展开点附近效果极佳,但距离越远,所需阶数越高。

2.2 傅里叶级数:用“正弦波”分解周期信号

如果说泰勒展开擅长处理局部平滑的函数,那么傅里叶级数则是处理周期性现象的王者。它的核心思想是:任何周期性的波动,无论多复杂,都可以分解成一系列不同频率、不同振幅的简单正弦波和余弦波的叠加。

生活类比:这就像一位调音师在分析一段复杂的音乐。他听到的是一段完整的和弦或旋律(复杂周期函数)。但他的耳朵(或仪器)能将其分解成多个单一频率的声音:一个厚重的低音(基频)、一个明亮的中音(二次谐波)、一个清脆的高音(三次谐波)等等。每个单一频率的声音就是一个纯净的正弦波(基函数)。这段复杂音乐,就是所有这些纯净正弦波以特定强度(系数)同时播放的结果。调音师通过调整各个音轨的音量(系数),就能精确地重建或修改那段音乐。

数学核心:傅里叶级数的基函数是sin(nωt)cos(nωt),其中n是整数,ω是基频。a_nb_n就是我们需要学习的系数,它们代表了每个频率分量所占的“权重”。

下面的代码演示了如何用三个不同频率的正弦波,合成一个新的波形。

import numpy as np import matplotlib.pyplot as plt # 时间轴 t = np.linspace(0, 4*np.pi, 1000) omega = 1 # 基频 # 定义三个简单的“基函数”——正弦波 base_wave1 = np.sin(omega * t) # 基波 base_wave2 = 0.5 * np.sin(2*omega * t) # 二次谐波,振幅减半 base_wave3 = 0.3 * np.sin(3*omega * t) # 三次谐波,振幅更小 # 线性组合:合成复杂波形 complex_wave = base_wave1 + base_wave2 + base_wave3 # 可视化 fig, axes = plt.subplots(4, 1, figsize=(12, 8), sharex=True, gridspec_kw={'hspace': 0.4}) axes[0].plot(t, base_wave1, 'b-', label='基波: sin(t)') axes[0].set_ylabel('振幅') axes[0].legend() axes[0].set_title('基函数1') axes[1].plot(t, base_wave2, 'g-', label='二次谐波: 0.5*sin(2t)') axes[1].set_ylabel('振幅') axes[1].legend() axes[1].set_title('基函数2') axes[2].plot(t, base_wave3, 'r-', label='三次谐波: 0.3*sin(3t)') axes[2].set_ylabel('振幅') axes[2].legend() axes[2].set_title('基函数3') axes[3].plot(t, complex_wave, 'k-', linewidth=2, label='合成波') axes[3].plot(t, base_wave1, 'b:', alpha=0.5) axes[3].plot(t, base_wave2, 'g:', alpha=0.5) axes[3].plot(t, base_wave3, 'r:', alpha=0.5) axes[3].set_xlabel('时间 [弧度]') axes[3].set_ylabel('振幅') axes[3].legend() axes[3].set_title('线性组合结果: 基波 + 二次谐波 + 三次谐波') plt.suptitle('傅里叶级数思想:复杂波形由简单正弦波叠加而成', fontsize=14) plt.show()

你会发现,合成后的黑色波形已经不再是简单的正弦曲线,它有了更丰富的细节和特征。傅里叶级数的强大之处在于,理论上,只要使用足够多的正弦余弦波(足够高的阶数),就能以任意精度表示任何周期函数。这是一种全局逼近,关注的是整个周期内的整体形态。

2.3 球谐函数:将“基函数组合”思想推向球面

现在,让我们把视角从一维的数轴、二维的时间轴,提升到三维的球面。球谐函数要解决的问题是:如何表示一个定义在球面(所有可能的方向)上的函数?例如,地球表面的温度分布、一个星球的光照强度,或者——对于我们最关键的——一个三维物体表面从不同方向看过去的颜色

核心直觉:球谐函数就是定义在球面上的傅里叶级数。傅里叶级数用不同频率的正弦波作为基函数,来分解一维周期信号。球谐函数则用一组定义在球面上的特殊正交函数(Y_l^m(θ, φ))作为基函数,来分解球面上的二维信号(比如颜色分布)。

为什么是“球面”?因为观察方向可以用一个单位向量表示,这个向量的所有可能终点,就构成了一个球面。θ(极角)和 φ(方位角)就是这个球面上的坐标。

基函数长什么样?球谐函数的基函数Y_l^m看起来比正弦波复杂,它们是由连带勒让德多项式和三角函数组合而成的。但我们可以直观地理解它们:每个Y_l^m都代表了球面上一种特定的“振动模式”

  • 阶数l:决定了模式的“复杂程度”。l=0是常数函数(整个球面一个值);l=1是类似“偶极子”的分布(正负半球);l=2是“四极子”模式;l越高,模式在球面上的变化就越快、越精细。
  • 次数m:在同一个阶数l下,m-l变化到l,它决定了这种“振动模式”在方位角 φ 方向上的具体形态,可以理解为不同的“谐波”。

下面的表格对比了这三种逼近方法,帮你快速建立联系:

特性泰勒展开 (Taylor)傅里叶级数 (Fourier)球谐函数 (Spherical Harmonics)
定义域一维实数轴(局部)一维周期区间(全局)三维单位球面(全局)
基函数幂函数(x-a)^n正弦/余弦函数sin(nωt), cos(nωt)球谐基函数Y_l^m(θ, φ)
系数意义函数在展开点处的各阶导数值各频率分量的振幅和相位球面函数在该“振动模式”上的投影强度
主要用途局部函数近似、求极限信号处理、频域分析球面函数近似、全局光照、3DGS颜色表达
直观比喻用乐高积木在一点附近拼曲线将复杂和弦分解成单个音符用一套“球面图案模板”去描述一个球面上的花纹

所以,当你看到球谐函数那复杂的公式时,心里可以把它想象成一套标准的、覆盖整个球面的“图案模板库”。而我们要表示的任何球面函数(比如颜色分布),都可以看作是这些标准模板以不同权重复合叠加的结果。阶数l决定了我们模板库的“精细度”,l越大,模板的细节越多,能表达的颜色变化就越复杂。

3. 3DGS中的球谐函数:如何让颜色“活”起来

理解了球谐函数作为“球面图案模板库”的本质,我们现在来看看3DGS是如何巧妙地运用这个工具,解决“颜色随视角变化”这个核心渲染问题的。

3.1 从固定颜色到方向函数

在传统的点云或体素渲染中,一个3D点通常只有一个固定的RGB颜色。这显然不符合现实世界中的非朗伯体表面特性。现实中的物体,如金属、陶瓷、水面、丝绸,其颜色和亮度会随着你观察角度的改变而发生显著变化(即视角依赖的外观)。

3DGS的每个高斯椭球,本质上是一个微小的、可学习的局部表面元。如果只给它一个固定颜色,渲染出来的物体就会像塑料或石膏一样,缺乏光泽和质感。为了让渲染结果逼真,我们必须为每个高斯赋予一个颜色函数c = f(θ, φ),其中输入是观察方向(θ, φ),输出是RGB颜色。

直接学习一个任意的、从方向到颜色的映射函数是极其困难的,因为这是一个无限维的问题。而球谐函数提供了一条完美的捷径:我们不需要直接学习这个复杂的函数f,只需要学习一组有限的系数,用这组系数去组合已知的球谐基函数,从而间接地表达f

具体操作如下:

  1. 确定阶数L:3DGS原始论文默认使用3阶球谐函数。这意味着我们使用从l=0l=3的所有基函数。每个l对应(2l+1)个基函数(m-ll)。所以总基函数数量为:1 (l=0) + 3 (l=1) + 5 (l=2) + 7 (l=3) =16个
  2. 为每个高斯学习一组系数:对于每个高斯椭球,我们不再只存储一个RGB向量,而是存储16组RGB系数。每一组系数(c_l^m_R, c_l^m_G, c_l^m_B)对应一个基函数Y_l^m在红、绿、蓝三个颜色通道上的权重。
  3. 渲染时实时计算:当我们需要从某个特定方向d = (θ, φ)渲染这个高斯时,我们就做一次“查表+加权求和”:
    • “查表”:根据方向(θ, φ),计算出那16个球谐基函数Y_l^m(θ, φ)的值。这一步是纯数学计算,不包含任何可学习参数,速度很快。
    • “加权求和”:将这个高斯存储的16组RGB系数,分别与对应的基函数值相乘,然后对所有结果求和,最终得到这个方向上的预测RGB颜色。

用公式表示就是:颜色(θ, φ) = Σ_{l=0}^{3} Σ_{m=-l}^{l} (系数_RGB_l^m) * Y_l^m(θ, φ)

这个过程,完全类比于傅里叶级数:系数是我们需要学习的“乐谱”,基函数值是固定不变的“音符”,而渲染就是根据当前“时间”(观察方向)演奏出对应的“旋律”(颜色)。

3.2 系数学习与可视化理解

在3DGS的训练过程中,优化器(如Adam)会不断地调整每个高斯的这16组RGB系数。训练的目标是:对于所有训练图片,从图片对应的相机视角方向去计算每个高斯贡献的颜色,并将所有高斯混合后的结果,与训练图片的像素颜色尽可能匹配。

一开始,这些系数是随机初始化的,所以预测的颜色乱七八糟。随着训练迭代,优化器通过梯度下降,逐步调整这些系数,使得球谐函数的加权和(即预测的颜色函数)越来越接近该高斯点在真实世界中从各个角度被观察到的颜色。

为了让你对3阶球谐函数(16个基函数)的表达能力有个直观感受,我们可以看看它们各自代表的“图案模板”是什么样子。下面的代码可视化前几阶的实值球谐基函数(3DGS中使用的是实值形式)。

import numpy as np import matplotlib.pyplot as plt from matplotlib import cm from scipy.special import sph_harm # 生成球面坐标网格 theta = np.linspace(0, np.pi, 100) # 极角 [0, pi] phi = np.linspace(0, 2*np.pi, 100) # 方位角 [0, 2pi] Theta, Phi = np.meshgrid(theta, phi) # 转换为笛卡尔坐标用于3D绘图 X = np.sin(Theta) * np.cos(Phi) Y = np.sin(Theta) * np.sin(Phi) Z = np.cos(Theta) # 选择要可视化的几个低阶基函数 harmonics_to_plot = [(0,0), (1,0), (1,1), (2,0), (2,2)] # (l, m) 组合 fig = plt.figure(figsize=(15, 8)) for idx, (l, m) in enumerate(harmonics_to_plot): ax = fig.add_subplot(2, 3, idx+1, projection='3d') # 计算球谐函数值 (使用实部) Ylm = sph_harm(abs(m), l, Phi, Theta) if m < 0: Ylm = np.sqrt(2) * (-1)**m * Ylm.imag elif m > 0: Ylm = np.sqrt(2) * (-1)**m * Ylm.real else: Ylm = Ylm.real # 将函数值映射为颜色和径向距离 # 颜色表示正负(红正,蓝负),径向距离表示绝对值大小 norm = Ylm / np.max(np.abs(Ylm)) if np.max(np.abs(Ylm)) > 0 else Ylm facecolors = cm.coolwarm((norm + 1) / 2) # 归一化到[0,1]用于着色 R = 0.5 + 0.5 * np.abs(norm) # 半径在0.5到1之间变化,体现振幅 X_plot = R * X Y_plot = R * Y Z_plot = R * Z surf = ax.plot_surface(X_plot, Y_plot, Z_plot, facecolors=facecolors, rcount=50, ccount=50, linewidth=0, antialiased=True, shade=False) ax.set_title(f'$Y_{{{l}}}^{{{m}}}$', fontsize=16) ax.set_axis_off() ax.view_init(elev=20, azim=45) plt.suptitle('低阶球谐基函数可视化 (颜色表示正负,形状表示振幅)', fontsize=18) plt.tight_layout() plt.show()

运行这段代码,你会看到几个像气球一样的3D形状。Y_0^0是一个均匀的球,代表常数项。Y_1^0像一个“哑铃”,从北极到南极由正变负。Y_2^2则呈现出更复杂的四叶草形状。这些形状就是我们的“模板”。3DGS学习到的系数,决定了在最终的颜色函数中,每个模板的“贡献”有多大。低阶(l=0,1)的模板捕捉大范围的、平滑的颜色变化(比如漫反射底色);高阶(l=2,3)的模板则捕捉更快速的、细节的颜色变化(比如高光、镜面反射的细节)。

3.3 工程实现中的关键细节与效率考量

在实际的3DGS代码实现中,球谐函数的应用有几个非常精妙且实用的设计,这些设计平衡了表达能力和计算效率。

1. 使用实数形式与预计算原始的球谐函数定义中包含复数。但在计算机图形学中,我们处理的是实数值(RGB颜色)。因此,3DGS使用的是实值球谐函数,它是复值球谐函数的线性组合,确保输出是实数。更重要的是,由于基函数Y_l^m(θ, φ)只依赖于方向,而与具体的高斯点无关,因此可以预计算

在渲染时,对于给定的观察方向d(通常是相机到高斯中心的单位向量),我们只需要计算一次这16个基函数的值,得到一个长度为16的向量SH_basis。然后,对于视线方向上的每一个高斯点,我们执行一次高效的点积操作:颜色 = (高斯点的16个RGB系数) · SH_basis。这相当于一次小型矩阵乘法,在现代GPU上可以并行化处理成千上万个高斯点,速度极快。

2. 阶数的选择:3阶的权衡为什么是3阶,而不是2阶或4阶?这是一个经典的精度与存储/计算开销的权衡

  • 0阶:退化为固定颜色,无视角变化。
  • 1阶:能表达简单的颜色梯度(类似线性变化),可以模拟基本的漫反射 shading。
  • 2阶:可以表达更丰富的二次变化,能开始捕捉一些简单的高光效果。
  • 3阶:论文实验表明,3阶已经能够很好地表达大多数场景中视角依赖的外观,包括较为复杂的高光和材质变化。
  • 4阶及以上:表达能力更强,但每个高斯需要存储的系数数量急剧增加(4阶需要25组RGB系数)。这会显著增加显存占用和计算量,但对最终渲染质量的提升在多数场景下边际效益递减。

因此,3阶是一个经过实践验证的“甜点”。下表对比了不同阶数的开销:

球谐阶数 (L)基函数数量 (K = (L+1)²)每个高斯所需系数数量 (RGB * K)表达能力描述
013固定颜色,无视角变化
1412可表达简单的颜色梯度
3 (默认)1648能很好表达常见高光与材质
42575能表达更精细的反射细节
536108表达能力极强,但开销巨大

3. 与高斯椭球属性的协同球谐系数是高斯的可学习属性之一,与其他属性(如位置、协方差矩阵定义的椭球形状、不透明度)一起被优化。在可微分渲染的框架下,从像素颜色损失反向传播的梯度,会同时更新高斯的形状、位置和它的球谐系数。这意味着,系统会自动学习将颜色变化与几何形状对齐。例如,一个代表金属边缘的高斯,其球谐系数会学习到强烈的镜面反射模式;而一个代表粗糙墙面的高斯,其系数则更接近漫反射的平缓模式。

4. 从理论到代码:一个极简的球谐颜色计算示例

为了彻底打通“最后一公里”,让我们抛开庞大的3DGS框架,写一个最核心的、计算给定方向球谐颜色的Python函数。这将让你亲手触摸到理论的实现。

假设我们已经有了一个高斯点的16组RGB系数(这里为了演示,我们随机生成),以及一个归一化的观察方向向量view_dir。我们的任务是计算这个方向上的颜色。

import numpy as np import math def compute_sh_basis(view_dir, max_degree=3): """ 计算3阶实值球谐基函数在给定方向上的值。 参数: view_dir: 归一化的3D观察方向向量 (x, y, z)。 max_degree: 球谐最大阶数,默认为3。 返回: basis: 长度为 (max_degree+1)^2 的向量,包含所有基函数值。 """ x, y, z = view_dir # 确保是单位向量 norm = np.sqrt(x*x + y*y + z*z) if norm > 0: x, y, z = x/norm, y/norm, z/norm # 预计算一些常用值,提升效率 x2, y2, z2 = x*x, y*y, z*z xy, xz, yz = x*y, x*z, y*z x2_y2 = x2 - y2 z2_3 = 3*z2 - 1 # 用于l=2, m=0的简化计算 # 初始化基函数列表,顺序为: l=0; l=1(m=-1,0,1); l=2(m=-2,-1,0,1,2); l=3(m=-3,-2,-1,0,1,2,3) basis = [] # l=0 basis.append(0.28209479177387814) # Y00 = sqrt(1/(4pi)) # l=1 basis.append(0.4886025119029199 * y) # Y1-1 basis.append(0.4886025119029199 * z) # Y10 basis.append(0.4886025119029199 * x) # Y11 # l=2 basis.append(1.0925484305920792 * xy) # Y2-2 basis.append(1.0925484305920792 * yz) # Y2-1 basis.append(0.31539156525252005 * z2_3) # Y20 (注意:此处是简化后的实值形式) basis.append(1.0925484305920792 * xz) # Y21 basis.append(0.5462742152960396 * x2_y2) # Y22 # l=3 (3阶,共7个基函数) basis.append(0.5900435899266435 * y * (3*x2 - y2)) # Y3-3 basis.append(2.890611442640554 * xy * z) # Y3-2 basis.append(0.4570457994644658 * y * (5*z2 - 1)) # Y3-1 (简化形式) basis.append(0.3731763325901154 * z * (5*z2 - 3)) # Y30 (简化形式) basis.append(0.4570457994644658 * x * (5*z2 - 1)) # Y31 (简化形式) basis.append(1.445305721320277 * z * x2_y2) # Y32 basis.append(0.5900435899266435 * x * (x2 - 3*y2)) # Y33 return np.array(basis) def compute_color_from_sh(view_dir, sh_coeffs_r, sh_coeffs_g, sh_coeffs_b): """ 根据观察方向和球谐系数计算颜色。 参数: view_dir: 观察方向。 sh_coeffs_r/g/b: 分别为RGB通道的16个球谐系数。 返回: color: (R, G, B) 三元组,值在合理范围内(可能需后续裁剪)。 """ # 1. 计算基函数值 basis = compute_sh_basis(view_dir) # 2. 分别对RGB通道进行加权求和(点积) r = np.dot(sh_coeffs_r, basis) g = np.dot(sh_coeffs_g, basis) b = np.dot(sh_coeffs_b, basis) # 3. 通常会用sigmoid激活函数将输出约束到[0,1]范围,这里为演示省略 return np.array([r, g, b]) # ===== 模拟一个高斯点的数据 ===== # 假设我们有一个高斯点,其球谐系数是随机初始化的(实际中由优化器学习) np.random.seed(42) sh_coeffs_r = np.random.randn(16) * 0.1 # R通道的16个系数 sh_coeffs_g = np.random.randn(16) * 0.1 # G通道 sh_coeffs_b = np.random.randn(16) * 0.1 # B通道 # ===== 测试从不同方向观察该点的颜色 ===== # 定义几个不同的观察方向(单位向量) view_directions = [ np.array([0, 0, 1]), # 从正前方看 (沿z轴) np.array([1, 0, 0]), # 从右侧看 (沿x轴) np.array([0, 1, 0]), # 从上方看 (沿y轴) np.array([1, 1, 1]) / np.sqrt(3), # 从一个斜角看 ] print("模拟高斯点从不同方向观察的颜色(RGB值):") for i, vd in enumerate(view_directions): color = compute_color_from_sh(vd, sh_coeffs_r, sh_coeffs_g, sh_coeffs_b) # 简单裁剪到[0,1]模拟sigmoid,仅用于演示 color_clipped = np.clip(color, 0, 1) print(f" 方向 {i+1} ({vd.round(2)}): R={color_clipped[0]:.3f}, G={color_clipped[1]:.3f}, B={color_clipped[2]:.3f}") print(f" (原始加权和: R={color[0]:.3f}, G={color[1]:.3f}, B={color[2]:.3f})")

这段代码做了几件关键事情:

  1. compute_sh_basis函数实现了3阶实值球谐基函数的快速计算。它直接使用了预计算的常数和方向向量的分量组合,避免了复杂的三角函数和连带勒让德多项式计算,这是工程中常见的优化。
  2. compute_color_from_sh函数执行核心的加权求和操作。这就是3DGS渲染管线中每个高斯点颜色计算的核心步骤。
  3. 我们用随机系数模拟了一个高斯点,并计算从四个不同方向看它的颜色。你会看到,即使系数是随机的,不同方向计算出的颜色值也已经不同了。在真实的训练中,优化器会调整这些系数,使得从某个方向计算出的颜色,接近于该高斯点在该方向被拍摄到的真实颜色。

通过这个极简示例,你应该能清晰地感受到,球谐函数在3DGS中并非一个黑盒。它是一个高效的、可微分的“函数逼近器”,将方向映射到颜色的复杂问题,转化为了学习一组系数和进行快速点乘的简单问题。这种将复杂数学工具优雅地嵌入可学习框架的思路,正是3DGS乃至许多现代深度学习渲染方法成功的关键。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 17:16:48

ClickHouse物化视图实战:从原理到高效应用

1. 物化视图&#xff1a;ClickHouse的“空间换时间”利器 大家好&#xff0c;我是老张&#xff0c;在数据仓库和实时分析领域摸爬滚打了十来年&#xff0c;用过不少数据库&#xff0c;ClickHouse算是我在应对海量数据实时查询时最得力的“老伙计”之一。今天想和大家深入聊聊Cl…

作者头像 李华
网站建设 2026/8/30 14:39:23

高效公式转换指南:从图片到MathType与Mathematica的无缝衔接

1. 从图片到公式&#xff1a;OCR工具的魔法时刻 你是不是也遇到过这种情况&#xff1f;读论文、看网页&#xff0c;突然看到一个特别复杂的公式&#xff0c;想把它“搬”到自己的文档里。手动敲&#xff1f;光是那些上下标、积分符号、分式结构就让人头皮发麻&#xff0c;更别提…

作者头像 李华
网站建设 2026/7/14 17:16:52

模电课设实战:用NTC热敏电阻+LM358搭建水温控制系统的5个关键细节

模电课设实战&#xff1a;用NTC热敏电阻LM358搭建水温控制系统的5个关键细节 又到了电子工程专业同学们挠头的模电课设季。每年这个时候&#xff0c;实验室里总能见到对着面包板和一堆元器件发呆的身影&#xff0c;其中“水温控制系统”算是个经典又磨人的项目。看起来原理简单…

作者头像 李华
网站建设 2026/7/14 17:16:51

GoCron并发控制终极指南:单例模式和限流策略的完整实践

GoCron并发控制终极指南&#xff1a;单例模式和限流策略的完整实践 【免费下载链接】gocron Easy and fluent Go cron scheduling. This is a fork from https://github.com/jasonlvhit/gocron 项目地址: https://gitcode.com/gh_mirrors/goc/gocron GoCron作为一款简洁…

作者头像 李华