3dgs代码前向传播部分
先来讨论一下glm,因为定义变量的时候用到了这个。
glm的解释
glm
是指 OpenGL Mathematics,这是一个针对图形编程的数学库。它的全称是 OpenGL Mathematics (GLM),主要用于 OpenGL 的开发。这个库是基于 C++ 的模板库,设计目标是尽可能提供类似 GLSL(OpenGL 着色语言)的语法和功能,使得在 C++ 中使用数学运算时更加直观和方便。
GLM 提供了各种数学功能和数据结构,包括:
- 向量(vec2, vec3, vec4)
- 矩阵(mat2, mat3, mat4)
- 四元数(quaternion)
- 常见的数学函数(如平移、旋转、缩放、透视投影等)
在计算机视觉领域,处理几何变换和坐标系转换时,GLM 非常有用。例如,我们需要进行矩阵运算、向量运算等,都可以通过 GLM 提供的功能来方便地实现。使用方法如下所示。
1. 向量
glm::vec3 v1(1.0f, 2.0f, 3.0f);
glm::vec3 v2(4.0f, 5.0f, 6.0f);
glm::vec3 v3 = v1 + v2; // 向量加法
2. 矩阵
#include <glm/gtc/matrix_transform.hpp>
glm::mat4 identity = glm::mat4(1.0f); // 单位矩阵
glm::mat4 translation = glm::translate(identity, glm::vec3(10.0f, 0.0f, 0.0f)); // 平移矩阵
glm::mat4 rotation = glm::rotate(identity, glm::radians(45.0f), glm::vec3(0.0f, 1.0f, 0.0f)); // 旋转矩阵
3. 四元数
#include <glm/gtc/quaternion.hpp>
glm::quat q = glm::angleAxis(glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)); // 绕 Y 轴
3dgs的代码的forward部分
球谐函数的建立
submodules\diff-gaussian-rasterization\cuda_rasterizer\forward.cu
line 20
// Forward method for converting the input spherical harmonics
// coefficients of each Gaussian to a simple RGB color.
__device__ glm::vec3 computeColorFromSH(int idx, int deg, int max_coeffs, const glm::vec3* means, glm::vec3 campos, const float* shs, bool* clamped)
{
'''
这个函数基于 Zhang 等人 (2022) 的论文"Differentiable Point-Based Radiance Fields for Efficient View Synthesis" 中的代码实现
'''
// 获取当前点的中心位置
glm::vec3 pos = means[idx];
// 计算从相机位置到当前点的位置向量
glm::vec3 dir = pos - campos;
// 将位置向量归一化
dir = dir / glm::length(dir);
// 获取当前点的 SH 系数
glm::vec3* sh = ((glm::vec3*)shs) + idx * max_coeffs;
// 计算 SH 零阶系数的颜色值
glm::vec3 result = SH_C0 * sh[0];
// 如果阶数大于 0,则计算一阶 SH 系数的颜色值
if (deg > 0)
{
float x = dir.x;
float y = dir.y;
float z = dir.z;
result = result - SH_C1 * y * sh[1] + SH_C1 * z * sh[2] - SH_C1 * x * sh[3];
// 如果阶数大于 1,则计算二阶 SH 系数的颜色值
if (deg > 1)
{
float xx = x * x, yy = y * y, zz = z * z;
float xy = x * y, yz = y * z, xz = x * z;
result = result +
SH_C2[0] * xy * sh[4] +
SH_C2[1] * yz * sh[5] +
SH_C2[2] * (2.0f * zz - xx - yy) * sh[6] +
SH_C2[3] * xz * sh[7] +
SH_C2[4] * (xx - yy) * sh[8];
// 如果阶数大于 2,则计算三阶 SH 系数的颜色值
if (deg > 2)
{
result = result +
SH_C3[0] * y * (3.0f * xx - yy) * sh[9] +
SH_C3[1] * xy * z * sh[10] +
SH_C3[2] * y * (4.0f * zz - xx - yy) * sh[11] +
SH_C3[3] * z * (2.0f * zz - 3.0f * xx - 3.0f * yy) * sh[12] +
SH_C3[4] * x * (4.0f * zz - xx - yy) * sh[13] +
SH_C3[5] * z * (xx - yy) * sh[14] +
SH_C3[6] * x * (xx - 3.0f * yy) * sh[15];
}
}
}
// 为结果颜色值加上一个偏移量
result += 0.5f;
// 将 RGB 颜色值限制在正值范围内。如果值被限制,则需要在反向传播过程中记录此信息。
clamped[3 * idx + 0] = (result.x < 0);
clamped[3 * idx + 1] = (result.y < 0);
clamped[3 * idx + 2] = (result.z < 0);
return glm::max(result, 0.0f);
}
EWA投影来消除误差
submodules\diff-gaussian-rasterization\cuda_rasterizer\forward.cu
line 73
// Forward version of 2D covariance matrix computation
// 在 CUDA 设备上定义的函数,用于计算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)
{
// 该函数实现了 "EWA Splatting" (Zwicker et al., 2002) 中的公式29和31,
// 同时考虑了视口的纵横比/缩放。
// 转置用于处理行/列优先顺序。
// 将3D点转换到视图坐标系中
float3 t = transformPoint4x3(mean, viewmatrix);
// 定义x和y方向的视锥限制
const float limx = 1.3f * tan_fovx;
const float limy = 1.3f * tan_fovy;
const float txtz = t.x / t.z;
const float tytz = t.y / t.z;
// 限制x和y方向的值,使其不超过视锥限制
t.x = min(limx, max(-limx, txtz)) * t.z;
t.y = min(limy, max(-limy, tytz)) * t.z;
// 计算雅可比矩阵J,表示从3D到2D的投影变换
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);
// 提取视图矩阵的前3x3部分,用于线性变换
glm::mat3 W = glm::mat3(
viewmatrix[0], viewmatrix[4], viewmatrix[8],
viewmatrix[1], viewmatrix[5], viewmatrix[9],
viewmatrix[2], viewmatrix[6], viewmatrix[10]);
// 计算组合变换矩阵T
glm::mat3 T = W * J;
// 从输入的3D协方差矩阵中构建Vrk矩阵
glm::mat3 Vrk = glm::mat3(
cov3D[0], cov3D[1], cov3D[2],
cov3D[1], cov3D[3], cov3D[4],
cov3D[2], cov3D[4], cov3D[5]);
// 计算最终的2D协方差矩阵
glm::mat3 cov = glm::transpose(T) * glm::transpose(Vrk) * T;
// 应用低通滤波器:每个高斯函数应至少在一个像素宽/高
// 丢弃第3行和第3列
cov[0][0] += 0.3f;
cov[1][1] += 0.3f;
// 返回2D协方差矩阵的三个元素
return {
float(cov[0][0]), float(cov[0][1]), float(cov[1][1]) };
}
EWA Splatting论文中6.2.2节的描述
我们将视图变换与将相机坐标转换为射线坐标的投影变换连接起来,如图8所示。相机空间的定义是相机坐标系的原点在投影中心,投影平面是 t 2 = 1 t_2 = 1 t2=1 的平面。相机空间和射线空间通过映射 x = t \mathbf{x} = \mathbf{t} x=t相关联。使用第4.1节中的射线空间定义, t \mathbf{t} t及其逆映射 t − 1 \mathbf{t}^{-1} t−1可以表示为:
( x 0 x 1 x 2 ) = ϕ ( t ) = ( t 0 / t 2 t 1 / t 2 ∣ ∣ ( t 0 , t 1 , t 2 ) T ∣ ∣ ) (26) \begin{pmatrix} x_0 \\ x_1 \\ x_2 \end{pmatrix} =\phi(t)= \begin{pmatrix} t_0/t_2 \\ t_1/t_2 \\ ||(t_0,t_1,t_2)^T|| \end{pmatrix} \tag{26}
x0x1x2
=ϕ(t)=
t0/t2t1/t2∣∣(t0,t1,t2)T∣∣
(26)
( t 0 t 1 t 2 ) = ϕ − 1 ( x ) = ( x 0 / l ⋅ x 2 x 1 / l ⋅ x 2 1 / l ⋅ x 2 ) (27) \begin{pmatrix} t_0 \\ t_1 \\ t_2 \end{pmatrix} =\phi^{-1}(x)= \begin{pmatrix} x_0/l \cdot x_2 \\ x_1/l \cdot x_2 \\ 1/l \cdot x_2 \end{pmatrix} \tag{27} t0t1t2 =ϕ−1(x)= x0/l⋅x2x1/l⋅x21/l⋅x2 (27)
其中, l = ∣ ∣ ( x 0 , x 1 , 1 ) ∣ ∣ T l = ||(x_0, x_1, 1)||^T l=∣∣(x0,x1,1)∣∣T。
不幸的是,这些映射不是仿射的,因此我们不能直接应用公式(21): G V n ( Φ − 1 ( u ) − p ) = 1 ∣ M − 1 ∣ G M V M T n ( u − Φ ( p ) ) G^n_V(\Phi^{-1}(u)-p)=\frac{1}{|M^{-1}|}G^{n}_{MVM^T}(u-\Phi(p)) GVn(Φ−1(u)−p)=∣