☰
Spirula Studio 的 SH 旋转符号约定全解:Ivanic-Ruedenberg 递归与 Condon-Shortley 相位
2026/9/30 7:29:17 网站建设 项目流程

Spirula Studio 的 SH 旋转符号约定全解:Ivanic-Ruedenberg 递归与 Condon-Shortley 相位

【免费下载链接】spirula-studioCross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA.项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio

Spirula Studio 是一款跨厂商的 3D Gaussian Splatting(3D 高斯泼溅)训练器,能把视频/照片训练成 splat 模型并进一步导出网格,支持 Vulkan 与 CUDA 后端。本文深入它的 SH 旋转实现,讲清 Ivanic-Ruedenberg 递归闭式解与 Condon-Shortley 相位这两个符号约定是如何咬合在一起的。

为什么 SH 系数必须跟着模型一起旋转 🔄

3DGS 里每个高斯的颜色分两部分:

  • DC 分量(零阶球谐):固定颜色,与视角无关;
  • 高阶球谐系数(l = 1..4):描述"从哪个方向看是什么颜色"的视角相关项,在高斯处沿相机→高斯的单位方向d求值:
colour(d) = Σ c_lm · Y_lm(d)

几何一旋转,方向就从d变成R·d。如果只转几何、不动这组系数,高光会"钉"在旧世界里,物体在它下面打转。于是旋转后的系数c'必须满足对任意d都成立:

colour'(R·d) = colour(d)

这是整个实现的出发点,完整的推导见 docs/notes/sh-rotation.md。

频带旋转矩阵 M_l:旋转不串频带

球谐旋转有一个漂亮的性质:不同频带(band)之间不混合。对每个l,存在一个正交的(2l+1) × (2l+1)矩阵M_l(R),满足Y_l(R·d) = M_l(R) · Y_l(d)。代入上式后可以直接解出系数变换:

c'_l = M_l(R) · c_l

由此带来两个实用结论:

  • 零阶(DC 颜色)和透明度在任何摆放变换中都原封不动;
  • 平移和等比缩放不改变任何方向,真正起作用的是旋转R。

闭式解:Ivanic-Ruedenberg 递归

求M_l最直观的办法是采样法:在球面上取大量方向,分别在d和R·d处求值基函数,再解一个最小二乘问题。它"显然正确",但每次旋转都要解一次线性方程组,精度还受限于采样密度。

Spirula Studio 采用的是Ivanic-Ruedenberg 递归(J. Phys. Chem. 100:6342, 1996,含 1998 勘误):只用一阶矩阵M_1和上一阶M_(l-1),就能递推出高阶M_l——闭式解,无需采样,l=1..4 全部频带只需几百次乘法。

工程上更划算的是"一次建矩阵,处处复用":一次摆放对应整个模型的一次旋转,矩阵只建一次,之后每个高斯每频带每通道只是一次矩阵-向量乘,四阶时共 3 × (9 + 25 + 49 + 81) 次乘法。核心实现在 src/core/ShRotation.cpp,由 src/checkpoint/SplatTransform.h 消费。

符号约定陷阱:Condon-Shortley 相位 ⚠️

问题出在递归的"原始出处"上:Ivanic-Ruedenberg 递归是针对不带 Condon-Shortley 相位的实球谐写的。在那个"无相位"基里,一阶频带是(y, z, x),M_1就是行、列同序置换后的R本身。

但 Spirula Studio 的着色器 src/shaders/harmonics.slang 的基函数带着相位:它的一阶频带是(-y, z, -x),一般地,每个Y_lm比无相位版本多一个(-1)^m的因子。

设S_l为这些符号构成的对角矩阵,则两套基的旋转矩阵相差一次共轭:

M_l^engine = S_l · M_l^plain · S_l

也就是说:M_l的第(m, n)个元素,恰好当m + n为奇数时变号。src/core/ShRotation.cpp 先无相位地跑完递归,最后再套上这层共轭:

跳过这一步是经典错误,而且是"安静"的错误:一阶和三阶频带会错在符号上,随手看一眼渲染画面根本发现不了。项目里是在写任何 C++ 之前就用原型代码抓住它的——无相位版本每个频带误差达到 1 的量级,共轭后的版本与采样拟合一致到 1e-15。

双重验证:从单元测试到端到端渲染

符号约定对不对,光靠推导不够,Spirula Studio 用了两层防线:

第一层:单元测试对采样拟合。src/core/tests/sh_rotation_test.cpp 把harmonics.slang里 25 个基函数常数对常数地誊抄进测试,覆盖随机旋转加每个轴的 90°/180° 旋转,检查:

  • 每个频带矩阵与采样最小二乘拟合一致(实测 2.8e-15);
  • 矩阵正交性、M(R1·R2) = M(R1)·M(R2)组合律;
  • 0~4 阶随机系数经apply()旋转后,满足colour'(R·d) = colour(d)。

第二层:真实渲染闭环。src/backend/tests/engine/splat_transform_render.cpp 渲染一个视角依赖很强的随机场景,用transform_splats把相似变换"烘焙"进 splat,再把相机做同样的变换重新渲染——两张图必须一致(实测 3DGS / mip / 3DGUT、针孔 / 鱼眼、SH 3 阶 / 4 阶下平均差异仅 3e-7)。对照组只移动几何而不动 SH,差异为 0.07~0.13,肉眼可辨,证明测试确实"看得见"它要测的东西。

核心文件一览 📁

内容路径
推导与验证思路(设计笔记)docs/notes/sh-rotation.md
SH 旋转矩阵与系数变换src/core/ShRotation.h、src/core/ShRotation.cpp
引擎实际求值的 25 个实球谐基函数src/shaders/harmonics.slang
模型摆放/相似变换入口src/checkpoint/SplatTransform.h
符号约定单元测试src/core/tests/sh_rotation_test.cpp
端到端渲染验证src/backend/tests/engine/splat_transform_render.cpp

一句话总结:Ivanic-Ruedenberg 递归给你高效的闭式旋转矩阵,Condon-Shortley 相位则要求最后补一次对角共轭——少了它,高斯会带着"错位的光"转起来,而这正是 Spirula Studio 用原型和双层测试提前堵死的坑。

【免费下载链接】spirula-studioCross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA.项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询