TECHARTNOMAD | MAZELINE.TECH

TECH.ART.FLOW.IO

[번역] Impostors 상세 해설 — 종이 한 장으로 만들어낸 아름다운 환상

jplee 2026. 2. 17. 00:10

역자의 말: 구정 연휴 잘 보내고 계신가요? 부모님댁에도 다녀오셨겠죠? 여전히 운전면허가 없는 저로서는 대중교통에서 보내는 시간이 참 많습니다. 고향에 가는 길이라도 만약 대중교통을 이용한다면 그 시간에 짧게 읽어볼만한 짧은 임포스터 소개글이 있어서 업데이트 해 봅니다.


저자: 欧几里得范数

Impostors 상세 해설 — 종이 한 장으로 만들어낸 아름다운 환상

0 들어가며

예전에 《Call of Dragons》를 프레임 캡처로 분석한 적이 있는데, 자세한 내용은 《Call of Dragons》렌더링 프레임 캡처 분석과 미스터리에서 볼 수 있습니다. 해당 글에서는 《Call of Dragons》가 3D 씬과 일부 2D 렌더링을 결합해 성능을 최적화한다고 설명하며, 그 핵심 아이디어로 “3D→2D 변환(三转二)”을 언급합니다. “3D→2D 변환”에 대해 더 자세히는 제가 더 예전에 쓴 《3D→2D 변환(三渲二/三转二) 2D 애니메이션 방안 조사》도 참고해 주세요.

이렇게 3D 오브젝트를 2D로 바꾸는 사고방식은 또 하나의 렌더링 기술에도 그대로 적용되는데, 그게 바로 Impostors입니다. 중국어로는 “冒名顶替者(사칭자)” 정도로 번역되지만(어감이 조금 애매해서…), 아래에서는 그냥 Impostors라고 하겠습니다.

Impostors를 아주 간단히 말하면, Billboard 형태의 2D 판으로 복잡한 3D 모델을 표현하는 방식입니다. 《Call of Dragons》에서 사용한 3D→2D는 Impostors의 단순화 버전 정도라고 보면 됩니다. 특히 《Call of Dragons》는 카메라가 회전하지 않기 때문에 상황이 더 단순합니다. 그래서 billboard 렌더링조차 필요 없이, 2D 판을 카메라 니어 플레인에 평행하게 두기만 해도 됩니다.

하지만 일반적인 의미의 Impostors는 다양한 게임에서 널리 쓰이며(대개 원경 표현), 카메라 관찰 각도의 변화, 원근, 렌더링 결과 재현, 전환(transition) 등 고려해야 할 요소가 많습니다. 그래서 Impostors 실전은 절대 만만한 일이 아닙니다.

Unity Asset Store에 Amplify Impostors라는 도구가 있는데, Impostors 기술의 훌륭한 실전 구현을 제공합니다. 본 글의 목적은 Amplify Impostors를 바탕으로 Impostors의 구현 원리와 사용된 기술을 자세히 해부해 보면서, Impostors 렌더링 기술을 학습하고 분석하는 것입니다.

본 글에서 다루는 내용:

  1. Impostor 개념.
  2. Amplify Impostors의 간단한 사용법(인터페이스 파라미터 소개, LOD Group 연동 등).
  3. 공식 매뉴얼 핵심 요약(지원/미지원 기능, Impostor 타입별 특징, 적합한 사용 시나리오 등).
  4. Amplify Impostors 베이킹 과정 상세(빌보드 메시 생성, GBufferTextures 생성 등).
  5. Amplify Impostors 데모 씬 간단 분석.

테스트 환경: Unity 2021.3.12f1, Amplify Impostors 0.9.9.3, URP 12.1.7

1 Impostor 개념

《Real-Time Rendering, Fourth Edition》(감사하게도 Morakito가 번역한 중문판이 있습니다)의 13.6절에는 빌보드(Billboarding) 기술이 소개되어 있습니다. 원리는 카메라 관찰 방향에 맞춰 텍스처 사각형의 방향을 바꾸는 것으로, 보통 그 사각형이 항상 화면과 평행하게 보이도록 합니다. 풀, 연기, 에너지 실드, 구름 등을 표현할 때 자주 쓰이며(예: 원신의 원경 구름), 원서에도 빌보드로 구름층을 시뮬레이션한 예시가 나옵니다.

Impostor는 빌보드 기술의 한 가지 응용입니다. 현재 관찰점에서 복잡한 오브젝트를 텍스처로 렌더링하고, 그 텍스처를 빌보드 메시(Billboard Mesh)에 매핑해 Impostor를 만드는 것이죠. 같은 오브젝트의 여러 인스턴스에 재사용하거나, 몇 프레임 동안 반복 사용해서 생성 비용을 분산할 수도 있습니다.

Impostor 텍스처는 오브젝트가 렌더링된 영역에서는 불투명하고, 그 외 영역은 완전히 투명합니다. 즉 복잡한 모델을 단일 텍스처로 단순화할 수 있기 때문에 먼 거리 오브젝트를 빠르게 렌더링하는 데 아주 유용합니다.

먼 거리 렌더링이라고 하면 떠오르는 기술이 또 있죠. 바로 LOD입니다. 멀리 있는 오브젝트에 더 단순한 메시를 쓰는 방식인데, 이런 단순화 메시는 종종 오브젝트의 형태 정보와 색 정보가 크게 손실됩니다. 반면 Impostor는 그런 단점이 덜한데, 생성 텍스처 해상도를 디스플레이 해상도에 가깝게 맞출 수 있기 때문입니다.

또 다른 Impostor 사용 시나리오는 관찰자가 그 오브젝트의 한쪽 면만 보게 되는 경우입니다(《Call of Dragons》가 딱 그 케이스죠).

Impostor 텍스처 생성 과정에서는 카메라가 오브젝트 바운딩 박스 중심을 향하도록 두고, 뷰 프러스텀을 오브젝트 투영 바운딩 박스의 최소 사각형으로 설정합니다. Impostor 텍스처의 알파는 먼저 0으로 클리어하고, 오브젝트가 렌더링되는 픽셀은 알파를 1.0으로 설정합니다. 이렇게 만든 텍스처를 “시점(뷰포인트)을 바라보는” 빌보드 메시의 텍스처로 사용합니다.

UE4 문서 — Render 3D Imposter Sprites를 참고하면, Impostors는 플립북(Flipbook) 스타일 텍스처를 사용하는 스프라이트입니다. 하나의 정적 메시를 가능한 관찰 각도별로 렌더링한 결과를 저장해 두고, 스프라이트가 원본 메시와 머티리얼/라이팅 측면에서 최대한 비슷하게 보이도록 하는 것이죠. 다만 **프레임 간 팝핑(popping)**이 생길 수도 있어서 모든 상황에 적합하진 않습니다. 하지만 카메라에 가까이 오지 않고, 천천히 움직이는 오브젝트를 대량으로 렌더링할 때 매우 유용합니다.

아래는 플립북 스타일 텍스처 예시입니다. 다양한 각도에서의 머티리얼 속성(Albedo, Normal 등)을 텍스처에 저장해, 스프라이트 렌더링 시 원본 메시를 최대한 흉내 냅니다. 이 텍스처들이 바로 Impostor의 GBufferTextures입니다.

2 Amplify Impostors 소개

Amplify Impostors는 클래식 billboard 기술의 “현대적 버전”을 활용해, 기하를 단순화하면서도 부피감(볼륨)이 정확한 복잡 모델 표현을 구축하는 강력한 도구입니다.

공식 이미지 한 장만 봐도 Impostors가 무엇을 하는지 감이 옵니다. 3D 메시를 2D 판으로 바꿔서 시각적 착시를 만들어내고, 그로써 렌더링 성능과 표현을 최적화합니다. 보통은 원경 표현에 사용됩니다.

공식 데모에는 Impostors와 3D 메시의 렌더링 결과를 비교하는 씬이 있습니다(왼쪽 아래가 3D Mesh, 오른쪽 위가 Impostors). 멀리서 보면 Amplify Impostors의 결과는 원본 메시와 거의 구분이 안 될 정도로 훌륭해서, 완전히 “가짜로 진짜를 속이는” 수준까지 갑니다.

하지만 가까이 다가가면 Impostors가 티가 나기 시작하고, 시점을 회전할 때 팝핑과 부자연스러운 전환 고스팅이 비교적 명확히 보입니다.

3 Amplify Impostors Start Screen

Amplify Impostors.unitypackage를 임포트하면 Amplify Impostors Start Screen이 자동으로 뜹니다.

Manual 버튼을 누르면 공식 매뉴얼로 바로 이동할 수 있습니다.

Start Screen에는 HDRP, URP, Built-In 3가지 렌더 파이프라인용 데모 예제 임포트 버튼도 제공됩니다. 원문에서도 강조하듯, 데모를 수동으로 풀어 임포트하지 말고 Start Screen을 통해 임포트하는 것을 권장합니다.

공식적으로는 Unity 2019.4 LTS 이상, URP 10 또는 HDRP 10(SRP 10) 이상, Built-in 파이프라인을 원클릭으로 지원합니다. 다만 URP/HDRP를 실제 프로젝트에서 수정해 쓰는 경우가 많아서, 데모 임포트가 실패할 수 있습니다. 저도 커스텀 URP 파이프라인에 바로 임포트했다가 실패했는데, 원인은 Decal Feature와 SSAO Feature가 빠져 있었기 때문입니다(우리 쪽에서 URP에서 Decal을 제거한 상태였음).

그래서 Amplify Impostors를 처음 만져볼 때는, 우선은 네이티브 URP(또는 HDRP)로 시작하는 것을 권장합니다. 어느 정도 감이 잡힌 다음에 프로젝트 파이프라인으로 이식하는 편이 안전합니다.

4 매뉴얼 핵심 요약

4.1 Impostor 타입

Amplify Impostors는 3가지 사전 베이킹(Pre-baked) Impostor 타입을 제공합니다.

  • Spherical: 전통적인 경위도(위도/경도) 분할로 베이킹 각도를 잡아 스냅샷을 찍습니다. 이 타입은 셰이더가 매우 단순해서 성능이 좋고, 가까이서 봐도 기본적인 품질은 괜찮은 편입니다. 다만 시점이 변하는 여러 프레임 사이에서 떨림(지터)이 꽤 눈에 띕니다. 렌더 해상도를 희생해 프레임을 올리면 완화할 수는 있지만(…그걸 누가 하겠냐 싶긴 합니다).

Spherical 도해는 아래와 같습니다. 왼쪽 원숭이는 실제 3D 모델, 오른쪽 원숭이는 Impostor 결과이며, 회색 지오메트리는 사전 베이킹된 모든 각도를 나타냅니다(다른 타입도 같은 배치).

  • Octahedron: icosphere(측지 다면체, Geodesic polyhedron) 분포로 베이킹 각도를 잡아 스냅샷을 찍습니다. 장점은 카메라의 임의 좌표가 주어졌을 때 다면체 위에서 가장 가까운 3개의 프레임을 찾아 블렌딩할 수 있다는 점입니다. 그래서 시점 이동 시 프레임 간 떨림 문제를 잘 해결합니다. 다만 셰이더 복잡도가 더 올라가고, 가까이서 보면 블렌딩 때문에 고스팅 아티팩트가 눈에 띌 수 있습니다.

  • HemiOctahedron: Octahedron의 변형으로, “icosphere의 상반구”에서만 같은 수의 스냅샷을 찍습니다. 그 결과 블렌딩 정밀도가 사실상 2배로 올라갑니다. 단점은 하반구를 베이킹하지 않기 때문에 아래에서 보게 되면 결과가 틀어진다는 점입니다. 즉 항상 위에서만 관찰할 것이 확실할 때만 적합합니다.

4.2 런타임 렌더링 Feature

  1. SRP(HD and LW)(v4.9.0+)
  2. Standard/Legacy의 포워드 렌더링과 디퍼드 렌더링
  3. 동적 조명과 그림자
  4. 오브젝트 교차 시 깊이 쓰기
  5. 글로벌 일루미네이션
  6. 베이킹 라이트맵(커스텀 베이킹으로)
  7. GPU Instancing
  8. 디더(노이즈) 기반 교차 전환

4.3 기타 지원/미지원 사항

  1. 커스텀 패킹 텍스처 지원: Standard/커스텀 머티리얼에 최대 4장 텍스처를 렌더링할 수 있고, 커스텀 베이킹을 사용하면 최대 8장까지도 가능.
  2. 커스텀 셰이프 에디터 지원: Impostor의 커스텀 형태를 자동 생성하여 투명 영역을 줄여 오버드로를 줄이며, 수동 편집도 가능.
  3. 베이킹 프리셋 지원: 베이킹 프리셋과 다양한 임포트/익스포트 옵션 제공.
  4. Skinned Mesh 베이킹 미지원: 현재 캐릭터 등 애니메이션 스킨 메시를 지원하지 않습니다. 개발자는 실시간 베이킹 변형을 계획 중이며, 애니메이션 오브젝트를 지정 속도로 Impostors로 렌더링하게 할 예정이라고 합니다.
  5. 근거리 품질을 올리고 싶다면 텍스처 해상도를 높이는 수밖에 없음: 근거리에서는 아티팩트가 생깁니다. 즉 impostors는 근거리 렌더링용이 아니고, 실제 Mesh를 대체하는 것이 아니라 원거리 성능 최적화용입니다. 보통 2K 텍스처면 괜찮고, 모바일은 1K도 가능(…1K도 큰데요).
  6. 셰이더에 Deferred Pass가 있어야 표준 베이킹 지원: Unity Standard Shader 같은 경우죠. 단, 생성된 impostor는 포워드/디퍼드 렌더링 양쪽에서 사용 가능합니다. 원본 Mesh가 커스텀 포워드 셰이더(예: 카툰 렌더링)를 쓰는 경우에는 custom baking shader로 베이킹해야 합니다.

5 Impostor 베이킹 UI

Impostor 베이킹은 Inspector에서 Amplify Impostor 스크립트가 제공하는 UI로 수행합니다.

매뉴얼을 참고하여 몇 가지 핵심 파라미터를 정리하면:

  1. Impostor Asset: 베이킹 결과로 생성되는 리소스 파일 참조. 해당 오브젝트의 대부분 impostor 정보를 포함합니다. 같은 오브젝트의 impostor가 여러 개면 리소스를 공유할 수 있다는 장점이 있습니다.
  2. LOD Group: 오브젝트의 LOD Group 컴포넌트 참조. 작성자는 impostor를 LOD Group과 함께 쓰는 것을 더 권장하는 듯합니다.
  3. References: impostor로 베이킹할 renderer 참조.
  4. BakeType: 베이킹/런타임 렌더링에 사용할 기술, 즉 Spherical/Octahedron/HemiOctahedron.
  5. Texture Size: 최종 베이킹 이미지 크기. 클수록 근거리에서 좋아지지만 메모리/런타임 비용 증가.
  6. Axis Frames: 축 방향 프레임 수. 예를 들어 16이면 256(16x16) 스냅샷을 찍는다는 뜻입니다(최종 2D 텍스처의 가로/세로 축).
  7. Pixel Padding: 스냅샷 가장자리 픽셀 패딩. mipmap 때문에 생기는 아티팩트를 방지.
  8. Billboard Mesh: 베이킹할 Billboard Mesh 형태 편집. 자동 추정 + 수동 편집 지원.
  9. Bake Preset: Standard 프리셋 및 커스텀 프리셋 제공. 베이킹 셰이더, 런타임 셰이더(커스텀 가능), 출력 텍스처(_AlbedoAlpha, _SpecularSmoothness 등) 및 속성 설정 가능.

6 Impostor 베이킹 과정

UI를 봤으니 이제 베이킹 과정을 보겠습니다. 앞서 말했듯 셰이더에 Deferred Pass가 있어야 표준 베이킹이 가능하며, 매뉴얼에 따르면 기본 베이킹은 원본 오브젝트 셰이더의 deferred pass를 사용합니다.

전체 과정은 크게 3단계로 정리할 수 있습니다.

  1. Billboard Mesh 생성
  2. GBufferTextures 베이킹
  3. Impostor Billboard 생성

6.1 Billboard Mesh 생성

최종적으로 Impostor는 빌보드 방식의 메시 위에 렌더링되므로, 먼저 메시가 어떤 형태여야 하는지부터 고민해야 합니다. “그냥 사각형 Quad면 되지 않나?”가 가장 먼저 떠오르겠지만, 사각형도 충분히 쓸 수는 있어도 실제 오브젝트는 불규칙한 형태라서 투명 영역이 많이 생깁니다. UV는 메시 전체 정점 사이에서 보간되므로, 투명 영역이 많으면 텍스처 공간 낭비실제 정밀도 저하로 이어집니다.

그래서 Billboard Mesh는 원본 오브젝트 외곽에 최대한 붙는 단순한 불규칙 폴리곤이 가장 합리적입니다. 이렇게 하면 텍스처 활용이 좋아지고, 오버드로도 줄어듭니다.

그럼 이 “외곽에 붙는 단순 폴리곤”을 어떻게 만들까요? 핵심은 모든 프레임에서의 오브젝트 경계를 찾고, 그 경계의 최대(혹은 최소) 범위를 최종 메시 경계로 삼는 것입니다.

Amplify Impostors의 방식은 대략 다음과 같습니다(설명을 쉽게 하려고 단계 표현은 약간 다를 수 있지만 원리는 동일):

  1. 기본 256x256의 AlphaTexture를 만들고, 각 프레임에 대해 오브젝트를 정사영으로 AlphaTexture에 렌더링합니다(오브젝트가 화면 중앙에 오도록 적절한 VP 행렬을 설정). 이때 Pass는 DEFERRED → Deferred → GBuffer 순으로 사용 가능한 디퍼드 렌더링 Pass를 찾습니다. 즉 모든 프레임에서의 오브젝트 Alpha(마스크)를 하나의 마스크에 누적하여, 모든 프레임에서의 불투명 영역을 커버하는 Mask를 만듭니다. 여기서는 단순 폴리곤 생성용이므로 텍스처를 크게 잡아도 의미가 없습니다. 또한 여기서 베이킹 셰이더는 디퍼드 렌더링 Pass가 반드시 있어야 한다는 것도 확인할 수 있습니다.
  2. 위에서 만든 AlphaTexture의 Alpha 채널만 뽑아내어, 같은 크기(256x256)의 CombinedAlphaTexture에 Blit합니다. 목적은 디퍼드 패스로 인해 AlphaTexture에 포함된 RGB를 배제하고 Alpha만 필터링하기 위함입니다.
  3. CombinedAlphaTexture(최종 마스크)를 바탕으로 어떤 알고리즘이든 사용해(외곽 근사만 해도 됨) 오브젝트의 근사 외곽 폴리곤을 만들고, 그 정점으로 Billboard Mesh를 생성합니다. Amplify Impostors는 UnityEditor의 SpriteUtility.GenerateOutline을 사용합니다.

이 3단계의 구체 구현을 이해하면, 이후 GBufferTextures 베이킹 로직도 대부분 동일하기 때문에 자연스럽게 이어집니다.

6.1.1 AlphaTexture 생성(상세)

  1. AmplifyImpostor.cs가 참조하는 AmplifyImpostorAsset에서 **베이킹에 필요한 정보(타입/셰이더/프리셋 등)**를 가져옵니다.
  2. 모든 스냅샷 각도에서의 Bound 최대값을 계산합니다(CalculatePixelBounds). 각 각도별 CameraView 회전 성분을 적용해 mesh.bounds를 회전시키고, 모든 각도에서 xy Bound와 depth Bound의 최대를 찾습니다.
  3. AlphaTexture(또는 GBufferTextures 베이킹이면 _AlbedoAlpha, _SpecularSmoothness 등)와, 깊이 테스트용 trueDepth Texture를 생성합니다. 크기는 AlphaTexture는 기본 256, GBufferTextures면 설정한 Texture Size(예: 2048)입니다.
  4. sRGB 쓰기를 켭니다.
  5. 원본 renderer의 transform(position/rotation/scale 등)을 저장하고, 기본값으로 리셋합니다(원점, 스케일 1).
  6. 베이킹 시작(RenderImpostor). Bake shader가 비어 있으면 표준 베이킹, 있으면 커스텀 베이킹.
  7. commandBuffer를 만들고 MRT로 AlphaTexture(또는 GBufferTextures 배열) + trueDepth를 RenderTarget으로 설정한 뒤, ClearRenderTarget으로 초기화.
  8. 베이킹 대상 Renderer를 필터링(null/비활성/ShadowsOnly 제외).
  9. 모든 스냅샷 각도를 순회하며 각 프레임 스냅샷을 수행.
  10. 각 프레임마다 CameraView 회전 성분을 계산하고, 회전된 mesh.bounds로 frameBounds를 얻은 뒤, 이를 기반으로 카메라 V와 정사영 P를 계산.
  11. VP/viewport를 설정합니다. AlphaTexture 단계의 viewport는 항상 (0,0,256,256). 표준 베이킹이면 머티리얼에서 DEFERRED/Deferred/GBuffer 순으로 Pass를 찾고, 없으면 LightMode=Deferred로 폴백하며 DepthOnly PrePass가 있으면 기록. 커스텀 베이킹이면 (원본 머티리얼 → 커스텀 베이크 머티리얼) 매핑을 저장하고 Pass 0 사용.
  12. Lightmap/GI 키워드/값을 설정하고 LightProbe는 끕니다(베이킹 결과가 위치/환경에 흔들리지 않도록).
  13. DrawRenderer로 베이킹 수행(표준이면 PrePass 후 GBuffer, 커스텀이면 커스텀 머티리얼 Pass 0).
  14. 모든 프레임 반복 후, 누적된 AlphaTexture 획득.
  15. commandBuffer/임시 머티리얼 등 리소스 정리.

6.1.2 CombinedAlphaTexture 생성

AlphaTexture에서 Alpha 채널만 필터링하는 단계입니다.

  1. sRGB 쓰기 켜기.
  2. ShaderPacker 셰이더/머티리얼 로드.
  3. 256x256 RenderTexture(CombinedAlphaTexture) 생성.
  4. AlphaTexture를 Blit하여 CombinedAlphaTexture에 기록.
  5. RenderTexture → Texture2D로 ReadPixels 등으로 복사.

6.1.3 Billboard Mesh 생성

CombinedAlphaTexture를 입력으로 SpriteUtility.GenerateOutline로 외곽을 추출해 Billboard Mesh를 생성합니다.

6.2 GBufferTextures 베이킹

GBufferTextures 베이킹은 크게:

  1. GBufferTextures 생성
  2. 선택적 후처리

로 나눌 수 있습니다.

6.2.1 GBufferTextures 생성

AlphaTexture 생성 로직을 이해했다면, GBufferTextures도 거의 같습니다. 차이는 Viewport 설정입니다.

GBufferTextures에서는 RenderTarget에 누적하지 않고, 텍스처 위를 frame×frame의 Grid로 나눠 각 프레임을 해당 Grid 영역에 렌더링합니다(캐스케이드 그림자처럼).

아래는 나무통의 16×16 AlbedoAlpha 텍스처 예시입니다.

6.2.2 선택적 작업

프리셋/기능/설정에 따라 추가 단계가 있을 수 있습니다.

  1. 표준 베이킹에서 Depth/Albedo 등 값 보정.
  2. TGA 출력이면 채널을 BGRA로 재배치.
  3. PixelPadding > 0이면 ImpostorDilate로 가장자리 확장.
  4. 텍스처 크기 Override 시 Resize.

6.3 Impostor Billboard 생성

Billboard Mesh와 GBufferTextures를 합쳐 최종 **Impostor Billboard(GameObject)**를 만듭니다.

  1. 런타임 셰이더/머티리얼 설정 및 AmplifyImpostorAsset에 저장(프리셋 Runtime Shader가 있으면 사용, 없으면 타입별 기본 셰이더 사용).
  2. GBufferTextures를 Assets 폴더에 저장해 텍스처 리소스 생성.
  3. Impostor GameObject 생성, SharedMesh(Billboard Mesh) 및 LOD Group 설정.
  4. 런타임 머티리얼에 GBufferTextures 프로퍼티/키워드/필수 값 설정.

이렇게 하면 Impostor Billboard가 완성됩니다.

7 Runtime 렌더링

앞에서는 오프라인 베이킹 생성 과정을 봤고, 이제 런타임에서 Impostor가 어떻게 렌더링되는지 보겠습니다.

여기서는 프레임 선택과 보간에 집중하고, 포워드/디퍼드 Pass 자체는 본문 핵심이 아니므로 깊게 다루지 않습니다. 예시는 OctahedronImpostorURP.shader를 사용합니다.

버텍스 셰이더에서의 핵심은:

  • Billboard 판을 어떻게 만들고
  • 어떤 프레임을 쓰며
  • 각 프레임에 대응하는 정점 UV를 어떻게 구하느냐

입니다.

  1. CameraPosition을 월드→오브젝트 공간으로 변환해 objectCameraPosition을 구하고, 오브젝트→카메라 방향 단위 벡터 objectCameraDirection을 구합니다.
  2. 외적으로 Billboard의 로컬 상/우 방향(orthogonal vectors)을 구합니다(Billboard Local Y/X).
  3. Billboard Mesh의 Vertex의 X 성분에 우 방향 벡터를 곱하고, Y 성분도 마찬가지로 처리합니다. Billboard Mesh는 Z=0인 특성을 이용해 행렬 연산 대신 스칼라×벡터로 계산을 단순화합니다.
  4. 다음은 프레임 선택과 UV 계산입니다. camera에서 변환된 billboard vertex로 향하는 localDir을 구하고, 이를 팔면체(Octahedral) 매핑으로 2D로 투영합니다. 팔면체 매핑은 《팔면체 파라미터화 구면 매핑 방식 및 구현》을 참고하면 이해가 쉽습니다.
  5. localDir을 2D로 매핑한 OctaUV(0~1)가 어떤 Grid에 들어가는지로 baseOctaFrame을 결정합니다.
  6. baseOctaFrame으로 Grid의 시작 UV(좌하단)를 구하고, 팔면체 매핑 역연산으로 3D 방향 벡터(베이킹 카메라 니어 플레인의 법선 normal에 해당)를 구합니다.
  7. objectCameraPosition에서 localDir 방향으로 레이를 쏴, normal을 가진 평면과의 교차점 p를 구합니다.
  8. normal과 직교하는 TBN 로컬 좌표계를 구성합니다.
  9. p를 TBN 기준으로 분해해 frameX/frameZ를 얻고, uv = -(frameX, frameZ)를 얻습니다(왜 음수인지는 원문 그대로 두었습니다). localNormal도 계산합니다.
  10. uv에 scale/offset 적용.
  11. Octahedron impostor는 3프레임을 블렌딩하므로, baseOctaFrame의 인접 2프레임도 찾아 같은 계산을 수행해 2개의 추가 uv를 얻습니다.
  12. 최종적으로 3개의 프레임에 대한 3세트 uv를 얻습니다.

프래그먼트 셰이더는 3프레임을 샘플링해 가중치로 보간하고, 나머지는 일반적인 디퍼드 패스 처리입니다.

8 【번외】Amplify Impostors 데모 분석

이제 핵심 로직은 다 봤고, 여기부터는 제가 데모를 보면서 정리한 번외 분석입니다. 관심 없으면 건너뛰어도 됩니다.

제가 커스텀 URP에서 데모 임포트가 실패했으니, 공식 데모의 URP Asset/Renderer가 어떤 특성을 가지는지 먼저 살펴보죠.

데모에는 3가지 품질의 URP Asset이 있습니다: High Fidelity, Balanced, Performant. 세 Asset 모두 같은 AI Universal Render Pipeline Asset Renderer를 사용합니다.

여기서는 Balanced를 보겠습니다(루쉰이 말했죠. 중국인의 성정은 조화와 절충을 좋아한다고…).

먼저 AI Universal Render Pipeline Asset Balanced(AI는 Amplify Impostors 약자겠죠).

음… 딱히 특징이 없는 URP Asset입니다(URP Asset 자체가 크게 장난칠 수 있는 요소가 적긴 합니다). 한 가지 눈에 띄는 건 현재 Renderer가 OpenGLES3를 지원하지 않는다는 점인데, 모바일 프로젝트라면 꽤 치명적일 수 있습니다. 많은 기기가 OpenGLES3를 쓰니까요(다들 Vulkan 써라!). 왜 OpenGLES3를 지원하지 않는지는 나중에 원인을 찾아봐야겠네요. 일단은 “파묻힌 떡밥”으로 두겠습니다.

이 URP Asset에서는 Depth TextureOpaque Texture 기능이 켜져 있습니다. Depth Texture는 Opaque Pass 후(여기서는 After Opaques) 카메라 깊이를 _CameraDepthTexture로 Copy하고, Opaque Texture는 Opaque Pass 후 ColorAttachment를 다운샘플링해 _CameraOpaqueTexture로 만듭니다.

URP Asset은 이 정도면 됐고, 다음으로 AI Universal Render Pipeline Renderer를 봅니다.

여기서는 Rendering Path가 Deferred로, 디퍼드 렌더링을 사용합니다. Depth Texture Mode는 After Opaques로, Opaque Pass 후 Depth를 Copy합니다.

이제 데모 씬 하나를 보죠. Barrels & Boulders(나무통과 바위)입니다.

씬은 단순합니다. 나무통 5개, 바위 3개. 나무통 하나로 분석해 봅시다.

나무통 GameObject에는 LOD Group이 붙어 있습니다. **LOD0은 일반 3D 모델(삼각형 2106개)**이고, **LOD1은 Impostor(삼각형 6개)**입니다. 즉 카메라가 멀어지면 Impostor로 전환됩니다.

이게 Impostor를 가장 “정석적으로” 쓰는 방식이죠. 근거리에는 3D 모델, 원거리에는 Impostor.

공식 매뉴얼에서도 “The common purpose … represent far distant objects … trees, bushes, rocks …”라고 말하듯, Impostor는 원거리 오브젝트를 매우 낮은 폴리로 표현하기 위해 쓰는 기술입니다. 근거리에서도 그럭저럭 괜찮아 보일 때가 있지만, 시점 회전 등의 상황에서는 들통나는 경우가 있고 전환 아티팩트도 생기므로, 근거리까지 Impostor를 쓸지는 프로젝트 요구사항에 따라 결정해야 합니다.

9 마무리

자, 여기까지가 제가 정리한 Impostor에 대한 전부입니다. Impostor의 개념부터, Amplify Impostors의 구현 디테일까지 훑어봤습니다. 충분히 이해하고 나면, 직접 Impostor를 구현하든, Amplify Impostors를 기반으로 커스터마이징하든, 훨씬 덜 두렵게 느껴질 겁니다.

Impostors의 실제 성능이 어느 정도인지는 아직 깊게 보지 않았습니다. 저도 아직 학습 중이기도 하고, 씬 구성이나 Impostor 사용 방식에 따라 성능 편차가 커서 일반화하기가 어렵기 때문입니다. 글은 여기서 마무리하고, 이후 Impostors에 대해 새로운 이해가 생기면 계속 업데이트해 보겠습니다.

참고

  1. https://assetstore.unity.com/packages/tools/utilities/amplify-impostors-119877#description
  2. https://wiki.amplify.pt/index.php?title=Unity_Products:Amplify_Impostors/Manual
  3. https://en.wikipedia.org/wiki/Geodesic_polyhedron
  4. https://zhuanlan.zhihu.com/p/408898601
  5. https://zhuanlan.zhihu.com/p/667993188
  6. https://docs.unrealengine.com/4.26/en-US/RenderingAndGraphics/RenderToTextureTools/3/