当前位置:   article > 正文

3D Gaussian Splatting 原理跟代码 debug模式讲解_3d gaussian splatting 代码解读

3d gaussian splatting 代码解读

3大核心技术

  1. 使用了3维高斯分布来做点云处理.使用协方差矩阵影响高斯大小形状跟旋转
  2. 使用了cuda可微渲染编码提高了渲染质量跟速度
  3. 使用了球谐函数处理RGB颜色

光栅化流程图

abc是预处理流程,d是光栅化过程

a) 3d-2d投影过程

3d gaussian是不同于传统模型,没有使用3角面来处理.而是使用了椭球.

椭球经过splatting溅射以后投影到2D图片

下面gaussian模型主要训练参数初始化.

  1. def __init__(self, sh_degree : int):
  2. self.active_sh_degree = 0 //球谐函数等级
  3. self.max_sh_degree = sh_degree //球谐函数等级
  4. self._xyz = torch.empty(0) //高斯球xyz坐标
  5. self._features_dc = torch.empty(0) //球谐函数主要颜色
  6. self._features_rest = torch.empty(0) //球谐函数其他颜色
  7. self._scaling = torch.empty(0) //椭球xyz轴大小
  8. self._rotation = torch.empty(0) //椭球旋转
  9. self._opacity = torch.empty(0) //椭球透明度
  10. self.max_radii2D = torch.empty(0) //椭圆2D半径
  11. self.xyz_gradient_accum = torch.empty(0) //xyz梯度累计
  12. self.denom = torch.empty(0) //分母
  13. self.optimizer = None //优化器
  14. self.percent_dense = 0 //密度参数
  15. self.spatial_lr_scale = 0 //学习率
  16. self.setup_functions() //初始化方法

b) 2d 投影后tile每个块大小计算跟高斯椭球覆盖的关系

每个tile都是16*16像素组成,每个tile对应cuda的每个block,

每个tile都由多个高斯覆盖,每个高斯都有自己的深度,每个高斯对应cuda的一个block的一个线程

c) 计算2d投影后tile每个块列表List

用tile跟gaussian编号做排序处理

d) 最终光栅化流程

每个tile对应cuda的每个block,每个像素对应一个线程

通过覆盖这个像素的高斯点颜色跟透明度技术最终颜色

技术流程

python->c++->cuda

cuda:核函数-grid-block-thread做并行处理

python 核心代码文件

train.py
训练代码主入口 
scene/__init_py
场景训练数据,相机数据,gaussian模型初始化 
gaussian_model.py
gaussian模型核心代码 
gaussian_renderer/init.py
render渲染核心代码

cuda核心代码文件

  1. gaussian_renderer/init.py 渲染的时候调用子模块的diff_gaussian_rasterization/init.py 文件
    class GaussianRasterizer(nn.Module)类 
  2. class GaussianRasterizer(nn.Module)调用继承了自动微分的类class _RasterizeGaussians(torch.autograd.Function).重写forward跟backward方法.
    里面调用C++的rasterize_gaussians方法 
  3. ext.cpp定义了python方法到rasterize_points文件的C++方法 
  4. rasterize_points文件最终调用rasterizer_impl.cu的cuda代码
    主要看CudaRasterizer::Rasterizer::forward跟CudaRasterizer::Rasterizer::backword2个方法
    里面调用了forward跟backward的Cuda文件
    这两个就是核心文件 
  5. 渲染forward.cu 预处理核心代码备注
  1. // Perform initial steps for each Gaussian prior to rasterization.
  2. //预处理高斯球投影流程,计算高斯球投影的大小跟覆盖tile的范围,一个block处理一个tile,一个线程处理一个高斯球
  3. template<int C>
  4. __global__ void preprocessCUDA(int P, int D, int M,
  5. const float* orig_points,
  6. const glm::vec3* scales,
  7. const float scale_modifier,
  8. const glm::vec4* rotations,
  9. const float* opacities,
  10. const float* shs,
  11. bool* clamped,
  12. const float* cov3D_precomp,
  13. const float* colors_precomp,
  14. const float* viewmatrix,
  15. const float* projmatrix,
  16. const glm::vec3* cam_pos,
  17. const int W, int H,
  18. const float tan_fovx, float tan_fovy,
  19. const float focal_x, float focal_y,
  20. int* radii,
  21. float2* points_xy_image,
  22. float* depths,
  23. float* cov3Ds,
  24. float* rgb,
  25. float4* conic_opacity,
  26. const dim3 grid,
  27. uint32_t* tiles_touched,
  28. bool prefiltered)
  29. {
  30. auto idx = cg::this_grid().thread_rank();
  31. if (idx >= P)
  32. return;
  33. // Initialize radius and touched tiles to 0. If this isn't changed,
  34. // this Gaussian will not be processed further.
  35. //半径跟tiles数量
  36. radii[idx] = 0;
  37. tiles_touched[idx] = 0;
  38. // Perform near culling, quit if outside.
  39. float3 p_view;
  40. if (!in_frustum(idx, orig_points, viewmatrix, projmatrix, prefiltered, p_view))
  41. return;
  42. // Transform point by projecting
  43. //经过3d point跟投影矩阵获取高斯的中心点
  44. float3 p_orig = { orig_points[3 * idx], orig_points[3 * idx + 1], orig_points[3 * idx + 2] };
  45. float4 p_hom = transformPoint4x4(p_orig, projmatrix);
  46. float p_w = 1.0f / (p_hom.w + 0.0000001f);
  47. float3 p_proj = { p_hom.x * p_w, p_hom.y * p_w, p_hom.z * p_w };
  48. // If 3D covariance matrix is precomputed, use it, otherwise compute
  49. // from scaling and rotation parameters.
  50. //计算3D协方差矩阵
  51. const float* cov3D;
  52. if (cov3D_precomp != nullptr)
  53. {
  54. cov3D = cov3D_precomp + idx * 6;
  55. }
  56. else
  57. {
  58. computeCov3D(scales[idx], scale_modifier, rotations[idx], cov3Ds + idx * 6);
  59. cov3D = cov3Ds + idx * 6;
  60. }
  61. // Compute 2D screen-space covariance matrix
  62. //计算2d的协方差矩阵
  63. float3 cov = computeCov2D(p_orig, focal_x, focal_y, tan_fovx, tan_fovy, cov3D, viewmatrix);
  64. // Invert covariance (EWA algorithm)
  65. //计算行列式值
  66. float det = (cov.x * cov.z - cov.y * cov.y);
  67. if (det == 0.0f)
  68. return;
  69. float det_inv = 1.f / det;
  70. float3 conic = { cov.z * det_inv, -cov.y * det_inv, cov.x * det_inv };
  71. // Compute extent in screen space (by finding eigenvalues of
  72. // 2D covariance matrix). Use extent to compute a bounding rectangle
  73. // of screen-space tiles that this Gaussian overlaps with. Quit if
  74. // rectangle covers 0 tiles.
  75. //计算2X2矩阵的最大特征值来近似椭圆的长轴半径
  76. float mid = 0.5f * (cov.x + cov.z);
  77. float lambda1 = mid + sqrt(max(0.1f, mid * mid - det));
  78. float lambda2 = mid - sqrt(max(0.1f, mid * mid - det));
  79. float my_radius = ceil(3.f * sqrt(max(lambda1, lambda2)));
  80. float2 point_image = { ndc2Pix(p_proj.x, W), ndc2Pix(p_proj.y, H) };
  81. uint2 rect_min, rect_max;
  82. //计算覆盖的tile
  83. getRect(point_image, my_radius, rect_min, rect_max, grid);
  84. if ((rect_max.x - rect_min.x) * (rect_max.y - rect_min.y) == 0)
  85. return;
  86. // If colors have been precomputed, use them, otherwise convert
  87. // spherical harmonics coefficients to RGB color.
  88. //使用球鞋函数计算RGB颜色
  89. if (colors_precomp == nullptr)
  90. {
  91. glm::vec3 result = computeColorFromSH(idx, D, M, (glm::vec3*)orig_points, *cam_pos, shs, clamped);
  92. rgb[idx * C + 0] = result.x;
  93. rgb[idx * C + 1] = result.y;
  94. rgb[idx * C + 2] = result.z;
  95. }
  96. // Store some useful helper data for the next steps.
  97. //存储一些有用的参数后续使用,深度跟半径
  98. depths[idx] = p_view.z;
  99. radii[idx] = my_radius;
  100. points_xy_image[idx] = point_image;
  101. // Inverse 2D covariance and opacity neatly pack into one float4
  102. //2D协方差矩阵的逆跟tiles数量
  103. conic_opacity[idx] = { conic.x, conic.y, conic.z, opacities[idx] };
  104. tiles_touched[idx] = (rect_max.y - rect_min.y) * (rect_max.x - rect_min.x);
  105. }

6. 渲染forward.cu 光栅化核心代码备注

  1. // Main rasterization method. Collaboratively works on one tile per
  2. // block, each thread treats one pixel. Alternates between fetching
  3. // and rasterizing data.
  4. //最终像素渲染,一个block处理一个tile,一个线程处理一个像素
  5. template <uint32_t CHANNELS>
  6. __global__ void __launch_bounds__(BLOCK_X * BLOCK_Y)
  7. renderCUDA(
  8. const uint2* __restrict__ ranges,
  9. const uint32_t* __restrict__ point_list,
  10. int W, int H,
  11. const float2* __restrict__ points_xy_image,
  12. const float* __restrict__ features,
  13. const float4* __restrict__ conic_opacity,
  14. float* __restrict__ final_T,
  15. uint32_t* __restrict__ n_contrib,
  16. const float* __restrict__ bg_color,
  17. float* __restrict__ out_color)
  18. {
  19. // Identify current tile and associated min/max pixel range.
  20. //计算block的像素最小最大值
  21. auto block = cg::this_thread_block();
  22. uint32_t horizontal_blocks = (W + BLOCK_X - 1) / BLOCK_X;
  23. uint2 pix_min = { block.group_index().x * BLOCK_X, block.group_index().y * BLOCK_Y };
  24. uint2 pix_max = { min(pix_min.x + BLOCK_X, W), min(pix_min.y + BLOCK_Y , H) };
  25. //计算thread的像素坐标
  26. uint2 pix = { pix_min.x + block.thread_index().x, pix_min.y + block.thread_index().y };
  27. uint32_t pix_id = W * pix.y + pix.x;
  28. float2 pixf = { (float)pix.x, (float)pix.y };
  29. // Check if this thread is associated with a valid pixel or outside.
  30. //计算像素是否在图片
  31. bool inside = pix.x < W&& pix.y < H;
  32. // Done threads can help with fetching, but don't rasterize
  33. //是否处理像素
  34. bool done = !inside;
  35. // Load start/end range of IDs to process in bit sorted list.
  36. //处理block覆盖范围跟数量
  37. uint2 range = ranges[block.group_index().y * horizontal_blocks + block.group_index().x];
  38. const int rounds = ((range.y - range.x + BLOCK_SIZE - 1) / BLOCK_SIZE);
  39. int toDo = range.y - range.x;
  40. // Allocate storage for batches of collectively fetched data.
  41. //高斯球编号,坐标,透明度
  42. __shared__ int collected_id[BLOCK_SIZE];
  43. __shared__ float2 collected_xy[BLOCK_SIZE];
  44. __shared__ float4 collected_conic_opacity[BLOCK_SIZE];
  45. // Initialize helper variables
  46. //穿透率,高斯贡献数量,最后高斯数量,颜色
  47. float T = 1.0f;
  48. uint32_t contributor = 0;
  49. uint32_t last_contributor = 0;
  50. float C[CHANNELS] = { 0 };
  51. // Iterate over batches until all done or range is complete
  52. //开始循环计算
  53. for (int i = 0; i < rounds; i++, toDo -= BLOCK_SIZE)
  54. {
  55. // End if entire block votes that it is done rasterizing
  56. int num_done = __syncthreads_count(done);
  57. if (num_done == BLOCK_SIZE)
  58. break;
  59. // Collectively fetch per-Gaussian data from global to shared
  60. //计算高斯球编号,坐标,透明度
  61. int progress = i * BLOCK_SIZE + block.thread_rank();
  62. if (range.x + progress < range.y)
  63. {
  64. int coll_id = point_list[range.x + progress];
  65. collected_id[block.thread_rank()] = coll_id;
  66. collected_xy[block.thread_rank()] = points_xy_image[coll_id];
  67. collected_conic_opacity[block.thread_rank()] = conic_opacity[coll_id];
  68. }
  69. block.sync();
  70. // Iterate over current batch
  71. //开始循环出来需要计算的高斯球
  72. for (int j = 0; !done && j < min(BLOCK_SIZE, toDo); j++)
  73. {
  74. // Keep track of current position in range
  75. contributor++;
  76. // Resample using conic matrix (cf. "Surface
  77. // Splatting" by Zwicker et al., 2001)
  78. float2 xy = collected_xy[j]; //高斯椭圆中心像素坐标
  79. float2 d = { xy.x - pixf.x, xy.y - pixf.y };//像素跟高斯椭圆中心距离
  80. float4 con_o = collected_conic_opacity[j];//2d协方差矩阵的逆和不透明度
  81. // 计算高斯分布概率的指数部分
  82. 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;
  83. if (power > 0.0f)
  84. continue;
  85. // Eq. (2) from 3D Gaussian splatting paper.
  86. // Obtain alpha by multiplying with Gaussian opacity
  87. //获取通过透明度连乘后的alpha值
  88. // and its exponential falloff from mean.
  89. // Avoid numerical instabilities (see paper appendix).
  90. //计算alpha值
  91. float alpha = min(0.99f, con_o.w * exp(power));
  92. if (alpha < 1.0f / 255.0f)
  93. continue;
  94. float test_T = T * (1 - alpha);
  95. if (test_T < 0.0001f)
  96. {
  97. done = true;
  98. continue;
  99. }
  100. // Eq. (3) from 3D Gaussian splatting paper.
  101. //计算颜色
  102. for (int ch = 0; ch < CHANNELS; ch++)
  103. C[ch] += features[collected_id[j] * CHANNELS + ch] * alpha * T;
  104. T = test_T;
  105. // Keep track of last range entry to update this
  106. // pixel.
  107. last_contributor = contributor;
  108. }
  109. }
  110. // All threads that treat valid pixel write out their final
  111. // rendering data to the frame and auxiliary buffers.
  112. //最终输出穿透率,贡献数量,颜色
  113. if (inside)
  114. {
  115. final_T[pix_id] = T;
  116. n_contrib[pix_id] = last_contributor;
  117. for (int ch = 0; ch < CHANNELS; ch++)
  118. out_color[ch * H * W + pix_id] = C[ch] + T * bg_color[ch];
  119. }
  120. }

更详细的环境搭建,代码详解,实际应用可查看下面视频

课程07 3D Gaussian Splatting 原理跟代码 debug模式讲解_哔哩哔哩_bilibili

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号