3D Gaussian Splatting for Real-Time Radiance Field Rendering (SIGGRAPH 2023)
paper :
https://arxiv.org/abs/2308.04079
project website :
https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/
code :
https://github.com/graphdeco-inria/gaussian-splatting
code review :
https://semyeong-yu.github.io/blog/2024/3DGScode/
referenced blog :
https://xoft.tistory.com/51
포스팅을 시작하기에 앞서…
본 글은 직접 논문을 읽으며 정리한 내용이고
더 깔끔한 핵심 정리만 보고 싶다면
3DGS 분석1, 3DGS 분석2 글 괜찮아보임
3DGS Code 리뷰한 내 포스팅도 있음 3DGS 코드분석
수많은 3D Gaussian이 모여 scene을 구성
하고 있다!3D scene representation 방법
Mesh or Point
NeRF
method stochastic sampling
for rendering 때문에 연산량이 많고 noise
생김orthogonality
를 흐리기 때문에 high-freq. output을 잘 표현할 수 없어서 따로 미리 positional encoding을 수행3D Gaussian
method orthogonality
를 잘 살리기 때문에 high-freq. output 잘 표현 가능생략 (추후에 다시 볼 수도)
For unbounded and complete scenes,
For 1080p high resolution and real-time(\(\geq\) 30 fps) rendering,
input
: MVS
(Multi-View Stereo) data,SfM points
for initializationsparse point cloud
에서 시작해서empty space에서의 불필요한 계산을 하지 않도록
continuous volumetric radiance fields 정보를 저장optimization
interleaved with adaptive density control
: highly anisotropic volumetric splats
는 fine structures
를 compact하게 나타낼 수 있음!!spherical harmonics
를 통해 directional appearance(color)
를 잘 나타낼 수 있음!!real-time rendering
:anisotropic splats
respecting visibility orderGPU sorting
algorithm and tile-based rasterization
(projection and \(\alpha\)-blending)Gaussians 수에 제약 없이
빠른 backward pass도 가능빨간 박스 : initialization
파란 박스 : optimization
초록 박스 : 특정 iter.마다 Gaussian을 clone, split, remove
differentiable
volumetric representation의 특성을 가지고 있으면서도 빠른 rendering을 위해 unstructured and explicit
한 게 무엇이 있을까?
\(\rightarrow\) 3D Gaussian !!
a point를 a small planar circle with a normal이라고 가정하는 이전 Point-based rendering 논문들
SfM points는 sparse해서 normals(법선)를 estimate하기 어려울
뿐만 아니라, estimate 한다 해도 very noisy normals를 optimize하는 것은 매우 어렵
\(\rightarrow\) normals 필요 없는 3D Gaussians !!
k-dim. Gaussian : \(G(\boldsymbol x) = (2\pi)^{-\frac{k}{2}}det(\Sigma)^{-\frac{1}{2}}e^{-\frac{1}{2}(\boldsymbol x - \boldsymbol \mu)^T\Sigma^{-1}(\boldsymbol x - \boldsymbol \mu)}\)
scale vector
\(s\) and quaternion
\(q\) for covariance matrix
spherical harmonics
(SH) coeff. for color
opacity
\(\alpha\)3D position
for mean
scale vector(scale) and quaternion(rotation) for covariance matrix
\(\Sigma\) 가 symmetric
and positive semi-definite
이도록 \(\Sigma = R S S^T R^T\) 로 정의해서
\(\Sigma\) 대신 x,y,z-axis scale
을 나타내는 3D vector
\(s\) 와 rotation
을 나타내는 4D quaternion
\(q\) 를 optimize 하자!!
quaternion에 대한 설명은 Quaternion 블로그 참고!!
scale
3D vector \(s\) 초기값
:dist2 = torch.clamp_min(distCUDA2(torch.from_numpy(np.asarray(pcd.points)).float().cuda()), 0.0000001)
scales = torch.log(torch.sqrt(dist2))[...,None].repeat(1, 3)
scale
3D vector \(s\) activation function
:
smooth gradient 얻기 위해 exponential activation function을 씌움
quaternion
\(q\) 초기값
:rots = torch.zeros((fused_point_cloud.shape[0], 4), device="cuda")
rots[:, 0] = 1
anisotropic covariance
는 다양한 모양의 geometry를 나타내기 위해 optimize하기에 적합!EWA volume splatting (2001)
[14] [15] :
world-to-camera 는 linear transformation 이지만,
camera-to-image (projection)
는non-linear transformation
이다!!
world
coordinate (3D) : camera
coordinate (3D) : viewing transformation
affine matrix from world coordinate to camera coordinateimage
coordinate (2D) : Local Affine (Linear) transform으로 Approx.
하기 위해 \(\boldsymbol t = \boldsymbol t_{k}\) 에서의 Taylor Approx.
를 이용하면,Jacobian
(각 axis로 편미분한 matrix) of the affine approx.
of the projective transformation
from camera coordinate to image coordinateGaussian의 중심점
을 \(\boldsymbol t_{k}\) 로 두면 그 주변의 \(\boldsymbol t\)에 대해서는 Jacobian을 이용한 affine(linear) transformation 가능!
Projection
of 3D Gaussiancovariance
to 2D
world coordinate
:
\(\Sigma\) : 3 \(\times\) 3 covariance matrix of 3D Gaussian
image coordiante
(z=1) :
\(\Sigma^{\ast} = J W \Sigma W^T J^T\) : covariance matrix of 2D splat
affine
) :local affine approx.
) :world-to-image covariance
:covariance dimension reduction
:param. gradient 직접 유도 (Appendix A.)
training할 때 automatic differentiation으로 인한 overhead를 방지
하기 위해 param. gradient를 직접 유도
함!
By chain rule, \(\frac{d\Sigma^{\ast}}{ds} = \frac{d\Sigma^{\ast}}{d\Sigma}\frac{d\Sigma}{ds}\) and \(\frac{d\Sigma^{\ast}}{dq} = \frac{d\Sigma^{\ast}}{d\Sigma}\frac{d\Sigma}{dq}\)
By covariance dimension reduction, \(\Sigma^{\ast}\) 는 \(U \Sigma U^T\) 의 좌상단 2-by-2 matrix
where \(U = JW\)
So, 편미분 값은 \(\frac{d\Sigma^{\ast}}{d\Sigma_{ij}} = \begin{bmatrix} U_{1, i} U_{1, j} & U_{1, i} U_{2, j} \\ U_{1, j} U_{2, i} & U_{2, i} U_{2, j} \end{bmatrix}\)
For symmetric and positive semi-definite property of covariance matrix, we set \(\Sigma = MM^T\)
where \(M = RS\)
So, \(\frac{d\Sigma}{ds} = \frac{d\Sigma}{dM} \frac{dM}{ds}\) and \(\frac{d\Sigma}{dq} = \frac{d\Sigma}{dM} \frac{dM}{dq}\)
where \(\frac{d\Sigma}{dM} = 2M^T\)
\(M = RS\)
where \(S = \begin{bmatrix} s_x & s_x & s_x \\ s_y & s_y & s_y \\ s_z & s_z & s_z \end{bmatrix}\)
So, \(\frac{dM_{i, j}}{ds_k} = \begin{cases} R_{i, k} & \text{if j=k} \\ 0 & O.W. \end{cases}\)
\(M = RS\) and \(R(q) = \begin{bmatrix} 1 - 2 \cdot (q_j^2 + q_k^2) & 2 \cdot (q_iq_j - q_rq_k) & 2 \cdot (q_iq_k + q_rq_j) \\ 2 \cdot (q_iq_j + q_rq_k) & 1 - 2 \cdot (q_i^2 + q_k^2) & 2 \cdot (q_jq_k - q_rq_i) \\ 2 \cdot (q_iq_k - q_rq_j) & 2 \cdot (q_jq_k + q_rq_i) & 1 - 2 \cdot (q_i^2 + q_j^2) \end{bmatrix}\)
where \(q = \begin{bmatrix} q_r \\ q_i \\ q_j \\ q_k \end{bmatrix}\)
So, \(\frac{dM}{dq_r} = 2 \begin{bmatrix} 0 & -s_y q_k & s_z q_j \\ s_x q_k & 0 & -s_z q_i \\ -s_x q_j & s_y q_i & 0 \end{bmatrix}\)
and \(\frac{dM}{dq_i} = 2 \begin{bmatrix} 0 & s_y q_j & s_z q_k \\ s_x q_j & -2 s_y q_i & -s_z q_r \\ s_x q_k & s_y q_r & -2 s_z q_i \end{bmatrix}\)
and \(\frac{dM}{dq_j} = 2 \begin{bmatrix} -2 s_x q_j & s_y q_i & s_z q_r \\ s_x q_i & 0 & s_z q_k \\ -s_x q_r & s_y q_k & -2 s_z q_j \end{bmatrix}\)
and \(\frac{dM}{dq_k} = 2 \begin{bmatrix} -2 s_x q_k & -s_y q_r & s_z q_i \\ s_x q_r & -2 s_y q_k & s_z q_j \\ s_x q_i & s_y q_j & 0 \end{bmatrix}\)
gradient for quaternion normalization is straightforward
Spherical Harmonics
(SH) :각도
(\(\theta, \phi\))를 입력받아 구의 표면 위치에서의 값
을 출력하는 함수SH coeff. 초기값
:
GaussianModel().create_from_pcd()
0-band SH (\(\theta, \phi\) 와 관계없는 view-independent color) 의 경우 SfM으로 얻은 point cloud의 RGB color값과 RGB2SH 이용하여 초기화
다른 band의 경우 0으로 초기화
smoothing
역할approx.
새로운 각도(view)를 rendering
할 때 추가적인 MLP query 없이
SH func.으로부터 바로 color 정보 얻을 수 있음SH coeff.
로 color
나타내는 법 :trainable parameter
: SH coeff.인 \(k_{l}^{m}\)light source
마다 SH coeff. \(k_{l}^{m}\) 다르므로 find optimal value)opacity \(\sigma\) 초기값
:
임의의 실수값으로 초기화
inverse_sigmoid(0.1 * torch.ones(…))
opacity \(\sigma\) range
:
\(\sigma \in [0, 1)\) 위해
마지막에 sigmoid activation function을 씌워서 smooth gradient를 얻음
Tile Rasterizer
기능 : 3D Gaussians로 구성된 3D model을 특정 camera pose에 대해 2D rendering
input
: Frustum Culling
:Guard Band
:view frustum의 near plane에 가까이 있는
Gaussian의 경우,nonlinearity
가 심하기 때문에?????
Create Tiles
:CUDA 병렬 처리
를 위해Parallelism
:
tile마다
개별 CUDA thread
block으로 실행하여
forward/backward processing, data loading/sharing을 병렬처리
(여러 threads가 Gaussian points를 shared memory에 collaboratively load)
(VRAM과 DRAM 사이의 이동은 overhead 발생하기 때문에 VRAM
에서 모두 처리해버릴 수 있도록 CUDA Functions
(.cu)를 직접 짬!)
Duplicate with Keys
:
view-space-depth
와 tile-ID
를 이용하여 tile마다 각 Gaussian의 key를 생성CUDA 병렬처리
덕분에 2D Gaussian 하나가 3개의 tiles에 걸쳐 있다면, 3개의 2D Gaussians로 복제(instance화
)되는 것처럼 작동Sort by Keys
:처음에 한 번
sort 하고 나면 끝!! 추가로 per-pixel sorting 할 필요 없음splats가 각 pixel size 정도로 작기 때문에
해당 approx. 오차는 무시 가능! ???
from collections import deque
# 양방향에서 삽입/삭제 가능한 queue형 자료구조
# 1의 자릿수 기준으로 정렬한 뒤
# 10의 자릿수 기준으로 정렬한 뒤
# ...
def radixSort():
nums = list(map(int, input().split(' ')))
buckets = [deque() for _ in range(10)] # 각 자릿수(0~9)에 대응되는 10개의 empty deque()
max_val = max(nums)
queue = deque(nums) # 정렬할 숫자들
digit = 1 # 정렬 기준이 되는 자릿수
while (max_val >= digit): # 가장 큰 수의 자릿수일 때까지만 실행
while queue:
num = queue.popleft() # 정렬할 숫자
buckets[(num // digit) % 10].append(num) # 각 자릿수(0~9)에 따라 buckets에 num을 넣는다.
# 해당 정렬 기준 자릿수에서 buckets에 다 넣었으면, buckets에 담겨있는 순서대로 꺼내와서 정렬한다.
for bucket in buckets:
while bucket:
queue.append(bucket.popleft())
digit *= 10 # 정렬 기준이 되는 자릿수 증가시키기
print(list(queue))
Identify Tile Ranges
: parallel
하게 이루어짐Get Tile Ranges
:
i-th tile에 대한 Gaussian list 범위 읽어옴
forward process
) : 각 pixel에 대해
color
및 opacity
\(\alpha\) 값을 Gaussian list의 앞에서 뒤로
accumulateGaussian의 개수를 제한하지 않음
으로써 scene-specific hyper-param. tuning 없이 arbitrary depth complexity를 가지는 scene을 커버 가능기존 기법들은 pixel마다 정렬이 필요
해서 inefficient했지만Backward process
: opacity 비율에 따라
뒤에서 앞으로
gradient update각 pixel에 대해
pixel마다
global memory에 blended points list를 저장할 수도 있지만tile마다
구했던 range 및 sorted Gaussian list를 재사용
?????
Primitives :
본 논문의 Gaussians는 Euclidean space
에 primitives
를 남김 ?????
\(\rightarrow\)
primitives
)를 미리 정렬(pre-sort
)하여 primitives = Gaussians ?????
Loss :
predicted image와 GT image를 비교하는
L1 loss
및 D-SSIM loss
D-SSIM : Directional Structural Similarity Index Measure
3D Gaussian의 xyz-mean에 대해서만 standard exponential decay scheduling
사용
low resol.부터 warm-up
:stability
향상low band부터 warm-up
:놓친 angular 영역이 있을 경우 SH의 0-band coeff. (base or diffuse color)가 부적절
하게 만들어질 수 있어서optimization of 4 param.의 경우 매 iter.마다 update하지만,
Adaptive Density Control of Gaussians의 경우 100 iter.마다
update
Remove
:Gaussians가 scene을 제대로 표현 못 하는 중
\(\rightarrow\) scene을 제대로 표현하기 위해선 Gaussian position을 크게 옮겨야 함
\(\rightarrow\) view-space positional gradient \(\Delta_{p} L\)가 큼
\(\rightarrow\) under/over-reconstruction 상황이므로 clone/split을 통해 정확한 위치에 Gaussian이 분포하도록 하자
Split
:over-reconstruction
의 경우 3D Gaussians split 2개로 분리
하고 각 scale을 줄인 후 기존 3D Gaussian의 PDF
에 따라 sampling하여 배치view-space positional gradient
\(\Delta_{p} L\)의 avg. magnitude \(\geq\) threshold \(\tau_{pos}\)covariance
가 큼Clone
:under-reconstruction
의 경우 3D Gaussians clone 같은 크기로 copy
후 positional gradient 방향
에 배치view-space positional gradient
\(\Delta_{p} L\)의 avg. magnitude \(\geq\) threshold \(\tau_{pos}\)covariance
가 작음알파 값을 주기적으로 0으로 초기화
하면 전체 Gaussian 조절에 큰 도움이 됨! camera와 가까운 영역
에서 많은 floater
들이 생겨서 Gaussian density가 증가하는데, 이를 제거해주는 역할큰 Gaussian들이 중첩
되어 있는 case를 제거해주는 역할custom CUDA kernel :
tile-based rasterization을 위해
custom CUDA kernel를 추가하여 사용 like
Radix Sort :
fast Radix Sort를 위해 NVIDIA CUB sorting routines
interactive image viewer :
open-source SIBR SIBR 이용해서
interactive image-rendering viewer 만듬 (frame rate 측정에 사용)
Quality
: NeRF 계열 중 SOTA인 Mip-Nerf360
Speed
: NeRF 계열 중 SOTA인 InstantNGP
Plenoxels
Space Carving :
- 설명 : 여러 camera에 대해 voxel-space에서 object 있는 부분만 남기고 깎아내는 기법
- 이유 : 3D reconstruction을 할 때 color 정보만으로 segmentation 가능할 정도로 background는 simple할수록 좋기 때문
- 한계 : 빛, 그림자 같은 정보는 사용하지 않기 때문에 fg/bg 판단만 가능하다. 따라서 lidar처럼 camera에 depth-detection 메커니즘이 없을 경우 물체 내부의 구멍 같은 건 reconstruct 불가능
Intialization (SfM)
: background
퀄리티 저하floater
많이 발생Densification (clone, split)
: background
reconstruction에 중요한 역할thin
structure reconstruction에 중요한 역할Unlimited depth complexity of splats with gradients
: Anisotropic Covariance
: align with surfaces
잘 하지 못해서 fine
structure 잘 나타내지 못함Spherical Harmonics
: view-dependent
effect 담당training view가 부족한 영역
에서는 여전히 floater
, elongated(길쭉한) artifacts
, splotchy(얼룩진) Gaussians
등 artifacts 발생 (Mip-NeRF360 등 prev. methods도 마찬가지)view-dependent appearance
가 나타나는 영역에서는 large Gaussian 만들 때 guard band
등의 이유로 popping
artifacts 발생depth-order
갑자기 바뀔 수 있음anti-aliasing
으로 해결 가능large scene
에 대해서는 position learning-rate
를 줄이는 게 도움됨3D Gaussian
:CUDA
Implementation :real-time rasterization by GPU
:Q1 :
tile-based rasterization과 parallelism의 관계를 간략히 설명해주세요
A1 :
tile(block)에 겹치는 2DGS들을 shared memory에 저장해서
(overlap 기준 : \(\Sigma^{\ast} = J W \Sigma W^T J^T\) 의 eigenvalue \(\times 3\))
그 tile 내에 있는 pixel(thread)들은 block shared memory(tile)에 있는 2DGS를 전부 쓰되
비교적 멀리 있는 2DGS라면 opacity의 \(e^{- \cdot}\) 항에 의해 그 pixel에는 덜 반영됨