3D Gaussian Splatting 에셋을 실시간 엔진 배경으로 이식하기

3D Gaussian Splatting 에셋을 실시간 엔진 배경으로 이식하기

3D Gaussian Splatting으로 캡처한 장면을 Unity·Unreal 같은 실시간 엔진의 배경으로 가져오는 과정을 정리한다. 변환 포맷, 렌더링 방식, 충돌과 조명 보완, 성능 검증까지 실무에서 먼저 확인할 지점을 다룬다.

먼저 알아둘 점

3D Gaussian Splatting(이하 3DGS)은 장면을 삼각형 메시와 텍스처로 직접 표현하지 않는다. 공간에 배치한 수많은 3차원 가우시안을 카메라 방향으로 투영해 화면에 합성하는 방식이다. 각 가우시안에는 위치, 크기와 회전으로 구성된 공분산, 색상 또는 구면 조화 계수, 불투명도 등의 속성이 들어간다.

따라서 일반적인 FBX나 glTF 배경처럼 가져와 머티리얼만 지정하는 작업으로 끝나지 않는다. 엔진 안에서 스플랫을 렌더링할 방법을 정하고 충돌·그림자·동적 오브젝트처럼 3DGS가 원래 제공하지 않는 기능을 별도 자산으로 보완해야 한다.

이식 파이프라인 설계

가장 안전한 흐름은 원본 데이터를 보존한 채 배포용 데이터를 따로 만드는 것이다. 학습이나 재구성 도구가 출력한 PLY에는 속성 이름과 구면 조화 계수 배치가 도구마다 다를 수 있으므로 대상 렌더러가 요구하는 포맷을 먼저 확인해야 한다.

flowchart LR
  A[사진 또는 영상 촬영] --> B[3DGS 재구성]
  B --> C[원본 PLY 보관]
  C --> D[좌표계·스케일 정리]
  D --> E[스플랫 정리·압축]
  E --> F[엔진용 렌더러 데이터 생성]
  D --> G[충돌용 프록시 메시]
  D --> H[조명·내비게이션용 보조 자산]
  F --> I[실시간 엔진 통합]
  G --> I
  H --> I

여기서 원본 PLY는 재변환과 품질 비교를 위한 기준본이다. 엔진별로 최적화된 바이너리나 텍스처 아틀라스만 남기면 나중에 변환기나 셰이더를 바꿀 때 다시 만들기 어려워진다.

좌표계도 초기에 고정해야 한다. 재구성 결과의 위쪽 축, 전방 축, 단위 길이를 엔진 씬의 기준과 맞추고 카메라 시작 위치로 검증한다. 특히 원본이 센티미터인지 미터인지 불분명하면 이동 속도, 안개 거리, 그림자 거리까지 함께 어긋난다.

엔진 좌표계와 스플랫 장면의 축 및 기준점을 대조하는 모습

렌더링 데이터는 무엇으로 구성되는가

가우시안 하나는 화면에서 타원형 커널로 보인다. 카메라 공간에서의 공분산을 2차원으로 투영해 타원의 크기와 방향을 구하고 깊이 순서에 따라 알파 합성을 수행한다. 일반적인 알파 블렌딩의 개념적 표현은 다음과 같다.

Cout=αsCs+(1αs)CdstC_{out}=\alpha_s C_s+(1-\alpha_s)C_{dst}

문제는 반투명 가우시안이 매우 많다는 점이다. 정확한 정렬은 비용이 크고 카메라가 움직일 때 순서도 바뀐다. 그래서 런타임 구현은 보통 다음 요소를 함께 사용한다.

  • 가우시안 속성을 구조화 버퍼나 압축 바이너리에 저장한다.
  • 뷰 프러스텀 밖의 가우시안을 먼저 제거한다.
  • 화면에서 너무 작게 보이는 가우시안을 생략하거나 더 낮은 품질 집합으로 바꾼다.
  • 깊이 기준 버킷 정렬이나 순서 독립 투명도에 가까운 근사 방식으로 합성 비용을 제어한다.
  • 색상은 RGB만 쓰거나 시점 의존 반사를 살릴 필요가 있을 때 제한된 차수의 구면 조화를 사용한다.

구면 조화 계수는 시점 변화에 따른 색 변화를 표현하지만 메모리와 대역폭을 늘린다. 배경이 고정된 조명 아래 있고 카메라 이동 폭도 작다면 낮은 차수로 줄이거나 RGB만 남긴 결과를 비교해 볼 만하다. 반대로 유리창, 광택 바닥, 나뭇잎처럼 시점에 따라 색이 크게 바뀌는 영역은 단순화했을 때 품질 저하가 쉽게 드러난다.

배경으로 쓸 때의 핵심: 하이브리드 구성

3DGS를 배경으로 채택할 때 가장 현실적인 방법은 모든 것을 스플랫으로 해결하려 하지 않는 것이다. 플레이어가 상호작용하는 공간은 단순 메시로 덧대고 시각적 밀도를 요구하는 원경과 복잡한 자연물은 스플랫으로 유지하는 구성이 관리하기 쉽다.

충돌과 내비게이션

스플랫은 표면이 불연속적이고 내부 구조도 모호할 수 있어 충돌체로 적합하지 않다. 별도의 저폴리 프록시 메시를 만들고 콜라이더와 내비게이션 베이크는 이 메시를 기준으로 처리한다. 계단, 경사로, 문턱처럼 플레이 감각에 영향을 주는 면은 캡처 결과를 그대로 믿지 말고 직접 정리한다.

그림자와 조명

많은 3DGS 렌더러는 자체 색상에 이미 촬영 당시의 조명 정보가 포함되어 있다. 그러므로 동적 라이트를 비추면 새 조명과 캡처 조명이 겹쳐 어색해질 수 있다. 다음처럼 역할을 나누면 문제가 줄어든다.

  • 스플랫 배경은 대체로 비조명 또는 제한된 조명 반응으로 렌더링한다.
  • 캐릭터와 상호작용 물체는 일반 PBR 머티리얼과 동적 조명을 사용한다.
  • 바닥 프록시에는 그림자 수신용 머티리얼을 배치해 캐릭터의 접지감을 만든다.
  • 시간대 변화가 중요하면 3DGS 전체를 조명 가능한 배경으로 간주하기보다 메시 기반 환경을 병행하는 편이 낫다.

스플랫 배경 위에 충돌용 바닥 메시와 캐릭터 그림자 수신 영역을 배치한 장면

깊이와 가림 처리

캐릭터가 스플랫의 나무나 난간 뒤로 자연스럽게 가려지려면 렌더러가 깊이 버퍼를 일관되게 기록하거나 최소한 가림에 사용할 깊이 근사값을 제공해야 한다. 이 부분은 플러그인이나 구현마다 차이가 크므로 에셋을 대량 변환하기 전에 다음 장면을 반드시 시험한다.

  1. 카메라가 벽과 난간 가까이 이동하는 장면
  2. 캐릭터가 전경 오브젝트 뒤를 통과하는 장면
  3. 반투명처럼 보이는 식생 앞에서 빠르게 회전하는 장면
  4. 깊이 효과와 포스트 프로세싱을 켠 장면

Unity와 Unreal에서 확인할 인터페이스

엔진 선택보다 중요한 것은 렌더링 경로와 데이터 접근 방식이다. Unity에서는 사용 중인 렌더 파이프라인에서 커스텀 패스, 컴퓨트 셰이더, 구조화 버퍼, 깊이 텍스처를 어떻게 연결할지 확인한다. Unreal에서는 렌더 스레드 통합 방식, 머티리얼과 깊이 패스의 관계, 패키징 대상 플랫폼을 먼저 검토한다.

직접 구현하는 경우 데이터 구조는 대략 다음처럼 설계할 수 있다. 실제 직렬화 레이아웃은 정렬 규칙과 대상 그래픽 API에 맞춰 조정해야 한다.

struct GaussianSplat
{
    float3 position;
    float opacity;
    float4 rotation;
    float3 logScale;
    float shCoefficients[48]; // 예시: 사용 차수에 따라 달라짐
};

셰이더 단계에서는 인스턴스 ID로 가우시안을 읽고 카메라 기준으로 중심점과 타원 축을 계산한 뒤 작은 쿼드에 래스터화한다. 픽셀 셰이더에서는 타원 좌표로 가중치를 구해 가장자리 알파를 감쇠한다.

float GaussianWeight(float2 localPos)
{
    float r2 = dot(localPos, localPos);
    return exp(-0.5 * r2);
}

float4 Fragment(float2 localPos, float3 color, float opacity)
{
    float alpha = saturate(opacity * GaussianWeight(localPos));
    return float4(color, alpha);
}

이 코드는 개념 설명용이다. 실제 구현에서는 화면 공간 타원 계산, 잘림 처리, 깊이 정렬 또는 합성 전략, 감마 공간 처리까지 필요하다.

성능은 개수보다 화면 점유율로 측정한다

가우시안 수가 적어도 화면을 크게 덮는 스플랫이 많으면 픽셀 처리량이 커진다. 반대로 수가 많아도 멀리 있는 배경이라면 컬링과 다운샘플링으로 비용을 줄일 수 있다. 따라서 스플랫 개수 하나만으로 품질과 성능을 판단하면 안 된다.

프로파일링에서는 CPU 준비 시간, GPU 컬링 시간, 정렬 또는 버킷 처리 시간, 드로우 호출 수, 투명도 합성으로 인한 오버드로를 나눠 본다. 품질 단계도 단순히 전체 개수를 균일하게 줄이기보다 원경·중경·전경 또는 화면 기여도에 따라 다른 밀도를 적용하는 편이 효과적이다.

배포 전 체크리스트

  • 원본 PLY와 변환 설정을 버전 관리 가능한 위치에 보관했는가?
  • 대상 플랫폼의 GPU 메모리에서 배경 한 장면이 안정적으로 동작하는가?
  • 카메라 이동과 회전에서 팝핑, 정렬 깨짐, 과도한 깜빡임이 없는가?
  • 캐릭터 충돌, 내비게이션, 그림자가 프록시 메시와 일치하는가?
  • 톤 매핑, 안개, 심도 효과를 적용했을 때 배경의 깊이 정보가 의도대로 동작하는가?
  • 저사양 품질 단계에서 스플랫 밀도와 구면 조화 차수를 함께 낮춰 보았는가?

3DGS는 빠르게 현실 장면을 배경 자산으로 옮길 수 있는 강력한 표현이지만 게임 월드의 모든 기능을 대신하는 범용 메시 포맷은 아니다. 스플랫은 시각적 배경에 집중시키고 플레이 규칙과 조명 반응이 필요한 부분은 메시·콜라이더·라이트로 분리하면 품질과 유지보수성을 함께 확보할 수 있다.

#3D Gaussian Splatting#Unity#Unreal Engine#실시간 렌더링#셰이더

계속 읽어보기

이런 글은 어떠세요?

< Back to Logs