对于3D高斯 (Gaussian Splatting) Forward&Backward的粗浅总结

笔者初学小白,有错误的地方欢迎指正!

Basic Math Knowledge

只看前向传播的话,知道协方差矩阵和特征值是啥就可,看反向传播的话需要知道矩阵求梯度的计算法则,建议读一下参考文献的[1];

  • (1) 协方差矩阵是啥[4]:
    椭球方程(半轴长分别是 a , b , c a,b,c a,b,c):
    ( x − x ˉ ) a 2 + ( y − y ˉ ) 2 b 2 + ( z − z ˉ ) 2 z 2 = 1 \frac{(x-\bar{x})}{a^2}+\frac{(y-\bar{y})^2}{b^2}+\frac{(z-\bar{z})^2}{z^2} = 1 a2(xxˉ)+b2(yyˉ)2+z2(zzˉ)2=1
    但是这椭球的三个轴都是平行于坐标轴的,假设对这个椭球的每个点 ( x , y , z − x ˉ , y ˉ , z ˉ ) (x,y,z-\bar{x},\bar{y},\bar{z}) (x,y,zxˉ,yˉ,zˉ) ( x ˉ , y ˉ , z ˉ ) (\bar{x},\bar{y},\bar{z}) (xˉ,yˉ,zˉ)为中心都做一步旋转 R R R,就可以得到一个任意姿态的三维空间的椭球方程 ( x n e w , y n e w , z n e w ) (x_{new}, y_{new}, z_{new}) (xnew,ynew,znew)
    V ( x y z ) = [ x − x ˉ , y − y ˉ , z − z ˉ ] T V(xyz)= [x-\bar{x}, y-\bar{y}, z-\bar{z}]^T V(xyz)=[xxˉ,yyˉ,zzˉ]T
    V ( x y z ) = R − 1 V ( x y z n e w ) V(xyz) = R^{-1}V(xyz_{new}) V(xyz)=R1V(xyznew)
    椭球公式变成:
    ( R − 1 [ 0 , : ] ⋅ V ( x y z n e w ) ) 2 a 2 + ( R − 1 [ 1 , : ] ⋅ V ( x y z n e w ) ) 2 b 2 + ( R − 1 [ 2 , : ] ⋅ V ( x y z n e w ) ) 2 c 2 = 1 \frac{(R^{-1}[0,:] \cdot V(xyz_{new}) )^2}{a^2} + \frac{(R^{-1}[1,:] \cdot V(xyz_{new}) )^2}{b^2} + \frac{(R^{-1}[2,:] \cdot V(xyz_{new}) )^2}{c^2} = 1 a2(R1[0,:]V(xyznew))2+b2(R1[1,:]V(xyznew))2+c2(R1[2,:]V(xyznew))2=1

    ( R − 1 [ 0 , 0 ] 2 a 2 + R − 1 [ 1 , 0 ] 2 b 2 + R − 1 [ 2 , 0 ] 2 c 2 ) ( x n e w − x ˉ ) 2 + ( R − 1 [ 0 , 1 ] 2 a 2 + R − 1 [ 1 , 1 ] 2 b 2 + R − 1 [ 2 , 1 ] 2 c 2 ) ( y n e w − y ˉ ) 2 + ( R − 1 [ 0 , 2 ] 2 a 2 + R − 1 [ 1 , 2 ] 2 b 2 + R − 1 [ 2 , 2 ] 2 c 2 ) ( z n e w − z ˉ ) 2 + ( . . . ) ( x n e w − x ˉ ) ( y n e w − y ˉ ) + ( . . . ) ( y n e w − y ˉ ) ( z n e w − z ˉ ) + ( . . . ) ( x n e w − x ˉ ) ( z n e w − z ˉ ) = 1 (\frac{R^{-1}[0,0]^2}{a^2} + \frac{R^{-1}[1,0]^2}{b^2} + \frac{R^{-1}[2,0]^2}{c^2}) (x_{new} - \bar{x})^2 + \\(\frac{R^{-1}[0,1]^2}{a^2} + \frac{R^{-1}[1,1]^2}{b^2} + \frac{R^{-1}[2,1]^2}{c^2}) (y_{new} - \bar{y})^2 + \\(\frac{R^{-1}[0,2]^2}{a^2} + \frac{R^{-1}[1,2]^2}{b^2} + \frac{R^{-1}[2,2]^2}{c^2}) (z_{new} - \bar{z})^2 + \\(...) (x_{new} - \bar{x})(y_{new} - \bar{y}) + \\ (...) (y_{new} - \bar{y})(z_{new} - \bar{z}) + \\(...)(x_{new} - \bar{x})(z_{new} - \bar{z}) = 1 (a2R1[0,0]2+b2R1[1,0]2+c2R1[2,0]2)(xnewxˉ)2+(a2R1[0,1]2+b2R1[1,1]2+c2R1[2,1]2)(ynewyˉ)2+(a2R1[0,2]2+b2R1[1,2]2+c2R1[2,2]2)(znewzˉ)2+(...)(xnewxˉ)(ynewyˉ)+(...)(ynewyˉ)(znewzˉ)+(...)(xnewxˉ)(znewzˉ)=1

    ⇒ \Rightarrow
    [ x n e w − x ˉ y n e w − y ˉ z n e w − z ˉ ] R − 1 [ 1 a 2 1 b 2 1 c 2 ] ( R − 1 ) T [ x n e w − x ˉ y n e w − y ˉ z n e w − z ˉ ] = 1 \begin{bmatrix} x_{new}-\bar{x} & y_{new}-\bar{y} & z_{new}-\bar{z} \end{bmatrix} R^{-1} \begin{bmatrix} \frac{1}{a^2} & & \\ & \frac{1}{b^2} & \\ & & \frac{1}{c^2} \end{bmatrix} (R^{-1})^T \begin{bmatrix} x_{new}-\bar{x} \\ y_{new}-\bar{y} \\ z_{new}-\bar{z} \end{bmatrix} = 1 [xnewxˉynewyˉznewzˉ]R1 a21b21c21 (R1)T xnewxˉynewyˉznewzˉ =1
    这个新的矩阵就是 ( Σ 3 × 3 ) − 1 (\Sigma_{3\times 3})^{-1} (Σ3×3)1, 特征值的定义是: ∣ X − λ I ∣ = 0 |X - \lambda I| = 0 XλI=0
    ∣ R − 1 [ 1 a 2 1 b 2 1 c 2 ] ( R − 1 ) T − λ I ∣ = 0 ∣ [ 1 a 2 1 b 2 1 c 2 ] R − λ R ∣ = 0 ⇒ λ = [ 1 a 2 1 b 2 1 c 2 ] | R^{-1} \begin{bmatrix} \frac{1}{a^2} & & \\ & \frac{1}{b^2} & \\ & & \frac{1}{c^2} \end{bmatrix} (R^{-1})^T - \lambda I | = 0 \\ |\begin{bmatrix} \frac{1}{a^2} & & \\ & \frac{1}{b^2} & \\ & & \frac{1}{c^2} \end{bmatrix}R - \lambda R| = 0 \\ \Rightarrow \lambda = \begin{bmatrix} \frac{1}{a^2} & & \\ & \frac{1}{b^2} & \\ & & \frac{1}{c^2} \end{bmatrix} R1 a21b21c21 (R1)TλI=0 a21b21c21 RλR=0λ= a21b21c21
    二阶椭圆方程也同理,因此在求出2D投影的协方差矩阵之后要求逆然后特征值是两个半轴的长度;

  • (2) ∇ \nabla Δ \Delta Δ分别是啥:
    假设系统的最终输出是 S o S_o So, S o = f ( C ) , C = g ( A , B ) S_o = f(C),C = g(A, B) So=f(C),C=g(A,B)
    俺们可以得出:
    ∇ C : = d S o d C ∇ A : = ∇ C ∂ C ∂ A \nabla C := \frac{d S_o}{dC} \\ \nabla A := \nabla C \frac{\partial C}{\partial A} C:=dCdSoA:=CAC
    很好,我学了老半天也没学会矩阵求导🐷,问题不大,下面的推导都是逐元素的,只要会高三数学的求导就可以理解。

Vanilla 3DGS, SIGGRAPH2023

https://github.com/graphdeco-inria/gaussian-splatting

概述

(主函数在diff-gaussian-rasterization/cuda_rasterizer/rasterizer_impl.cu/"int CudaRasterizer::Rasterizer::forward(...)"

  • STEP 1: 分配 ( p + 255 / 256 ) (p+255/256) (p+255/256)个block, 每个block有256个thread, 对每个高斯做preprocessCUDA(...);
  • STEP 2: 生成buffer并对高斯做排序;
  • STEP 3: 分配 n u m _ t i l e s num\_tiles num_tiles个block,每个block有256个thread,对每个pixel做渲染;

STEP 1

  • diff-gaussian-rasterization/cuda_rasterizer/forward.cu/"FORWARD::preprocess"/"preprocessCUDA":
    • 计算 Σ 2 × 2 \Sigma_{2 \times 2} Σ2×2
    • 计算gaussians的投影半径radii, 计算gaussians属于哪个tiletiles_touched
    • 如果用的sh,将sh转成rgb;
    • 记录gaussians的像素坐标points_xy_image;
    • [ Σ 2 × 2 , o p a c i t y ] [\Sigma_{2\times 2},opacity] [Σ2×2,opacity]存成conic_opacity
    • 记录gaussians接触到的tiles的数目tiles_touched,注意这里是数目不是序号;

STEP 2

  • duplicateWithKeyscub::DeviceRadixSort::SortPairs:
    • 创建buffer并记录每个gaussiangaussian_values_unsorted touch到了哪些tile_idsgaussian_keys_unsorted
    // diff-gaussian-rasterization/cuda_rasterizer/rasterizer_impl.cu
    __global__ void duplicateWithKeys
    // gaussian_keys_unsorted.shape[0] = sum(each_gaussian_touch_tiles_num)
    // gaussian_keys_unsorted的每一项记录的是tile的索引;
    // gaussian_values_unsorted的每一项记录的是gaussian的索引;
    
    • cub::DeviceRadixSort::SortPairs是CUB库的一个内置函数,经过排序后得到①point_list: 排序之后的 tilegaussians 对应的gaussians_id; ② point_list_keys: 排序之后的 tilegaussians 对应的 tile_id;
    • identifyTileRanges: 这里直接按照tile_id切开就行,然后得到的imgState.ranges是dim2, shape=(N_tile, 2),用这个可以直接索引每个tile下的gaussians_id们;

STEP 3

渲染人们比较熟悉,分成<numtiles_unumtiles_v, 1616>渲染,每个thread是这样的:

  • diff-gaussian-rasterization/cuda_rasterizer/forward.cu/"renderCUDA(...)": 这个函数的最外层的for循环是针对tile滴;
    • 拿到当前pixel下的从前到后排好序的gaussians_ids:
      uint2 range = ranges[block.group_index().y * horizontal_blocks + block.group_index().x];
    • 下面是两层循环,第一层循环的循环次数是toDo代表当前block(tile)下高斯数量;内层循环的循环次数是BLOCK_SIZE(256),代表对当前tile的pixel数;
    • 迭代每个高斯之后要block.sync()即所有当前block下的所有thread等同步;
    • 对于内层循环干的活儿很简单:
      • 计算 G G G:
      float power = -0.5f * (con_o.x * d.x * d.x + con_o.z * d.y * d.y) - con_o.y * d.x * d.y;
      
      • 计算 α \alpha α并按3通道循环累加颜色(如果 α < 1 / 255 \alpha<1/255 α<1/255或者后面的累计不透明度T<1e-4 都直接跳过,不再做累加颜色运算)
    • 结尾还有一个循环次数为256的小循环,就是把上面计算的结果赋值给pixel;
      梯度传播图
      这里的球谐是根据sh直接线性计算出来的,我这里的 c c c就是计算好的colors_precomp
      在这里插入图片描述

Forward👉

① ② ( q , s ) ⇒ Σ 3 × 3 (q,s) \Rightarrow \Sigma_{3\times 3} (q,s)Σ3×3

  • 数学公式很简单:
    q = [ w x y z ] T / w 2 + x 2 + y 2 + z 2 R = [ 1 − 2 q 2 2 − 2 q 3 2 2 q 1 q 2 − 2 q 0 q 3 2 q 1 q 3 + 2 q 0 q 2 2 q 1 q 2 + 2 q 0 q 3 1 − 2 q 1 2 − 2 q 3 2 2 q 2 q 3 − 2 q 0 q 1 2 q 1 q 3 − 2 q 0 q 2 2 q 2 q 3 + 2 q 0 q 1 1 − 2 q 1 2 − 2 q 2 2 ] Σ 3 × 3 = R S ( R S ) T q = \begin{bmatrix} w & x & y & z \end{bmatrix}^T / \sqrt{w^2+x^2+y^2+z^2} \\ R = \begin{bmatrix} 1 - 2q_2^2 - 2q_3^2 & 2q_1q_2 - 2q_0q_3 & 2q_1q_3 + 2q_0q_2 \\ 2q_1q_2 + 2q_0q_3 & 1 - 2q_1^2 - 2q_3^2 & 2q_2q_3 - 2q_0q_1 \\ 2q_1q_3 - 2q_0q_2 & 2q_2q_3 + 2q_0q_1 & 1 - 2q_1^2 - 2q_2^2 \end{bmatrix}\\ \Sigma_{3\times 3} = RS (RS)^T q=[wxyz]T/w2+x2+y2+z2 R= 12q222q322q1q2+2q0q32q1q32q0q22q1q22q0q312q122q322q2q3+2q0q12q1q3+2q0q22q2q32q0q112q122q22 Σ3×3=RS(RS)T
  • 代码里是这样的:
    # scene/gaussian_model.py
    self.rotation_activation = torch.nn.functional.normalize
    
    @property
    def get_rotation(self):
        return self.rotation_activation(self._rotation)
    
    # gaussian_render/__init__.py
    rotations = pc.get_rotation
    // diff-gaussian-rasterization/cuda_rasterizer/forward.cu
    // __global__ void preprocessCUDA(...)
    computeCov3D(scales[idx], scale_modifier, rotations[idx], cov3Ds + idx * 6);
    cov3D = cov3Ds + idx * 6;
    
    // diff-gaussian-rasterization/cuda_rasterizer/forward.cu
    // __device__ void computeCov3D(...)
    // Create scaling matrix
    glm::mat3 S = glm::mat3(1.0f);
    S[0][0] = mod * scale.x;
    S[1][1] = mod * scale.y;
    S[2][2] = mod * scale.z;
    // Compute rotation matrix from quaternion
    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)
    );
    glm::mat3 M = S * R;
    // Compute 3D world covariance matrix Sigma
    glm::mat3 Sigma = glm::transpose(M) * M;
    // Covariance is symmetric, only store upper right
    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];
    

( J W , Σ 3 × 3 ) ⇒ Σ 2 × 2 (JW, \Sigma_{3\times 3}) \Rightarrow \Sigma_{2\times 2} (JW,Σ3×3)Σ2×2

  • 数学公式很简单:
    Σ 2 × 2 = J W Σ 3 × 3 ( J W ) T [ u   v ] = [ f x 0 c x   0 f y c y ] [ x z   y z   1 ] \Sigma_{2\times2} = JW \Sigma_{3\times 3} (JW)^T \\ \begin{bmatrix} u \\\ v \end{bmatrix} = \begin{bmatrix} f_x & 0 & c_x \\\ 0 & f_y & c_y \end{bmatrix} \begin{bmatrix} \frac{x}{z} \\\ \frac{y}{z} \\\ 1 \end{bmatrix} Σ2×2=JWΣ3×3(JW)T[u v]=[fx 00fycxcy] zx zy 1
  • 代码里是这样的:
    // diff-gaussian-rasterization/cuda_rasterizer/forward.cu
    // __global__ void preprocessCUDA(...)
    float3 p_orig = { orig_points[3 * idx], orig_points[3 * idx + 1], orig_points[3 * idx + 2] };
    float3 cov = computeCov2D(p_orig, focal_x, focal_y, tan_fovx, tan_fovy, cov3D, viewmatrix);
    
    // ↓
    
    // diff-gaussian-rasterization/cuda_rasterizer/forward.cu
    //  __device__ float3 computeCov2D(...)
    float3 t = transformPoint4x3(mean, viewmatrix);
    // 区别一
    // tan_fovx = H/2 / fv 感觉就是 cv/fv
    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;
    t.x = min(limx, max(-limx, txtz)) * t.z;
    t.y = min(limy, max(-limy, tytz)) * t.z;
    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);
    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;
    glm::mat3 Vrk = glm::mat3(
        cov3D[0], cov3D[1], cov3D[2],
        cov3D[1], cov3D[3], cov3D[4],
        cov3D[2], cov3D[4], cov3D[5]);
    glm::mat3 cov = glm::transpose(T) * glm::transpose(Vrk) * T;
    // 区别二
    // Apply low-pass filter: every Gaussian should be at least
    // one pixel wide/high. Discard 3rd row and column.
    cov[0][0] += 0.3f;
    cov[1][1] += 0.3f;
    return { float(cov[0][0]), float(cov[0][1]), float(cov[1][1]) };
    
  • 这里和公式有两处小区别:
    • 区别一:这里写的min,max很迷惑,写成if else就懂了
      limx = 1.3f * cv/fv;
      const float txtz = t.x / t.z;
      if txtz < -limx:
          txtz = -limx
      if txtz > limx:
          txtz = limx
      t.x *= txtz
    
    • 区别二:让高斯投影大一些,保证每个高斯的投影半径至少大于1个pixel。(感觉像是抑制低频采样高频的情况)

④⑤ ( Σ 2 × 2 , u v p i x , u v p r o j ) ⇒ G (\Sigma_{2\times 2}, uv_{pix},uv_{proj}) \Rightarrow G (Σ2×2,uvpix,uvproj)G & ( G , o ) ⇒ α (G, o)\Rightarrow \alpha (G,o)α

  • 数学公式是这样的:
    G = e x p ( − [ u − u ˉ v − v ˉ ] Σ 2 × 2 − 1 [ u − u ˉ v − v ˉ ] 2 ) α = G ⋅ o G = exp(-\frac{ \begin{bmatrix} u - \bar{u} & v - \bar{v} \end{bmatrix} \Sigma_{2 \times 2}^{-1} \begin{bmatrix} u - \bar{u} \\ v - \bar{v} \end{bmatrix} }{2} ) \\ \alpha = G \cdot o G=exp(2[uuˉvvˉ]Σ2×21[uuˉvvˉ])α=Go
  • 代码是这样的:
    // diff-gaussian-rasterization/cuda_rasterizer/forward.cu
    // __global__ void __launch_bounds__(BLOCK_X * BLOCK_Y) renderCUDA(...)
    // 对于循环
    for (int i = 0; i < rounds; i++, toDo -= BLOCK_SIZE)
        // 对于每个pixel循环
        for (int j = 0; !done && j < min(BLOCK_SIZE, toDo); j++)
            ...
            float2 xy = collected_xy[j];
            float2 d = { xy.x - pixf.x, xy.y - pixf.y };
            float4 con_o = collected_conic_opacity[j];
            float power = -0.5f * (con_o.x * d.x * d.x + con_o.z * d.y * d.y) - con_o.y * d.x * d.y;
            ...
            float alpha = min(0.99f, con_o.w * exp(power));
    

( α , c ) ⇒ r g b (\alpha, c) \Rightarrow rgb (α,c)rgb

  • 数学公式是这样的:
    r g b = ∑ i = 0 N ( ∏ j = 0 i ( 1 − α j ) ) α i rgb = \sum_{i=0}^{N} (\prod_{j=0}^{i} (1-\alpha_j)) \alpha_i rgb=i=0N(j=0i(1αj))αi
  • 代码是这样的:
    // diff-gaussian-rasterization/cuda_rasterizer/forward.cu
    // __global__ void __launch_bounds__(BLOCK_X * BLOCK_Y) renderCUDA(...)
    // 对于循环
    for (int i = 0; i < rounds; i++, toDo -= BLOCK_SIZE)
        // 对于每个pixel循环
        for (int j = 0; !done && j < min(BLOCK_SIZE, toDo); j++)
            ...
            float test_T = T * (1 - alpha);
            // Eq. (3) from 3D Gaussian splatting paper.
            for (int ch = 0; ch < CHANNELS; ch++)
                C[ch] += features[collected_id[j] * CHANNELS + ch] * alpha * T;
    
            T = test_T
    
    // __global__ void __launch_bounds__(BLOCK_X * BLOCK_Y) renderCUDA(...)
    // All threads that treat valid pixel write out their final
    // rendering data to the frame and auxiliary buffers.
    if (inside)
    {
        final_T[pix_id] = T;
        n_contrib[pix_id] = last_contributor;
        for (int ch = 0; ch < CHANNELS; ch++)
            out_color[ch * H * W + pix_id] = C[ch] + T * bg_color[ch];
    }
    

Backward👈

  • 反向传播的任务也很简单,根据 ∇ r g b ⇒ ( ∇ q , ∇ s , ∇ o , ∇ c ) \nabla rgb \Rightarrow (\nabla q, \nabla s, \nabla o, \nabla c) rgb(q,s,o,c),反传的主函数在diff-gaussian-rasterization/cuda_rasterizer/rasterizer_impl.cu/"RasterizeGaussiansBackwardCUDA(...)"/"CudaRasterizer::Rasterizer::backward(...)", 和前向传播正好相反,先BACKWARD::render(...)然后再BACKWARD::preprocess(...)

⑥⑤④ ∇ r g b ⇒ ( ∇ o , ∇ c , ∇ u v p r o j , ∇ ( ( ∑ 2 × 2 ) − 1 ) \nabla rgb \Rightarrow (\nabla o, \nabla c, \nabla uv_{proj}, \nabla ((\sum_{2 \times 2})^{-1}) rgb(o,c,uvproj,((2×2)1)

  • 数学公式是这样的:
    • ∇ r g b ⇒ ∇ c \nabla rgb \Rightarrow \nabla c rgbc
      r g b + = T i α i c i ∇ c + = ∇ r g b T i α i rgb += T_{i}\alpha_i c_i \\ \nabla c += \nabla rgb T_i \alpha_i rgb+=Tiαicic+=rgbTiαi
      ( Σ 2 × 2 ) − 1 = [ α β β γ ] (\Sigma_{2 \times 2})^{-1} = \begin{bmatrix} \alpha & \beta \\ \beta & \gamma \end{bmatrix} (Σ2×2)1=[αββγ]

    • ∇ r g b ⇒ ( ∇ o , ∇ u v p r o j , ∇ ( Σ 2 × 2 ) − 1 ) \nabla rgb \Rightarrow (\nabla o, \nabla uv_{proj}, \nabla (\Sigma_{2 \times 2})^{-1} ) rgb(o,uvproj,(Σ2×2)1)
      ∇ α i = ∇ r g b T i c i G = e x p ( − [ u − u ˉ v − v ˉ ] Σ 2 × 2 − 1 [ u − u ˉ v − v ˉ ] 2 ) = e x p [ ( u − u ˉ ) 2 α + 2 ( u − u ˉ ) ( v − v ˉ ) β + ( v − v ˉ ) 2 γ − 2 ] α = G ⋅ o \nabla \alpha_i = \nabla rgb T_i c_i \\ G = exp(-\frac{ \begin{bmatrix} u - \bar{u} & v - \bar{v} \end{bmatrix} \Sigma_{2 \times 2}^{-1} \begin{bmatrix} u - \bar{u} \\ v - \bar{v} \end{bmatrix} }{2} ) = exp[\frac{(u-\bar{u})^2\alpha+ 2(u-\bar{u})(v-\bar{v})\beta + (v-\bar{v})^2 \gamma}{-2}] \\ \alpha = G \cdot o αi=rgbTiciG=exp(2[uuˉvvˉ]Σ2×21[uuˉvvˉ])=exp[2(uuˉ)2α+2(uuˉ)(vvˉ)β+(vvˉ)2γ]α=Go
      ∇ o = ∇ α ⋅ G \nabla o = \nabla \alpha \cdot G o=αG

∇ G = ∇ α ⋅ o \nabla G = \nabla \alpha \cdot o G=αo
∇ u ˉ = ∇ G ⋅ G ⋅ [ ( u − u ˉ ) α + ( v − v ˉ ) β ] ∇ v ˉ = ∇ G ⋅ G ⋅ [ ( v − v ˉ ) γ + ( u − u ˉ ) β ] \nabla \bar{u} = \nabla G \cdot G \cdot [ (u-\bar{u})\alpha + (v-\bar{v})\beta]\\ \nabla \bar{v} = \nabla G \cdot G \cdot [ (v-\bar{v})\gamma + (u-\bar{u})\beta] uˉ=GG[(uuˉ)α+(vvˉ)β]vˉ=GG[(vvˉ)γ+(uuˉ)β]

∇ α = ∇ G ⋅ G ⋅ ( u − u ˉ ) 2 − 2 ∇ β = ∇ G ⋅ G ⋅ ( u − u ˉ ) ( v − v ˉ ) − 1 ∇ γ = ∇ G ⋅ G ⋅ ( v − v ˉ ) 2 − 2 \nabla \alpha = \nabla G \cdot G \cdot \frac{(u-\bar{u})^2}{-2}\\ \nabla \beta = \nabla G \cdot G \cdot \frac{(u-\bar{u})(v - \bar{v})}{-1} \\ \nabla \gamma = \nabla G \cdot G \cdot \frac{(v-\bar{v})^2}{-2} α=GG2(uuˉ)2β=GG1(uuˉ)(vvˉ)γ=GG2(vvˉ)2

  • 代码是这样的:
    • ∇ r g b ⇒ ∇ c \nabla rgb \Rightarrow \nabla c rgbc
      // diff-gaussian-rasterization/cuda_rasterizer/backward.cu
      // renderCUDA(...)
      const float dchannel_dcolor = alpha * T;
      const float dL_dchannel = dL_dpixel[ch];
      
      atomicAdd(&(dL_dcolors[global_id * C + ch]), dchannel_dcolor * dL_dchannel);
        - $$\nabla rgb \Rightarrow (\nabla o, \nabla uv_{proj}, \nabla (\Sigma_{2 \times 2})^{-1} )$$
          - `gdx`是$$G \cdot (v-\bar{v})$$
      // diff-gaussian-rasterization/cuda_rasterizer/backward.cu
      // renderCUDA(...)
      const float2 d = { xy.x - pixf.x, xy.y - pixf.y };
      const float gdx = G * d.x;
      const float gdy = G * d.y;
      
      const float ddelx_dx = 0.5 * W;
      const float dL_dG = con_o.w * dL_dalpha;
      const float dG_ddelx = -gdx * con_o.x - gdy * con_o.y;
      atomicAdd(&dL_dmean2D[global_id].x, dL_dG * dG_ddelx * ddelx_dx);
      
      atomicAdd(&dL_dconic2D[global_id].x, -0.5f * gdx * d.x * dL_dG);
      
      // \nabla opacity.
      atomicAdd(&(dL_dopacity[global_id]), G * dL_dalpha);
      

③②① ( ∇ u v p r o j , ∇ ( ( Σ 2 × 2 ) − 1 ) ⇒ ( ∇ s , ∇ q , ∇ x y z ) (\nabla uv_{proj}, \nabla ((\Sigma_{2 \times 2})^{-1}) \Rightarrow (\nabla s, \nabla q, \nabla xyz) (uvproj,((Σ2×2)1)(s,q,xyz)

  • 数学公式是这样的:
    • ∇ u v p r o j ⇒ ∇ x y z \nabla uv_{proj} \Rightarrow \nabla xyz uvprojxyz
      ∇ x = ∇ u ∂ u ∂ y + ∇ v ∂ v ∂ x 记 w 2 c 为 ( R , t ) : u = f u ⋅ y c a m z c a m + c u = f u ⋅ R 21 x + R 22 y + R 23 z + t 2 R 31 x + R 32 y + R 33 z + t 3 + c u v = f u ⋅ y c a m z c a m + c u = f v ⋅ R 11 x + R 12 y + R 13 z + t 2 R 31 x + R 32 y + R 33 z + t 3 + c v ∇ x = ∇ u ⋅ R 21 z c a m − R 31 y c a m z c a m 2 + ∇ v ⋅ R 11 z c a m − R 31 x c a m z c a m 2 ( ∇ y , ∇ z 一样,此处省略) \nabla x = \nabla u \frac{\partial u}{\partial y} + \nabla v \frac{\partial v}{\partial x} \\ 记w2c为(R,t): \\u = fu\cdot \frac{y_{cam}}{z_{cam}} + cu= fu \cdot \frac{R_{21}x+R_{22}y+R_{23}z+t_2}{R_{31}x+R_{32}y+R_{33}z+t_3} + cu \\ v = fu\cdot \frac{y_{cam}}{z_{cam}} + cu= fv \cdot \frac{R_{11}x+R_{12}y+R_{13}z+t_2}{R_{31}x+R_{32}y+R_{33}z+t_3} + cv \\ \nabla x = \nabla u \cdot \frac{R_{21}z_{cam}-R_{31}y_{cam}}{z_{cam}^2}+ \nabla v \cdot \frac{R_{11}z_{cam}-R_{31}x_{cam}}{z_{cam}^2} \\(\nabla y, \nabla z 一样,此处省略) x=uyu+vxvw2c(R,t):u=fuzcamycam+cu=fuR31x+R32y+R33z+t3R21x+R22y+R23z+t2+cuv=fuzcamycam+cu=fvR31x+R32y+R33z+t3R11x+R12y+R13z+t2+cvx=uzcam2R21zcamR31ycam+vzcam2R11zcamR31xcamy,z一样,此处省略)

    • ∇ ( Σ 2 × 2 ) − 1 ⇒ ∇ Σ 3 × 3 \nabla (\Sigma_{2\times 2})^{-1} \Rightarrow \nabla \Sigma_{3\times 3} (Σ2×2)1Σ3×3
      M : = R S ∇ ( Σ 3 × 3 ) = ∇ ( Σ 2 × 2 − 1 ) ⋅ ∂ [ ( M Σ 3 × 3 M T ) − 1 ] ∂ Σ 3 × 3 M := RS\\\nabla (\Sigma_{3\times3}) = \nabla (\Sigma_{2\times 2}^{-1}) \cdot \frac{\partial [(M \Sigma_{3 \times 3} M^T)^{-1} ]}{\partial \Sigma_{3 \times 3}} M:=RS(Σ3×3)=(Σ2×21)Σ3×3[(MΣ3×3MT)1]

很好我是废物,这种复杂矩阵求导不会算,那就拆开写变量哈;

Σ 2 × 2 = [ a b b c ] ( Σ 2 × 2 ) − 1 = [ c − b − b a ] / ( a c − b 2 ) : = [ α β β γ ] { ∇ a = ∇ α ⋅ − c 2 ( a c − b 2 ) 2 + ∇ β ⋅ 2 b c ( a c − b 2 ) 2 + ∇ γ ⋅ − b 2 ( a c − b 2 ) 2 ∇ b = ∇ α ⋅ 2 b c ( a c − b 2 ) 2 + ∇ β ⋅ − a c − b 2 ( a c − b 2 ) 2 + ∇ γ ⋅ 2 a b ( a c − b 2 ) 2 ∇ c = ∇ α ⋅ − b 2 ( a c − b 2 ) 2 + ∇ β ⋅ a b ( a c − b 2 ) 2 + ∇ γ ⋅ − a 2 ( a c − b 2 ) 2 \Sigma_{2\times 2} = \begin{bmatrix} a & b\\ b & c \end{bmatrix} \\ (\Sigma_{2\times 2})^{-1} = \begin{bmatrix} c & -b \\ -b & a \end{bmatrix} /(ac-b^2) := \begin{bmatrix} \alpha & \beta \\ \beta & \gamma \end{bmatrix} \\ \left\{\begin{matrix} \nabla a = \nabla \alpha \cdot \frac{-c^2}{(ac-b^2)^2} + \nabla \beta \cdot \frac{2bc}{(ac-b^2)^2} + \nabla \gamma\cdot \frac{-b^2}{(ac-b^2)^2} \\ \nabla b = \nabla \alpha \cdot \frac{2bc}{(ac-b^2)^2} + \nabla \beta \cdot \frac{-ac-b^2}{(ac-b^2)^2} + \nabla \gamma\cdot \frac{2ab}{(ac-b^2)^2} \\ \nabla c = \nabla \alpha \cdot \frac{-b^2}{(ac-b^2)^2} + \nabla \beta \cdot \frac{ab}{(ac-b^2)^2} + \nabla \gamma\cdot \frac{-a^2}{(ac-b^2)^2} \end{matrix}\right. Σ2×2=[abbc](Σ2×2)1=[cbba]/(acb2):=[αββγ] a=α(acb2)2c2+β(acb2)22bc+γ(acb2)2b2b=α(acb2)22bc+β(acb2)2acb2+γ(acb2)22abc=α(acb2)2b2+β(acb2)2ab+γ(acb2)2a2

Σ 3 × 3 : = [ Σ 3 , 3 [ 0 ] Σ 3 , 3 [ 1 ] Σ 3 , 3 [ 3 ] Σ 3 , 3 [ 1 ] Σ 3 , 3 [ 2 ] Σ 3 , 3 [ 4 ] Σ 3 , 3 [ 3 ] Σ 3 , 3 [ 4 ] Σ 3 , 3 [ 5 ] ] T : = W J = [ T [ 0 , 0 ] T [ 0 , 1 ] T [ 1 , 0 ] T [ 1 , 1 ] T [ 2 , 0 ] T [ 2 , 2 ] ] Σ 2 × 2 = T T Σ 3 × 3 T ⇒ { α = T [ 0 , 0 ] 2 Σ 3 , 3 [ 0 ] + T [ 1 , 0 ] 2 Σ 3 , 3 [ 1 ] + T [ 2 , 0 ] 2 Σ 3 × 3 [ 3 ] + T [ 0 , 0 ] 2 Σ 3 , 3 [ 1 ] + T [ 1 , 0 ] 2 Σ 3 , 3 [ 2 ] + T [ 2 , 0 ] 2 Σ 3 , 3 [ 4 ] + T [ 0 , 0 ] 2 Σ 3 , 3 [ 3 ] + T [ 1 , 0 ] 2 Σ 3 , 3 [ 4 ] + T [ 2 , 0 ] 2 Σ 3 , 3 [ 5 ] β = T [ 0 , 0 ] T [ 0 , 1 ] Σ 3 × 3 [ 0 ] + T [ 1 , 0 ] T [ 0 , 1 ] Σ 3 × 3 [ 1 ] + T [ 2 , 0 ] T [ 0 , 1 ] Σ 3 × 3 [ 3 ] + T [ 0 , 0 ] T [ 1 , 1 ] Σ 3 × 3 [ 1 ] + T [ 1 , 0 ] T [ 1 , 1 ] Σ 3 × 3 [ 2 ] + T [ 2 , 0 ] T [ 2 , 1 ] Σ 3 × 3 [ 4 ] + T [ 0 , 0 ] T [ 2 , 1 ] Σ 3 × 3 [ 3 ] + T [ 1 , 0 ] T [ 2 , 1 ] Σ 3 × 3 [ 4 ] + T [ 2 , 0 ] T [ 2 , 1 ] Σ 3 × 3 [ 5 ] γ = T [ 0 , 1 ] 2 Σ 3 , 3 [ 0 ] + T [ 1 , 1 ] 2 Σ 3 , 3 [ 1 ] + T [ 2 , 1 ] 2 Σ 3 × 3 [ 3 ] + T [ 0 , 1 ] 2 Σ 3 , 3 [ 1 ] + T [ 1 , 1 ] 2 Σ 3 , 3 [ 2 ] + T [ 2 , 1 ] 2 Σ 3 , 3 [ 4 ] + T [ 0 , 1 ] 2 Σ 3 , 3 [ 3 ] + T [ 1 , 1 ] 2 Σ 3 , 3 [ 4 ] + T [ 2 , 1 ] 2 Σ 3 , 3 [ 5 ] \Sigma_{3 \times 3} := \begin{bmatrix} \Sigma_{3,3}[0] & \Sigma_{3,3}[1] & \Sigma_{3,3}[3] \\ \Sigma_{3,3}[1] & \Sigma_{3,3}[2] & \Sigma_{3,3}[4] \\ \Sigma_{3,3}[3] & \Sigma_{3,3}[4] & \Sigma_{3,3}[5] \end{bmatrix} \\ T := WJ = \begin{bmatrix} T[0,0] & T[0,1] \\ T[1,0] & T[1,1] \\ T[2,0] & T[2,2] \end{bmatrix} \\ \Sigma_{2\times 2} = T^T \Sigma_{3 \times 3} T \Rightarrow \\ \left\{\begin{matrix} \alpha = T[0,0]^2\Sigma_{3,3}[0] + T[1,0]^2\Sigma_{3,3}[1] + T[2,0]^2 \Sigma_{3\times 3}[3] \\ + T[0,0]^2\Sigma_{3,3}[1] + T[1,0]^2\Sigma_{3,3}[2] + T[2,0]^2\Sigma_{3,3}[4]\\ + T[0,0]^2\Sigma_{3,3}[3] + T[1,0]^2\Sigma_{3,3}[4] + T[2,0]^2\Sigma_{3,3}[5] \\ \beta = T[0,0]T[0,1]\Sigma_{3\times 3}[0] + T[1,0]T[0,1]\Sigma_{3\times 3}[1] + T[2,0]T[0,1]\Sigma_{3\times 3}[3]\\ +T[0,0]T[1,1]\Sigma_{3\times 3}[1] + T[1,0]T[1,1]\Sigma_{3\times 3}[2] + T[2,0]T[2,1]\Sigma_{3\times 3}[4]\\ + T[0,0]T[2,1]\Sigma_{3\times 3}[3] + T[1,0]T[2,1]\Sigma_{3\times 3}[4] + T[2,0]T[2,1]\Sigma_{3\times 3}[5] \\ \gamma = T[0,1]^2\Sigma_{3,3}[0] + T[1,1]^2\Sigma_{3,3}[1] + T[2,1]^2 \Sigma_{3\times 3}[3] \\ + T[0,1]^2\Sigma_{3,3}[1] + T[1,1]^2\Sigma_{3,3}[2] + T[2,1]^2\Sigma_{3,3}[4]\\ + T[0,1]^2\Sigma_{3,3}[3] + T[1,1]^2\Sigma_{3,3}[4] + T[2,1]^2\Sigma_{3,3}[5] \\ \end{matrix}\right. Σ3×3:= Σ3,3[0]Σ3,3[1]Σ3,3[3]Σ3,3[1]Σ3,3[2]Σ3,3[4]Σ3,3[3]Σ3,3[4]Σ3,3[5] T:=WJ= T[0,0]T[1,0]T[2,0]T[0,1]T[1,1]T[2,2] Σ2×2=TTΣ3×3T α=T[0,0]2Σ3,3[0]+T[1,0]2Σ3,3[1]+T[2,0]2Σ3×3[3]+T[0,0]2Σ3,3[1]+T[1,0]2Σ3,3[2]+T[2,0]2Σ3,3[4]+T[0,0]2Σ3,3[3]+T[1,0]2Σ3,3[4]+T[2,0]2Σ3,3[5]β=T[0,0]T[0,1]Σ3×3[0]+T[1,0]T[0,1]Σ3×3[1]+T[2,0]T[0,1]Σ3×3[3]+T[0,0]T[1,1]Σ3×3[1]+T[1,0]T[1,1]Σ3×3[2]+T[2,0]T[2,1]Σ3×3[4]+T[0,0]T[2,1]Σ3×3[3]+T[1,0]T[2,1]Σ3×3[4]+T[2,0]T[2,1]Σ3×3[5]γ=T[0,1]2Σ3,3[0]+T[1,1]2Σ3,3[1]+T[2,1]2Σ3×3[3]+T[0,1]2Σ3,3[1]+T[1,1]2Σ3,3[2]+T[2,1]2Σ3,3[4]+T[0,1]2Σ3,3[3]+T[1,1]2Σ3,3[4]+T[2,1]2Σ3,3[5]

∇ Σ 3 × 3 [ 0 ] = ∇ α ⋅ ∂ α ∂ Σ 3 × 3 [ 0 ] + ∇ β ⋅ ∂ β ∂ Σ 3 × 3 [ 0 ] + ∇ γ ⋅ ∂ γ ∂ Σ 3 × 3 [ 0 ] ⇒ \nabla \Sigma_{3\times 3}[0] = \nabla \alpha \cdot \frac{\partial \alpha}{\partial \Sigma_{3\times 3}[0]} + \nabla \beta \cdot \frac{\partial \beta}{\partial \Sigma_{3\times 3}[0]} + \nabla \gamma \cdot \frac{\partial \gamma }{\partial \Sigma_{3\times 3}[0]} \\ \Rightarrow \\ Σ3×3[0]=αΣ3×3[0]α+βΣ3×3[0]β+γΣ3×3[0]γ
∇ Σ 3 × 3 [ 0 ] = [ T [ 00 ] 2 , T [ 00 ] T [ 01 ] , T [ 01 ] 2 ] ⋅ [ ∇ α , ∇ β , ∇ γ ] ∇ Σ 3 × 3 [ 1 ] = [ T [ 00 ] 2 + T [ 10 ] 2 , T [ 10 ] T [ 01 ] + T [ 00 ] T [ 11 ] , T [ 01 ] 2 + T [ 11 ] 2 ] ⋅ [ ∇ α , ∇ β , ∇ γ ] \nabla \Sigma_{3\times 3}[0] = [ T[00]^2, T[00]T[01],T[01]^2] \cdot [\nabla \alpha, \nabla \beta, \nabla \gamma] \\ \nabla \Sigma_{3\times 3}[1] = [T[00]^2+T[10]^2, T[10]T[01]+T[00]T[11] , T[01]^2+T[11]^2] \cdot [\nabla \alpha, \nabla \beta, \nabla \gamma] Σ3×3[0]=[T[00]2,T[00]T[01],T[01]2][α,β,γ]Σ3×3[1]=[T[00]2+T[10]2,T[10]T[01]+T[00]T[11],T[01]2+T[11]2][α,β,γ]
剩下的一样的方式自己推吧,此处省略;

  • ∇ Σ 3 × 3 ⇒ ( ∇ R , ∇ S ) ⇒ ( ∇ q , ∇ s ) \nabla \Sigma_{3 \times 3} \Rightarrow (\nabla R, \nabla S) \Rightarrow (\nabla q, \nabla s) Σ3×3(R,S)(q,s)
    • ∇ Σ 3 × 3 ⇒ ( ∇ R , ∇ S ) \nabla \Sigma_{3\times 3} \Rightarrow (\nabla R, \nabla S) Σ3×3(R,S)
      [ Σ 3 × 3 [ 0 ] Σ 3 × 3 [ 1 ] Σ 3 × 3 [ 3 ] Σ 3 × 3 [ 1 ] Σ 3 × 3 [ 2 ] Σ 3 × 3 [ 4 ] Σ 3 × 3 [ 3 ] Σ 3 × 3 [ 4 ] Σ 3 × 3 [ 5 ] ] = R S ( R S ) T \begin{bmatrix} \Sigma_{3\times 3}[0] & \Sigma_{3\times 3}[1] & \Sigma_{3\times 3}[3] \\ \Sigma_{3\times 3}[1] & \Sigma_{3\times 3}[2] & \Sigma_{3\times 3}[4] \\ \Sigma_{3\times 3}[3] & \Sigma_{3\times 3}[4] & \Sigma_{3\times 3}[5] \end{bmatrix} = RS (RS)^T Σ3×3[0]Σ3×3[1]Σ3×3[3]Σ3×3[1]Σ3×3[2]Σ3×3[4]Σ3×3[3]Σ3×3[4]Σ3×3[5] =RS(RS)T

      ⇒ \Rightarrow

      Σ 3 × 3 [ 0 ] = R 00 2 S 0 2 + R 01 2 S 1 2 + R 02 2 S 2 2 Σ 3 × 3 [ 1 ] = R 00 R 10 S 0 2 + R 01 R 11 S 1 2 + R 02 R 12 S 2 2 Σ 3 × 3 [ 2 ] = R 10 2 S 0 2 + R 11 2 S 1 2 + R 12 2 S 2 2 Σ 3 × 3 [ 3 ] = R 00 R 20 S 0 2 + R 01 R 21 S 1 2 + R 02 R 22 S 2 2 Σ 3 × 3 [ 4 ] = R 10 R 20 S 0 2 + R 11 R 21 S 1 2 + R 12 R 22 S 2 2 Σ 3 × 3 [ 5 ] = R 20 2 S 0 2 + R 21 2 S 1 2 + R 22 2 S 2 2 \Sigma_{3\times 3}[0] = R_{00}^2S_0^2+R_{01}^2S_1^2+R_{02}^2S_2^2\\ \Sigma_{3\times 3}[1] = R_{00}R_{10}S_0^2+R_{01}R_{11}S_1^2+R_{02}R_{12}S_2^2 \\ \Sigma_{3\times 3}[2] = R_{10}^2S_0^2+R_{11}^2S_1^2+R_{12}^2S_2^2\\ \Sigma_{3\times 3}[3] = R_{00}R_{20}S_0^2+R_{01}R_{21}S_1^2+R_{02}R_{22}S_2^2 \\ \Sigma_{3\times 3}[4] = R_{10}R_{20}S_0^2+R_{11}R_{21}S_1^2+R_{12}R_{22}S_2^2 \\ \Sigma_{3\times 3}[5] = R_{20}^2S_0^2+R_{21}^2S_1^2+R_{22}^2S_2^2 Σ3×3[0]=R002S02+R012S12+R022S22Σ3×3[1]=R00R10S02+R01R11S12+R02R12S22Σ3×3[2]=R102S02+R112S12+R122S22Σ3×3[3]=R00R20S02+R01R21S12+R02R22S22Σ3×3[4]=R10R20S02+R11R21S12+R12R22S22Σ3×3[5]=R202S02+R212S12+R222S22
      ⇒ \Rightarrow

    • ∇ σ 3 × 3 : = [ ∇ Σ 3   × 3 [ 0 ] , . . . , ∇ Σ 3   × 3 [ 5 ] ] \nabla \sigma_{3\times 3} := [\nabla \Sigma_{3\ \times 3}[0], ..., \nabla \Sigma_{3\ \times 3}[5] ] σ3×3:=[Σ3 ×3[0],...,Σ3 ×3[5]]

      ∇ R 00 = [ 2 S 0 2 R 00 , R 10 S 0 2 , 0 , R 20 S 0 2 , 0 , 0 ] ⋅ ∇ σ 3 × 3 ∇ R 01 = [ 2 S 1 2 R 01 , R 11 S 1 2 , 0 , R 21 S 1 2 , 0 , 0 ] ⋅ ∇ σ 3 × 3 ∇ R 02 = [ 2 S 2 2 R 02 , R 12 S 2 2 , 0 , R 22 S 2 2 , 0 , 0 ] ⋅ ∇ σ 3 × 3 ∇ R 10 = [ 0 , R 00 S 0 2 , 0 , 2 R 10 S 0 2 , R 20 S 0 2 , 0 ] ⋅ ∇ σ 3 × 3 \nabla R_{00} =[2S_0^2R_{00},R_{10}S_0^2,0,R_{20}S_0^2,0,0] \cdot\nabla \sigma_{3\times 3}\\ \nabla R_{01} =[2S_1^2R_{01},R_{11}S_1^2,0,R_{21}S_1^2,0,0] \cdot \nabla \sigma_{3\times 3}\\ \nabla R_{02} =[2S_2^2R_{02},R_{12}S_2^2,0,R_{22}S_2^2,0,0] \cdot \nabla \sigma_{3\times 3} \\ \nabla R_{10} = [0,R_{00}S_0^2,0,2R_{10}S_0^2,R_{20}S_0^2,0] \cdot \nabla \sigma_{3\times 3}\\ R00=[2S02R00,R10S02,0,R20S02,0,0]σ3×3R01=[2S12R01,R11S12,0,R21S12,0,0]σ3×3R02=[2S22R02,R12S22,0,R22S22,0,0]σ3×3R10=[0,R00S02,0,2R10S02,R20S02,0]σ3×3
      剩下的省略,一样的推导方式;
      ⇒ \Rightarrow
      ∇ S 0 = [ 2 R 00 2 S 0 , 2 R 00 R 10 S 0 , 2 R 10 2 S 0 , 2 R 00 R 20 S 0 , 2 R 10 R 20 S 0 , 2 R 20 2 S 0 ] ⋅ ∇ σ 3 × 3 ∇ S 1 = [ 2 R 01 2 S 1 , 2 R 01 R 11 S 1 , 2 R 11 2 S 1 , 2 R 01 R 21 S 1 , 2 R 11 R 21 S 1 , 2 R 21 2 S 1 ] ⋅ ∇ σ 3 × 3 ∇ S 2 = [ 2 R 02 2 S 2 , 2 R 02 R 12 S 2 , 2 R 12 2 S 2 , 2 R 02 R 22 S 2 , 2 R 12 R 22 S 2 , 2 R 22 2 S 2 ] ⋅ ∇ σ 3 × 3 \nabla S_0 = [2R_{00}^2S_0,2R_{00}R_{10}S_0,2R_{10}^2S_0, 2R_{00}R_{20}S_0, 2R_{10}R_{20}S_0,2R_{20}^2S_0] \cdot \nabla \sigma_{3\times 3}\\ \nabla S_1 = [2R_{01}^2S_1, 2R_{01}R_{11}S_1, 2R_{11}^2S_1, 2R_{01}R_{21}S_1, 2R_{11}R_{21}S_1, 2R_{21}^2S_1] \cdot \nabla \sigma_{3\times 3}\\ \nabla S_2 = [2R_{02}^2S_2, 2R_{02}R_{12}S_2,2R_{12}^2S_2,2R_{02}R_{22}S_2,2R_{12}R_{22}S_2,2R_{22}^2S_2] \cdot \nabla \sigma_{3\times 3} \\ S0=[2R002S0,2R00R10S0,2R102S0,2R00R20S0,2R10R20S0,2R202S0]σ3×3S1=[2R012S1,2R01R11S1,2R112S1,2R01R21S1,2R11R21S1,2R212S1]σ3×3S2=[2R022S2,2R02R12S2,2R122S2,2R02R22S2,2R12R22S2,2R222S2]σ3×3

    • ( ∇ R , ∇ S ) ⇒ ( ∇ q , ∇ s ) (\nabla R, \nabla S) \Rightarrow (\nabla q, \nabla s) (R,S)(q,s)
      q = [ w x y z ] T / w 2 + x 2 + y 2 + z 2 R = [ 1 − 2 q 2 2 − 2 q 3 2 2 q 1 q 2 − 2 q 0 q 3 2 q 1 q 3 + 2 q 0 q 2 2 q 1 q 2 + 2 q 0 q 3 1 − 2 q 1 2 − 2 q 3 2 2 q 2 q 3 − 2 q 0 q 1 2 q 1 q 3 − 2 q 0 q 2 2 q 2 q 3 + 2 q 0 q 1 1 − 2 q 1 2 − 2 q 2 2 ] ⇒ ∇ q 0 = − 2 q 3 ∇ R 01 + 2 q 2 ∇ R 02 + 2 q 3 ∇ R 10 − 2 q 2 ∇ R 20 q = \begin{bmatrix} w & x & y & z \end{bmatrix}^T / \sqrt{w^2+x^2+y^2+z^2} \\ R = \begin{bmatrix} 1 - 2q_2^2 - 2q_3^2 & 2q_1q_2 - 2q_0q_3 & 2q_1q_3 + 2q_0q_2 \\ 2q_1q_2 + 2q_0q_3 & 1 - 2q_1^2 - 2q_3^2 & 2q_2q_3 - 2q_0q_1 \\ 2q_1q_3 - 2q_0q_2 & 2q_2q_3 + 2q_0q_1 & 1 - 2q_1^2 - 2q_2^2 \end{bmatrix} \\ \Rightarrow \\ \nabla q_0 = -2q_3 \nabla R_{01} + 2q_2 \nabla R_{02} + 2q_3 \nabla R_{10} - 2 q_2 \nabla R_{20} q=[wxyz]T/w2+x2+y2+z2 R= 12q222q322q1q2+2q0q32q1q32q0q22q1q22q0q312q122q322q2q3+2q0q12q1q3+2q0q22q2q32q0q112q122q22 q0=2q3R01+2q2R02+2q3R102q2R20

      ⇒ \Rightarrow
      ∇ w = ∇ q 0 ∂ q 0 ∂ w + ∇ q 1 ∂ q 1 ∂ w + ∇ q 2 ∂ q 2 ∂ w + ∇ q 3 ∂ q 3 ∂ w = ∇ q 0 x 2 + y 2 + z 2 ( w 2 + x 2 + y 2 + z 2 ) 1.5 ) + ∇ q 1 − x 2 ( w 2 + x 2 + y 2 + z 2 ) 1.5 ) + ∇ q 2 − y 2 ( w 2 + x 2 + y 2 + z 2 ) 1.5 ) + ∇ q 3 − z 2 ( w 2 + x 2 + y 2 + z 2 ) 1.5 ) \nabla w = \nabla q_0 \frac{\partial q_0}{\partial w} + \nabla q_1 \frac{\partial q_1}{\partial w} + \nabla q_2 \frac{\partial q_2}{\partial w} + \nabla q_3 \frac{\partial q_3}{\partial w} \\ = \nabla q_0 \frac{x^2+y^2+z^2}{(w^2+x^2+y^2+z^2)^{1.5})} + \nabla q_1 \frac{-x^2}{(w^2+x^2+y^2+z^2)^{1.5})} + \nabla q_2 \frac{-y^2}{(w^2+x^2+y^2+z^2)^{1.5})} + \nabla q_3 \frac{-z^2}{(w^2+x^2+y^2+z^2)^{1.5})} w=q0wq0+q1wq1+q2wq2+q3wq3=q0(w2+x2+y2+z2)1.5)x2+y2+z2+q1(w2+x2+y2+z2)1.5)x2+q2(w2+x2+y2+z2)1.5)y2+q3(w2+x2+y2+z2)1.5)z2

  • 代码是这样的:
    • backward.cu/preprocess_CUDA(...): ∇ u v p r o j ⇒ ∇ x y z \nabla uv_{proj} \Rightarrow \nabla xyz uvprojxyz

      // diff-gaussian-rasterization/cuda_rasterizer/backward.cu
      // preprocessCUDA(...)
      float3 m = means[idx];
      // Taking care of gradients from the screenspace points
      float4 m_hom = transformPoint4x4(m, proj);
      float m_w = 1.0f / (m_hom.w + 0.0000001f);
      // 
      glm::vec3 dL_dmean;
      float mul1 = (proj[0] * m.x + proj[4] * m.y + proj[8] * m.z + proj[12]) * m_w * m_w;
      float mul2 s= (proj[1] * m.x + proj[5] * m.y + proj[9] * m.z + proj[13]) * m_w * m_w;
      dL_dmean.x = (proj[0] * m_w - proj[3] * mul1) * dL_dmean2D[idx].x + (proj[1] * m_w - proj[3] * mul2) * dL_dmean2D[idx].y;
      dL_dmean.y = (proj[4] * m_w - proj[7] * mul1) * dL_dmean2D[idx].x + (proj[5] * m_w - proj[7] * mul2) * dL_dmean2D[idx].y;
      dL_dmean.z = (proj[8] * m_w - proj[11] * mul1) * dL_dmean2D[idx].x + (proj[9] * m_w - proj[11] * mul2) * dL_dmean2D[idx].y;
        - `backward.cu/computeCov2DCUDA`: $$\nabla (\Sigma_{2\times 2})^{-1} \Rightarrow \nabla \Sigma_{3\times 3}$$
      // diff-gaussian-rasterization/cuda_rasterizer/backward.cu
      // computeCov2DCUDA(...)
      
      // \nabla(\alpha, \alpha\beta, \alpha \gammma) → \nabla (a, b, c) 
      float a = cov2D[0][0] += 0.3f;
      float b = cov2D[0][1];
      float c = cov2D[1][1] += 0.3f;
      float denom = a * c - b * b;
      float dL_da = 0, dL_db = 0, dL_dc = 0;
      float denom2inv = 1.0f / ((denom * denom) + 0.0000001f);
      dL_da = denom2inv * (-c * c * dL_dconic.x + 2 * b * c * dL_dconic.y + (denom - a * c) * dL_dconic.z);
      dL_dc = denom2inv * (-a * a * dL_dconic.z + 2 * a * b * dL_dconic.y + (denom - a * c) * dL_dconic.x);
      dL_db = denom2inv * 2 * (b * c * dL_dconic.x - (denom + 2 * b * b) * dL_dconic.y + a * b * dL_dconic.z);
      
      // \nabla(a, b, c) → \nabla(Σ_{3,3})
      dL_dcov[6 * idx + 0] = (T[0][0] * T[0][0] * dL_da + T[0][0] * T[1][0] * dL_db + T[1][0] * T[1][0] * dL_dc);
      dL_dcov[6 * idx + 3] = (T[0][1] * T[0][1] * dL_da + T[0][1] * T[1][1] * dL_db + T[1][1] * T[1][1] * dL_dc);
      dL_dcov[6 * idx + 5] = (T[0][2] * T[0][2] * dL_da + T[0][2] * T[1][2] * dL_db + T[1][2] * T[1][2] * dL_dc);
      
      dL_dcov[6 * idx + 1] = 2 * T[0][0] * T[0][1] * dL_da + (T[0][0] * T[1][1] + T[0][1] * T[1][0]) * dL_db + 2 * T[1][0] * T[1][1] * dL_dc;
      dL_dcov[6 * idx + 2] = 2 * T[0][0] * T[0][2] * dL_da + (T[0][0] * T[1][2] + T[0][2] * T[1][0]) * dL_db + 2 * T[1][0] * T[1][2] * dL_dc;
      dL_dcov[6 * idx + 4] = 2 * T[0][2] * T[0][1] * dL_da + (T[0][1] * T[1][2] + T[0][2] * T[1][1]) * dL_db + 2 * T[1][1] * T[1][2] * dL_dc;
      
    • backward.cu/computeCov3D: ∇ Σ 3 × 3 ⇒ ( ∇ R , ∇ S ) ⇒ ( ∇ q , ∇ s ) \nabla \Sigma_{3 \times 3} \Rightarrow (\nabla R, \nabla S) \Rightarrow (\nabla q, \nabla s) Σ3×3(R,S)(q,s)

      • dL_dMt就是:
        [ 2 Σ 3 × 3 [ 0 ] Σ 3 × 3 [ 1 ] Σ 3 × 3 [ 2 ] Σ 3 × 3 [ 1 ] 2 Σ 3 × 3 [ 2 ] Σ 3 × 3 [ 3 ] Σ 3 × 3 [ 3 ] Σ 3 × 3 [ 4 ] 2 Σ 3 × 3 [ 5 ] ] R T S \begin{bmatrix} 2\Sigma_{3\times 3}[0] & \Sigma_{3\times 3}[1] & \Sigma_{3\times 3}[2] \\ \Sigma_{3\times 3}[1] & 2\Sigma_{3\times 3}[2] & \Sigma_{3\times 3}[3] \\ \Sigma_{3\times 3}[3] & \Sigma_{3\times 3}[4] & 2\Sigma_{3\times 3}[5] \end{bmatrix} R^T S 2Σ3×3[0]Σ3×3[1]Σ3×3[3]Σ3×3[1]2Σ3×3[2]Σ3×3[4]Σ3×3[2]Σ3×3[3]2Σ3×3[5] RTS

      按照代码:
      ∇ S 0 = [ R 00 , R 10 , R 20 ] ⋅ [ 2 Σ 3 × 3 [ 0 ] R 00 S 0 + Σ 3 × 3 [ 1 ] R 01 S 0 + Σ 3 × 3 [ 3 ] R 02 S 0 , Σ 3 × 3 [ 1 ] R 00 S 0 + 2 Σ 3 × 3 [ 2 ] R 01 S 0 + Σ 3 × 3 [ 4 ] R 02 S 0 , Σ 3 × 3 [ 3 ] R 00 S 0 + Σ 3 × 3 [ 4 ] R 01 S 0 + 2 Σ 3 × 3 [ 5 ] R 02 S 0 ] = [ 2 R 00 2 S 0 , R 01 R 10 S 0 + R 00 R 11 S 0 , 2 R 01 R 10 S 0 , R 00 R 02 S 0 + R 00 R 20 S 0 , . . . ] ⋅ ∇ σ 3 × 3 \nabla S_0 = [R_{00}, R_{10}, R_{20}] \cdot \\ [2\Sigma_{3\times 3}[0]R_{00}S_0+\Sigma_{3\times 3}[1]R_{01}S_0+\Sigma_{3\times 3}[3]R_{02}S_0, \\ \Sigma_{3 \times 3} [1] R_{00}S_0 + 2\Sigma_{3 \times 3} [2]R_{01}S_0 + \Sigma_{3 \times 3} [4]R_{02}S_0, \\ \Sigma_{3 \times 3} [3] R_{00}S_0 + \Sigma_{3 \times 3} [4]R_{01}S_0 + 2\Sigma_{3 \times 3} [5]R_{02}S_0] \\ = [2R_{00}^2S_0,R_{01}R_{10}S_0+R_{00}R_{11}S_0, 2R_{01}R_{10}S_0,R_{00}R_{02}S_{0}+R_{00}R_{20}S_0, ...] \cdot \nabla \sigma_{3 \times 3} S0=[R00,R10,R20][2Σ3×3[0]R00S0+Σ3×3[1]R01S0+Σ3×3[3]R02S0,Σ3×3[1]R00S0+2Σ3×3[2]R01S0+Σ3×3[4]R02S0,Σ3×3[3]R00S0+Σ3×3[4]R01S0+2Σ3×3[5]R02S0]=[2R002S0,R01R10S0+R00R11S0,2R01R10S0,R00R02S0+R00R20S0,...]σ3×3

      // diff-gaussian-rasterization/cuda_rasterizer/backward.cu
      // computeCov3D(...)
      
      glm::mat3 dL_dSigma = glm::mat3(
          dL_dcov3D[0], 0.5f * dL_dcov3D[1], 0.5f * dL_dcov3D[2],
          0.5f * dL_dcov3D[1], dL_dcov3D[3], 0.5f * dL_dcov3D[4],
          0.5f * dL_dcov3D[2], 0.5f * dL_dcov3D[4], dL_dcov3D[5]
      );
      glm::mat3 dL_dM = 2.0f * M * dL_dSigma;
      glm::mat3 Rt = glm::transpose(R);
      glm::mat3 dL_dMt = glm::transpose(dL_dM);
      
      
      // \nabla q
      glm::vec4 dL_dq;
      dL_dq.x = 2 * z * (dL_dMt[0][1] - dL_dMt[1][0]) + 2 * y * (dL_dMt[2][0] - dL_dMt[0][2]) + 2 * x * (dL_dMt[1][2] - dL_dMt[2][1]);
      dL_dq.y = 2 * y * (dL_dMt[1][0] + dL_dMt[0][1]) + 2 * z * (dL_dMt[2][0] + dL_dMt[0][2]) + 2 * r * (dL_dMt[1][2] - dL_dMt[2][1]) - 4 * x * (dL_dMt[2][2] + dL_dMt[1][1]);
      dL_dq.z = 2 * x * (dL_dMt[1][0] + dL_dMt[0][1]) + 2 * r * (dL_dMt[2][0] - dL_dMt[0][2]) + 2 * z * (dL_dMt[1][2] + dL_dMt[2][1]) - 4 * y * (dL_dMt[2][2] + dL_dMt[0][0]);
      dL_dq.w = 2 * r * (dL_dMt[0][1] - dL_dMt[1][0]) + 2 * x * (dL_dMt[2][0] + dL_dMt[0][2]) + 2 * y * (dL_dMt[1][2] + dL_dMt[2][1]) - 4 * z * (dL_dMt[1][1] + dL_dMt[0][0]);
      
      
      // \nabla S.
      glm::vec3* dL_dscale = dL_dscales + idx;
      dL_dscale->x = glm::dot(Rt[0], dL_dMt[0]);
      dL_dscale->y = glm::dot(Rt[1], dL_dMt[1]);
      dL_dscale->z = glm::dot(Rt[2], dL_dMt[2]);
      

Reference

  • [1] 矩阵求导:https://people.maths.ox.ac.uk/gilesm/files/NA-08-01.pdf
  • [2] https://dyn4j.org/2010/01/sat/
  • [3] https://github.com/joeyan/gaussian_splatting/blob/main/MATH.md
  • [4] 俺把带注释的cuda代码存放到了: https://vzia2ov9de.feishu.cn/file/L5RFbdLEpotN3ExPCSWcHo9En6y?from=from_copylink
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值