3D Gaussian Splatting部分原理介绍和CUDA代码解读(一)——3D/2D协方差和高斯颜色的计算

本系列文章旨在让3D GS的初学者或未接触过CUDA程序的读者能看懂代码内容。

3D GS论文原文链接:[2308.04079] 3D Gaussian Splatting for Real-Time Radiance Field Rendering

论文笔记链接:【论文笔记】3D Gaussian Splatting for Real-Time Radiance Field Rendering

【论文笔记】A Survey on 3D Gaussian Splatting 这篇综述的第3章也有详细介绍3D GS的方法。

官方代码链接:可微栅格化的CUDA代码(本系列文章介绍的代码均在此repo内);3D GS完整代码


本文介绍的代码若未单独说明,均位于gaussian-splatting/submodules/diff-gaussian-rasterization/cuda_rasterizer/forward.cu中。

第一部分 ------ 前向传播

3D高斯溅射的CUDA代码主要负责栅格化部分,其结构如下图所示。

本教程会在介绍每部分代码前,简单介绍代码涉及的基本原理和基本概念。

1. 3D协方差计算

1.1 数学基础

协方差矩阵的分解。根据

  1. 对称矩阵AA可通过正交对角化分解为A=RDRTA=RDRT,其中RR为正交矩阵,DD为对角矩阵,对角元素为矩阵AA的特征值。
  2. 协方差矩阵ΣΣ是半正定对称矩阵。

可得:协方差矩阵可分解为Σ=RSSTRTΣ=RSSTRT(其中SS为对角矩阵,且SST=S2=DSST=S2=D,因为ΣΣ的特征值均非负)。

若将RR视为旋转矩阵并转化为单位四元数表示qq,SS的对角元组成的向量ss视为尺度向量,则有R=q2r(q),S=diag(s)R=q2r(q),S=diag(s),其中q2r(⋅)q2r(⋅)表示将四元数转化为旋转矩阵。
分解原因:直接估计协方差矩阵难以满足其半正定对称性。

四元数与旋转矩阵

  1. 单位四元数qq满足∥q∥2=1∥q∥2=1。非单位四元数q′q′可通过q=q′/∥q′∥2q=q′/∥q′∥2转化为单位四元数。
  2. 单位四元数q=(qx,qy,qz,qw)q=(qx,qy,qz,qw)转化为旋转矩阵RR的公式:

R=[1−2qy2−2qz22qxqy−2qzqw2qxqz+2qyqw2qxqy+2qzqw1−2qx2−2qz22qyqz−2qxqw2qxqz−2qyqw2qyqz+2qxqw1−2qx2−2qy2](1.1)R=​1−2qy2​−2qz2​2qx​qy​+2qz​qw​2qx​qz​−2qy​qw​​2qx​qy​−2qz​qw​1−2qx2​−2qz2​2qy​qz​+2qx​qw​​2qx​qz​+2qy​qw​2qy​qz​−2qx​qw​1−2qx2​−2qy2​​​(1.1)

1.2 OpenGL/GLM中的矩阵存储方式

和python不同,OpenGL/GLM库的矩阵存储是列优先的。

例如,矩阵M=[1234]M=[13​24​]的存储顺序(即拉平后的数组)是[1,3,2,4][1,3,2,4]。 使用代码glm::mat2 M = glm::mat2(1,3,2,4)可创建上述矩阵MM。

1.3 相关代码解读

3D协方差计算代码为computeCov3D,功能是根据尺度向量ss和单位四元数qq计算3D协方差ΣΣ。

输入 :3维尺度向量scaless、尺度修正系数mod、旋转对应的单位四元数rotqq;

输出 :协方差矩阵cov3DΣΣ(仅保存上三角)。

复制代码
__device__ void computeCov3D(const glm::vec3 scale, float mod, const glm::vec4 rot, float* cov3D)
{
	// 尺度矩阵S=diag(mod*s)
	glm::mat3 S = glm::mat3(1.0f);	// 初始化3阶单位阵
	S[0][0] = mod * scale.x;	// 缩放因数不影响本质
	S[1][1] = mod * scale.y;
	S[2][2] = mod * scale.z;

        // rot和四元数的标准形式有着元素顺序上的差异,需进行转化
	glm::vec4 q = rot;
	float r = q.x;
	float x = q.y;
	float y = q.z;
	float z = q.w;

	// 旋转矩阵计算(式1.1),由于列优先存储方式,实际上是R^T。
	glm::mat3 R = glm::mat3(
		1.f - 2.f * (y * y + z * z), 2.f * (x * y - r * z), 2.f * (x * z + r * y),
		2.f * (x * y + r * z), 1.f - 2.f * (x * x + z * z), 2.f * (y * z - r * x),
		2.f * (x * z - r * y), 2.f * (y * z + r * x), 1.f - 2.f * (x * x + y * y)
	);

	// 协方差矩阵 Sigma = R * S * S * R'
	glm::mat3 M = S * R;
	glm::mat3 Sigma = glm::transpose(M) * M;

	// 由于对称性,仅保存协方差矩阵的上三角(由于列优先,下列代码实际存储的是下三角),共6个元素。
	cov3D[0] = Sigma[0][0];
	cov3D[1] = Sigma[0][1];
	cov3D[2] = Sigma[0][2];
	cov3D[3] = Sigma[1][1];
	cov3D[4] = Sigma[1][2];
	cov3D[5] = Sigma[2][2];
}

注意由于列优先存储方式,因此代码中的R实际上是RTRT。Sigma的结果和前面的公式Σ=RSSTRTΣ=RSSTRT一致。

2. 2D协方差计算

2.1 基本原理

世界坐标系到摄像机坐标系的坐标变换。设点在世界坐标系下的坐标为X=[X,Y,Z]TX=[X,Y,Z]T,在摄像机坐标系下的坐标为XCXC​。记摄像机外参矩阵E=[R∣t]E=[R∣t],Xˉ=[X,Y,Z,1]TXˉ=[X,Y,Z,1]T为增加一维的向量,则有关系

XC=EXˉ(2.1)XC​=EXˉ(2.1)

XC=RX+t(2.2)XC​=RX+t(2.2)

3D点X=[X,Y,Z]TX=[X,Y,Z]T在图像上的投影。设摄像机的3×43×4投影矩阵为PP,则dxˉ=PXˉdxˉ=PXˉ。其中x=(u,v)Tx=(u,v)T为投影的像素坐标,d=Zd=Z为深度。

若摄像机的3×43×4投影矩阵为P=K[I∣0],K=[fx0cx0fycy001]P=K[I∣0],K=​fx​00​0fy​0​cx​cy​1​​,则点XC=(XC,YC,ZC)TXC​=(XC​,YC​,ZC​)T(实际上是摄像机坐标系下的坐标)的投影坐标为

x=(u,v)T=(fxxz+cx,fyyz+cy)T(2.3)x=(u,v)T=(zfx​x​+cx​,zfy​y​+cy​)T(2.3)

投影的线性近似。由于涉及到射影变换,椭球(3D高斯)投影到图像上一般不是标准的椭圆(2D高斯)。因此,2D高斯只是投影的近似表达,可使用投影变换的线性近似来获得。

根据式(2.3),可求P=K[I∣0]P=K[I∣0]投影变换下的雅可比矩阵

J=[∂u/∂XC∂u/∂YC∂u/∂ZC∂v/∂XC∂v/∂YC∂v/∂ZC]=[fx/ZC0−fxXC/ZC20fy/ZC−fyYC/ZC2](2.4)J=[∂u/∂XC​∂v/∂XC​​∂u/∂YC​∂v/∂YC​​∂u/∂ZC​∂v/∂ZC​​]=[fx​/ZC​0​0fy​/ZC​​−fx​XC​/ZC2​−fy​YC​/ZC2​​](2.4)

因此,高斯中心μμ处对应的线性近似为x=J∣XC=μCXC+bx=J∣XC​=μC​​XC​+b。

3D协方差ΣΣ在图像上的投影:设摄像机的3×43×4投影矩阵为P=K[R∣t]P=K[R∣t],则先将ΣΣ变换到摄像机坐标系下:ΣC=RΣRTΣC​=RΣRT。此时,投影矩阵变为P=K[I∣0]P=K[I∣0],其线性近似为x=J∣XC=μCXC+bx=J∣XC​=μC​​XC​+b。进行这样的线性变换后,协方差变为Σ′=JΣCJT=JRΣRTJTΣ′=JΣC​JT=JRΣRTJT。

2.2 相关代码解读

2D协方差计算代码为computeCov2D,功能是获取投影后2D高斯的协方差矩阵Σ′Σ′。

输入 :高斯3D均值mean(X,Y,Z)(X,Y,Z),相机焦距focal_x,focal_y,相机最大视角正切值tan_fovx,tan_fovy,3D协方差(上三角)cov3DΣΣ,以及(扩维到4×44×4并拉平的)摄像机外参矩阵viewmatrix

返回值:2D协方差矩阵Σ′Σ′(上三角)。

复制代码
__device__ float3 computeCov2D(const float3& mean, float focal_x, float focal_y, float tan_fovx, float tan_fovy, const float* cov3D, const float* viewmatrix)
{
	// 将高斯均值利用摄像机外参矩阵转换到摄像机坐标系下(式(2.1))
	float3 t = transformPoint4x3(mean, viewmatrix);
	
	// 将视锥范围外部的高斯中心限制在范围内
	const float limx = 1.3f * tan_fovx;		// 1.3为缩放因子,稍微扩大裁剪范围,因为中心在边界外的高斯,可能仍然能对边界像素产生影响
	const float limy = 1.3f * tan_fovy;
	const float txtz = t.x / t.z;			// 计算像素坐标对应的正切值
	const float tytz = t.y / t.z;
	t.x = min(limx, max(-limx, txtz)) * t.z;	// 截断:若像素正切值大于limx(小于-limx)则会使用limx(-limx)替代
	t.y = min(limy, max(-limy, tytz)) * t.z;

	// 雅可比矩阵的计算(式2.4),注意由于列优先存储方式,实际上为J^L
	glm::mat3 J = glm::mat3(
		focal_x / t.z, 0.0f, -(focal_x * t.x) / (t.z * t.z),
		0.0f, focal_y / t.z, -(focal_y * t.y) / (t.z * t.z),
		0, 0, 0);				// 此处多一行不影响,计算结果取左上角2x2矩阵即可
	
	// 投影矩阵左上角3x3子矩阵的转置 即 W=R^T
	glm::mat3 W = glm::mat3(
		viewmatrix[0], viewmatrix[4], viewmatrix[8],
		viewmatrix[1], viewmatrix[5], viewmatrix[9],
		viewmatrix[2], viewmatrix[6], viewmatrix[10]);

	glm::mat3 T = W * J;				// T = R^T * J^T

	glm::mat3 Vrk = glm::mat3(			// 3D协方差矩阵
		cov3D[0], cov3D[1], cov3D[2],
		cov3D[1], cov3D[3], cov3D[4],
		cov3D[2], cov3D[4], cov3D[5]);
	
	// 2D协方差矩阵 Cov2D = J * R * Sigma * R^T * J^T
	glm::mat3 cov = glm::transpose(T) * glm::transpose(Vrk) * T;
	
	// 由于对称性,仅返回上三角(实际上是下三角),共三个元素
	return { float(cov[0][0]), float(cov[0][1]), float(cov[1][1]) };	
}

注意代码中W为RTRT,J为JTJT。2D协方差计算公式与Σ′=JRΣRTJTΣ′=JRΣRTJT一致。

  • 代码中涉及的transformPoint4x3()函数:

    复制代码
    __forceinline__ __device__ float3 transformPoint4x3(const float3& p, const float* matrix)
    {
    	float3 transformed = {
    		matrix[0] * p.x + matrix[4] * p.y + matrix[8] * p.z + matrix[12],
    		matrix[1] * p.x + matrix[5] * p.y + matrix[9] * p.z + matrix[13],
    		matrix[2] * p.x + matrix[6] * p.y + matrix[10] * p.z + matrix[14],
    	};
    	return transformed;
    }

    可以看到,matrix是下列4×44×4矩阵拉平后的结果(注意GLM库是列优先存储矩阵的)

    M=[M0M4M8M12M1M5M9M13M2M6M10M14M3M7M11M15](2.5)M=​M0​M1​M2​M3​​M4​M5​M6​M7​​M8​M9​M10​M11​​M12​M13​M14​M15​​​(2.5)

    故该函数的输出为

    p1′p2′p3′\]=\[M0M4M8M12M1M5M9M13M2M6M10M14\]\[p1p2p31\]​p1′​p2′​p3′​​​=​M0​M1​M2​​M4​M5​M6​​M8​M9​M10​​M12​M13​M14​​​​p1​p2​p3​1​​ 因此,该函数把输入矩阵MM的前3行与向量pˉ=\[pT,1\]Tpˉ​=\[pT,1\]T进行矩阵相乘。 > 当MM为扩维的摄像机外参矩阵时,该函数将世界坐标系下的点转化到摄像机坐标系下。

3.1 基本原理

球面谐波函数。在函数的泰勒展开式f(x)=∑i=0∞akxkf(x)=∑i=0∞​ak​xk或傅里叶展开式f(x)=∑i=0∞bksin⁡(kx)f(x)=∑i=0∞​bk​sin(kx)中,{xk}k=0∞{xk}k=0∞​或{sin⁡(kx)}k=0∞{sin(kx)}k=0∞​可称为一组基函数,也即函数f(x)f(x)被表达为一组基函数的线性组合。

点击3D Gaussian Splatting部分原理介绍和CUDA代码解读(一)------3D/2D协方差和高斯颜色的计算查看全文

相关推荐
opentrending1 小时前
Github 热点项目 awesome-mcp-servers MCP 服务器合集,3分钟实现AI模型自由操控万物!
服务器·人工智能·github
lisw051 小时前
DeepSeek原生稀疏注意力(Native Sparse Attention, NSA)算法介绍
人工智能·深度学习·算法
whaosoft-1432 小时前
51c深度学习~合集4
人工智能
逢生博客2 小时前
阿里 FunASR 开源中文语音识别大模型应用示例(准确率比faster-whisper高)
人工智能·python·语音识别·funasr
淮北4942 小时前
ros调试工具foxglove使用指南三:在3d空间写写画画(Panel->3D ->Scene entity)
python·学习·3d·机器人
哲讯智能科技3 小时前
智慧能源新篇章:SAP如何赋能光伏行业数字化转型
大数据·人工智能
云卓SKYDROID3 小时前
无人机DSP处理器工作要点!
人工智能·无人机·科普·云卓科技
gang_unerry3 小时前
量子退火与机器学习(2):少量实验即可找到新材料,黑盒优化➕量子退火
人工智能·机器学习·量子计算·量子退火
訾博ZiBo3 小时前
AI日报 - 2025年4月2日
人工智能
说私域3 小时前
消费品行业创新创业中品类创新与数字化工具的融合:以开源 AI 智能客服、AI 智能名片及 S2B2C 商城小程序为例
人工智能·小程序·开源