分块优化:分块边界计算
在 3D Gaussian Splatting 的渲染过程中,我们要解决的一个核心问题是:屏幕上的每个像素(或图像块 Tile)受哪些高斯球的影响?为了解答这个问题,我们不能盲目地遍历所有像素,而是需要为每个高斯球计算一个二维的屏幕包围盒(AABB - Axis-Aligned Bounding Box)。在这篇文章中,我们就来一步步实现这个计算过程。
计算高斯球的“影响半径”
虽然高斯分布在理论上是无限延伸的,但在实际渲染中,我们不需要计算无限远的像素。我们需要一个“截断半径”,在这个半径内,包含了高斯球绝大部分的能量(概率)。
这里我们引入统计学中著名的
- # 计算高斯球在屏幕上的包围盒 (AABB) 与筛选
- evals_v = evals[keep]
- evals_sorted = evals_v[order]
- major_variance = evals_sorted[:, 1].clamp(min=1e-12, max=1e4)
- radius = torch.ceil(3.0 * torch.sqrt(major_variance)).to(torch.int64)
构建初始的包围盒(AABB)
有了半径,我们就可以围绕高斯球在屏幕上的中心坐标 (u, v) 建立一个正方形的包围盒。我们需要计算它的左上角 (u_min, v_min) 和右下角 (u_max, v_max)。
- u_min = torch.floor(u_sorted - radius)
- u_max = torch.ceil(u_sorted + radius)
- v_min = torch.floor(v_sorted - radius)
- v_max = torch.ceil(v_sorted + radius)
判断高斯球是否在屏幕内(On-Screen Mask)
由于我们上一步保留了原始的计算结果(即使它是负数,或者是超过屏幕宽度的巨大数值),我们现在可以精准地判断哪些高斯球是完全在屏幕之外的废点。
我们要找出保留的高斯球,它的包围盒必须与屏幕有交集。
- onscreen = (u_max > 0) & (u_min < width) & (v_max > 0) & (v_min < height)
- if not onscreen.any():
- raise Exception("没有高斯球在屏幕范围内!(No Gaussians onscreen!)")
利用这个 onscreen 掩码,我们就可以安全地对所有的属性进行过滤,剔除屏幕外的高斯球,大幅节省算力:
- u_sorted = u_sorted[onscreen]
- v_sorted = v_sorted[onscreen]
- colors_sorted = colors_sorted[onscreen]
- opacity_sorted = opacity_sorted[onscreen]
- sigma_camera_sorted = sigma_camera_sorted[onscreen]
最终的边界限制(Clamping)
现在,我们手里的高斯球都已经确定会影响到屏幕画面了。但是,它们的包围盒可能有一部分超出了屏幕边界。此时,我们终于可以安全地进行限制操作(Clamping)了,把超出屏幕的部分切掉。
- u_min = u_min[onscreen].clamp(0, width - 1)
- u_max = u_max[onscreen].clamp(0, width - 1)
- v_min = v_min[onscreen].clamp(0, height - 1)
- v_max = v_max[onscreen].clamp(0, height - 1)