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),仅供参考