콘텐츠로 이동
Data Prep
상세

3D Gaussian Splatting

메타 정보

항목 내용
논문 3D Gaussian Splatting for Real-Time Radiance Field Rendering
저자 Bernhard Kerbl, Georgios Kopanas, Thomas Leimkuhler, George Drettakis (Inria, Max Planck Institute)
학회 SIGGRAPH 2023 (Best Paper Award)
DOI 10.1145/3592433
코드 https://github.com/graphdeco-inria/gaussian-splatting
분야 Computer Vision, 3D Reconstruction, Neural Rendering

1. 핵심 아이디어

3D Gaussian Splatting (3DGS)은 Neural Radiance Fields (NeRF)의 대안으로 제안된 3D 장면 표현 및 렌더링 기법이다. NeRF가 암묵적(implicit) 신경망 표현을 사용하는 반면, 3DGS는 명시적(explicit) 3D Gaussian primitives를 사용한다.

핵심 차별점

NeRF: 좌표 (x,y,z,theta,phi) --> MLP --> (색상, 밀도)
      - 픽셀당 수백 번의 신경망 추론 필요
      - 느린 렌더링 (수 초~수 분)

3DGS: 3D Gaussian 집합 --> Rasterization --> 이미지
      - 신경망 추론 불필요
      - 실시간 렌더링 (100+ FPS)

2. 3D Gaussian Primitive 정의

각 Gaussian은 다음 속성으로 정의된다:

속성 수학적 표현 설명
위치 (mean) mu in R^3 3D 공간상 중심점
공분산 Sigma in R^(3x3) 형태와 방향 (타원체)
불투명도 alpha in [0,1] 투명도
색상 c (SH coefficients) Spherical Harmonics로 표현된 view-dependent 색상

공분산 행렬 파라미터화

공분산 행렬 Sigma는 positive semi-definite 조건을 만족해야 한다:

Sigma = R * S * S^T * R^T

where:
  R: 회전 행렬 (quaternion q로 파라미터화)
  S: 스케일 행렬 (대각 행렬, s in R^3)

최적화 시에는 (q, s)를 학습하여 유효한 공분산 보장.


3. 렌더링 파이프라인

┌─────────────────────────────────────────────────────────────────┐
│                 3D Gaussian Splatting Pipeline                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  [3D Gaussians]                                                 │
│       |                                                         │
│       v                                                         │
│  ┌─────────────────┐                                           │
│  │ 1. Projection   │  3D Gaussian --> 2D Gaussian              │
│  │    (EWA)        │  Sigma_2D = J * W * Sigma * W^T * J^T     │
│  └────────┬────────┘                                           │
│           |                                                     │
│           v                                                     │
│  ┌─────────────────┐                                           │
│  │ 2. Tile-based   │  화면을 16x16 타일로 분할                  │
│  │    Culling      │  타일별 Gaussian 할당                      │
│  └────────┬────────┘                                           │
│           |                                                     │
│           v                                                     │
│  ┌─────────────────┐                                           │
│  │ 3. Depth Sort   │  타일 내 Gaussians를 깊이순 정렬           │
│  │    (per-tile)   │  GPU radix sort 사용                       │
│  └────────┬────────┘                                           │
│           |                                                     │
│           v                                                     │
│  ┌─────────────────┐                                           │
│  │ 4. Alpha        │  front-to-back 순서로 블렌딩               │
│  │    Blending     │  C = sum_i (c_i * alpha_i * T_i)          │
│  └────────┬────────┘  T_i = prod_{j<i} (1 - alpha_j)           │
│           |                                                     │
│           v                                                     │
│     [Rendered Image]                                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

3.1 EWA (Elliptical Weighted Average) Splatting

3D Gaussian을 2D로 투영할 때, 원근 투영의 비선형성으로 인해 정확한 2D 공분산 계산 필요:

Sigma' = J * W * Sigma * W^T * J^T

where:
  W: viewing transformation (world --> camera)
  J: Jacobian of the projective transformation
  Sigma': 2D screen-space covariance

3.2 Alpha Blending

깊이 순서대로 색상 합성:

C(x) = sum_{i in N} c_i * alpha_i * prod_{j=1}^{i-1} (1 - alpha_j)

where:
  c_i: i번째 Gaussian의 색상
  alpha_i: i번째 Gaussian의 기여도 (불투명도 x Gaussian 값)

4. 학습 (Optimization)

4.1 손실 함수

L = (1 - lambda) * L1 + lambda * L_D-SSIM

where:
  L1: 픽셀별 L1 loss
  L_D-SSIM: D-SSIM (structural similarity) loss
  lambda: 0.2 (기본값)

4.2 Adaptive Density Control

학습 중 Gaussian 수를 동적으로 조절:

┌─────────────────────────────────────────────────────────────────┐
│              Adaptive Density Control                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  [Densification] 100 iteration마다 수행                         │
│                                                                 │
│  1. Clone (작은 Gaussian)                                       │
│     - grad(position) > threshold                                │
│     - scale < scene_extent / 10                                 │
│     --> 같은 위치에 복사본 생성                                  │
│                                                                 │
│  2. Split (큰 Gaussian)                                         │
│     - grad(position) > threshold                                │
│     - scale >= scene_extent / 10                                │
│     --> 2개의 작은 Gaussian으로 분할                             │
│                                                                 │
│  [Pruning]                                                      │
│                                                                 │
│  3. Remove                                                      │
│     - alpha < threshold (너무 투명)                              │
│     - scale > scene_extent (너무 큼)                             │
│     --> 제거                                                    │
│                                                                 │
│  4. Opacity Reset                                               │
│     - 주기적으로 alpha를 낮은 값으로 리셋                         │
│     --> 불필요한 Gaussian이 자연스럽게 pruning됨                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5. NeRF vs 3D Gaussian Splatting 비교

특성 NeRF 3D Gaussian Splatting
표현 방식 Implicit (MLP) Explicit (3D Gaussians)
렌더링 속도 느림 (수 초) 실시간 (100+ FPS)
학습 시간 수 시간 수십 분
메모리 사용 낮음 (MLP weights) 높음 (수백만 Gaussians)
편집 용이성 어려움 용이 (명시적 primitives)
품질 높음 동등 또는 우수
Anti-aliasing 어려움 자연스러움 (Gaussian 특성)
View-dependent 효과 MLP로 학습 Spherical Harmonics

6. Spherical Harmonics (SH) for View-Dependent Color

각 Gaussian의 색상은 보는 방향에 따라 달라질 수 있다 (반사, 광택 등):

c(d) = sum_{l=0}^{L} sum_{m=-l}^{l} c_{l,m} * Y_l^m(d)

where:
  d: viewing direction
  Y_l^m: spherical harmonic basis functions
  c_{l,m}: learned coefficients
  L: maximum degree (보통 3, 총 16개 계수)

SH Degree별 효과

Degree 계수 수 표현 가능한 효과
0 1 Diffuse (단색)
1 4 부드러운 방향성
2 9 반사 하이라이트
3 16 복잡한 광택 효과

7. Python 구현 예시

7.1 공식 코드 사용법

# 설치
git clone https://github.com/graphdeco-inria/gaussian-splatting.git
cd gaussian-splatting

# 환경 설정
conda create -n gaussian_splatting python=3.8
conda activate gaussian_splatting
pip install -r requirements.txt

# submodules 설치 (diff-gaussian-rasterization, simple-knn)
pip install submodules/diff-gaussian-rasterization
pip install submodules/simple-knn

7.2 학습

# COLMAP으로 카메라 파라미터 추출된 데이터 필요
python train.py -s <path_to_dataset>

# 주요 옵션
python train.py \
    -s ./data/garden \           # 데이터 경로
    --iterations 30000 \         # 학습 iteration
    --sh_degree 3 \              # SH degree
    --densify_until_iter 15000 \ # densification 종료 시점
    --densification_interval 100 # densification 간격

7.3 렌더링

# 학습된 모델로 렌더링
python render.py -m <path_to_trained_model>

# 평가 (PSNR, SSIM, LPIPS)
python metrics.py -m <path_to_trained_model>

7.4 핵심 데이터 구조 (PyTorch)

import torch
import torch.nn as nn

class GaussianModel(nn.Module):
    """3D Gaussian Splatting 모델의 핵심 구조"""

    def __init__(self, sh_degree=3):
        super().__init__()
        self.sh_degree = sh_degree

        # 학습 가능한 파라미터
        self._xyz = None           # (N, 3) positions
        self._features_dc = None   # (N, 1, 3) DC component of SH
        self._features_rest = None # (N, (D+1)^2-1, 3) higher order SH
        self._scaling = None       # (N, 3) log scale
        self._rotation = None      # (N, 4) quaternion
        self._opacity = None       # (N, 1) logit opacity

    def create_from_pcd(self, pcd_points, pcd_colors):
        """Point cloud로부터 초기화"""
        num_points = pcd_points.shape[0]

        # 위치: point cloud 좌표
        self._xyz = nn.Parameter(torch.tensor(pcd_points, dtype=torch.float32))

        # 색상: RGB를 SH DC 성분으로 변환
        # C0 = 0.28209479177387814 (SH 기저 함수의 첫 번째 계수)
        fused_color = (pcd_colors - 0.5) / 0.28209479177387814
        self._features_dc = nn.Parameter(
            fused_color.unsqueeze(1).contiguous()
        )

        # Higher order SH: 0으로 초기화
        num_sh = (self.sh_degree + 1) ** 2 - 1
        self._features_rest = nn.Parameter(
            torch.zeros((num_points, num_sh, 3), dtype=torch.float32)
        )

        # 스케일: 이웃 점들과의 거리 기반 초기화
        dist = self._compute_nearest_neighbor_dist()
        scales = torch.log(dist.unsqueeze(-1).repeat(1, 3))
        self._scaling = nn.Parameter(scales)

        # 회전: identity quaternion
        rots = torch.zeros((num_points, 4), dtype=torch.float32)
        rots[:, 0] = 1  # w=1, x=y=z=0
        self._rotation = nn.Parameter(rots)

        # 불투명도: inverse sigmoid(0.1)
        opacities = torch.logit(torch.full((num_points, 1), 0.1))
        self._opacity = nn.Parameter(opacities)

    @property
    def get_xyz(self):
        return self._xyz

    @property
    def get_scaling(self):
        return torch.exp(self._scaling)  # log scale --> scale

    @property
    def get_rotation(self):
        return torch.nn.functional.normalize(self._rotation, dim=-1)

    @property
    def get_opacity(self):
        return torch.sigmoid(self._opacity)

    @property
    def get_covariance(self, scaling_modifier=1):
        """공분산 행렬 계산"""
        scaling = self.get_scaling * scaling_modifier
        rotation = self.get_rotation

        # Quaternion --> Rotation matrix
        r, x, y, z = rotation[:, 0], rotation[:, 1], rotation[:, 2], rotation[:, 3]
        R = torch.stack([
            1 - 2*(y*y + z*z), 2*(x*y - r*z), 2*(x*z + r*y),
            2*(x*y + r*z), 1 - 2*(x*x + z*z), 2*(y*z - r*x),
            2*(x*z - r*y), 2*(y*z + r*x), 1 - 2*(x*x + y*y)
        ], dim=-1).reshape(-1, 3, 3)

        # S * S^T
        S = torch.diag_embed(scaling)

        # Sigma = R * S * S^T * R^T
        covariance = R @ S @ S.transpose(-1, -2) @ R.transpose(-1, -2)
        return covariance

    def _compute_nearest_neighbor_dist(self):
        """KNN으로 최근접 이웃 거리 계산"""
        from simple_knn._C import distCUDA2
        dist2 = distCUDA2(self._xyz)
        return torch.sqrt(torch.clamp(dist2, min=1e-7))


# 학습 설정 예시
def setup_optimizer(model):
    """파라미터별 learning rate 설정"""
    params = [
        {'params': [model._xyz], 'lr': 0.00016, 'name': 'xyz'},
        {'params': [model._features_dc], 'lr': 0.0025, 'name': 'f_dc'},
        {'params': [model._features_rest], 'lr': 0.0025 / 20, 'name': 'f_rest'},
        {'params': [model._scaling], 'lr': 0.005, 'name': 'scaling'},
        {'params': [model._rotation], 'lr': 0.001, 'name': 'rotation'},
        {'params': [model._opacity], 'lr': 0.05, 'name': 'opacity'},
    ]
    return torch.optim.Adam(params, lr=0.0, eps=1e-15)

7.5 Differentiable Rasterization (개념)

def render(viewpoint_camera, gaussians, bg_color):
    """
    Differentiable Gaussian Rasterization
    실제 구현은 CUDA 커널 사용 (diff-gaussian-rasterization)
    """
    # 1. 3D Gaussian을 카메라 좌표계로 변환
    means3D = gaussians.get_xyz
    means2D = project_to_screen(means3D, viewpoint_camera)

    # 2. 2D 공분산 계산 (EWA splatting)
    cov3D = gaussians.get_covariance
    cov2D = compute_cov2D(means3D, cov3D, viewpoint_camera)

    # 3. 색상 계산 (SH evaluation)
    view_dir = means3D - viewpoint_camera.position
    view_dir = view_dir / view_dir.norm(dim=-1, keepdim=True)
    colors = eval_sh(gaussians.sh_degree, gaussians.get_features, view_dir)

    # 4. Rasterization (CUDA)
    rendered_image = rasterize_gaussians(
        means2D=means2D,
        cov2D=cov2D,
        colors=colors,
        opacities=gaussians.get_opacity,
        image_height=viewpoint_camera.image_height,
        image_width=viewpoint_camera.image_width,
        bg_color=bg_color
    )

    return rendered_image

8. 주요 확장 연구

프로젝트 설명 특징
4D Gaussian Splatting 동적 장면 표현 시간 축 추가, deformation field
Gaussian-SLAM SLAM에 적용 실시간 3D 재구성 + 위치 추정
DreamGaussian Text-to-3D 생성 SDS loss + 3DGS
GaussianEditor 3D 편집 Gaussian 단위 편집, inpainting
Scaffold-GS 구조화된 표현 Anchor points + local Gaussians
Mip-Splatting Anti-aliasing 3D smoothing filter

9. 하이퍼파라미터 가이드

9.1 주요 설정

파라미터 기본값 설명
iterations 30,000 총 학습 iteration
sh_degree 3 Spherical Harmonics 차수
position_lr_init 0.00016 위치 초기 learning rate
densify_until_iter 15,000 densification 종료 시점
densification_interval 100 densification 주기
opacity_reset_interval 3,000 opacity 리셋 주기
densify_grad_threshold 0.0002 densification 기준 gradient
percent_dense 0.01 clone/split 비율

9.2 품질 vs 속도 트레이드오프

고품질:
  - sh_degree: 3
  - iterations: 30,000+
  - densify_until_iter: 20,000

빠른 학습:
  - sh_degree: 0-1
  - iterations: 7,000
  - densify_until_iter: 5,000

10. 한계 및 고려사항

한계 설명
메모리 사용량 수백만 Gaussian -> GB 단위 VRAM
초기화 의존성 COLMAP 등 SfM 결과 필요
동적 장면 기본 버전은 정적 장면만 지원
저조도/반사 극단적 조명 조건에서 품질 저하
큰 장면 unbounded scene에서 추가 처리 필요

적합한 사용 사례

  • 실시간 Novel View Synthesis
  • VR/AR 콘텐츠 제작
  • 게임/시뮬레이션 환경 구축
  • 3D 스캔 및 재구성
  • 디지털 트윈

11. 참고 자료

  • 논문: https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/
  • 공식 코드: https://github.com/graphdeco-inria/gaussian-splatting
  • ACM DL: https://dl.acm.org/doi/10.1145/3592433
  • Awesome 3D Gaussian Splatting: https://github.com/MrNeRF/awesome-3D-gaussian-splatting
  • Radiance Fields: https://radiancefields.com/

최종 업데이트: 2026-02-16