yhc509

유니티 그래픽스 최적화 스타트업 (3) - Lighting, Shadow, GI

·24 min read

Lighting

  • 포워드 렌더링(Forward Rendering)과 디퍼드 렌더링(Deferred Rendering)
    • 라이팅을 처리하는 방식에 따라 나뉨
    • 카메라 컴포넌트에서 설정 가능
      • Use Graphics Settings = 다른 렌더링 파이프라인 사용
        • Edit → Project Settings → Graphics의 Tier Setings에서 플랫폼, 티어별 설정 가능
        • 디바이스 성능 등급에 따라 렌더링 설정
          • 낮은 등급에서 포워드 / 높은 등급에서 디퍼드 같은 식으로…
        • 모바일에선 유니티가 자동으로 등급을 감지하여 설정해줌.
          • Tier 1
            • OpenGL ES3를 지원하지 않는 안드로이드 단말
            • 아이폰5, 아이팟5세대, 아이패드 4세대, 아이패드 미니 1세대 이전 디바이스
          • Tier 2
            • OpenGL ES3 이상을 지원하는 안드로이드 단말
            • 아이폰5S, 아이팟6세대, 아이패드 에어, 아이패드 미니 2세대 이후 디바이스
          • Tier 3
            • 아직 없음 (책 기준.)
          • 유니티 공식 문서에서 티어표 확인 가능
  • 디퍼드 렌더링(Deferred Rendering)
    • 많은 수의 실시간 라이팅(동적 라이팅)을 좋은 성능으로 처리 가능.
    • PC, 콘솔 게임에서 많이 쓰임. 기기 성능이 받쳐줘야 함
      • 여러 개의 버퍼에 한번에 렌더링을 하는 멀티 렌더 타겟(Multi Render Target)을 지원해야 함.
      • G버퍼에 Diffuse, Normals, Specular, Smoothness, Depth 기록 → 라이팅 처리하여 최종 렌더링 결과를 얻음
        • G버퍼에 오브젝트를 렌더링 하는 지오매트리 패스 / 라이팅을 처리하는 라이팅 패스로 나뉘어 있음
        • 화면에 보이는 픽셀만 셰이딩 처리를 할 수 있음 → 불필요한 라이팅 연산 제거
        • 유니티에서 공개하는 대부분의 데모들이 디퍼드 렌더링에 해당
        • 모바일에선 한계가 있음
          • G버퍼를 처리하기 위해선 메모리가 뒷받침 되어야 한다.
          • 멀티 렌더 타겟 지원 여부가 디바이스마다 다름.
  • 포워드 렌더링(Forward Rendering)
    • 아직 모바일에선 포워드 렌더링을 많이 씀
    • 동적 라이팅이 많거나, 동적 라이팅에 영향을 받는 오브젝트가 많으면 성능 부하 발생
    • 전통적인 오브젝트 렌더링 기법
    • Light 컴포넌트
      • Baked : 미리 연산 저장된 라이트. 라이트맵이나 라이트 프로브로 저장함. 런타임 성능을 절약할 수 있으며 움직이는 물체는 적용 불가.
      • Realtime : 실시간으로 연산. 실시간으로 변경되는 라이트는 리얼타임으로 지정해야 함. 성능에 영향을 많이 줌.
      • Mixed : 둘을 섞은 것. 움직이는 물체에 적용이 가능하면서 미리 라이트 맵 등을 베이크 함.
  • 리얼타임 라이트(포워드 렌더링)와 드로우콜
    • 라이트 개수만큼 드로우콜을 차지함
    • GPU가 감당해야 하는 폴리곤도 늘어남
      • 770 폴리곤의 구체 1개를 5개의 리얼타임 라이트로 렌더한다면 770 * 5 = 3850개의 폴리곤이 필요
    • Light 컴포넌트의 Render Mode
      • Important : 항상 픽셀당 연산. 스페큘러 및 노멀맵 대응이 가능하다는 뜻. 성능 부담.
      • Not Important : 버텍스 당 연산하거나, 스피리컬 하모닉스(SH)를 활용. 품질이 떨어지고 스페큘러에 대응할 수 없음
      • 하나의 라이트만 Important, 나머지는 Not Important 로 설정하면 한번의 드로우콜로 처리할 수 있음
      • Auto : 런타임에서 라이트의 중요도를 자동으로 설정함.
        • Edit → Project Settings → Quality 의 Pixel Light Count
        • 픽셀당 처리될 수 있는 라이트의 개수. Auto로 설정된 라이트만 해당.
        • 기본적으로 메인 디렉셔널 라이트 1개 + Pixel Light Count 4개라면 사실상 픽셀당 처리 가능한 라이트는 5개.
          • Important로 설정된 라이트가 있다면 5개에 추가로 붙게 됨.
      • Important / Not Important를 지정하는 방법보다는 Auto로 하고 Pixel Light Count 를 조절하는 것이 편함.
    • 디렉셔널 라이트는 범위 제한이 없으나, 스팟 라이트와 포인트 라이트는 범위 제한이 있어서, 오브젝트가 영향을 받는 범위 밖에 있으면 연산되지 않음
      • 영향을 받는지는 바운딩 박스로 결정됨 (오브젝트나 메시의 경계 범위. 실제 폴리곤 범위와 다를 수 있음)
  • 내장 셰이더
    • 유니티 셰이더는 버텍스 셰이더 + 프래그먼트 셰이더
    • 스탠다드 셰이더
      • 물리 기반 렌더링(Physically Based Rendering), 실시간GI(Global Illumination) 지원
      • 물리 법칙 기반의 사실적인 표현 = 복잡한 연산
      • 모바일에는 적합하지 않음
      • Edit → Project Settings → Graphics에서 퀄리티 조절 가능.
      • Rendering Mode
        • Opaque : 불투명 오브젝트
        • Transparent : 알파 블렌딩을 적용하여 투명 오브젝트 처리. 실제로 투명하지 않더라도 투명 취급.
        • Cutout : 알파컷아웃 또는 알파테스트로 투명을 처리. 투명은 있지만, 반투명은 없는 경우 (잔디, 철망 같은 것). Opaque처럼 앞→뒤로 렌더함
        • PC, 콘솔에선 Cutout > Transparent
        • 모바일에선 Cutout < Transparent
          • 모바일에서는 대부분 컷아웃에서 사용하는 분기(if문)의 성능 저하가 발생함
          • 모바일 그래픽 칩셋이 주로 사용하는 타일 기반 렌더링(Tile Based Rendering : TBR), 타일 기반 지연 렌더링(Tile Based Deferred Rendering : TBDR)에서는 알파테스트 좋은 성능을 얻기 힘듬
            • 모바일에선 전력 소모와 물리적 크기를 고려하여 적은 대역폭으로 대응하기 위해 TBR, TBDR을 사용함.
            • 프레임 버퍼를 일정 크기 타일로 영역을 나눔
            • 버텍스셰이더 → 타일 선택 → 픽셀 셰이더
            • 알파테스트를 사용하면 버텍스 처리 단계에서는 폴리곤의 차폐 여부를 알 수 없음
      • 씬 일부에만 스탠다드 셰이더 적용하면 좋음
        • 캐릭터 = 스탠다드 셰이더 / 배경 = 가벼운 셰이더 → Mobile/Unlit (Supports Lightmap)
        • 씬 전체에 스탠다드 셰이더를 적용하더라도, 배경에 라이트 맵이나 라이트 프로브를 적용하면 성능 절약 가능. (라이트 맵이 적용된 오브젝트는 라이팅 연산 비용이 감소함)
        • 모바일에서 Mixed Lighting 모드를 Subtractive로 두면 무리 없이 사용 가능하기도 함.
    • 모바일 셰이더
      • 모바일에서 사용 가능하도록 부담스러운 연산을 제거한 셰이더
      • 요즘 단말 성능이 좋아서 스탠다드를 사용해도 상관 없으나 신경을 쓰긴 해야 함
      • Diffuse, Vertex Lit → 버텍스 당 처리되는 퍼 버텍스 라이팅(Per Vertex Lighting) 셰이더
      • Diffuse Bumped, Bumped Specular → 픽셀 당 처리되는 퍼 픽셀 라이팅(Per Pixel Lighting) 셰이더
      • 포워드 렌더링에서는 퍼 버텍스 라이팅과 퍼 픽셀 라이팅의 성능 차이가 큼
        • 노말맵 → 퍼 픽셀 라이팅
        • 중요도가 낮은 것 → 퍼 버텍스 라이팅
      • 모바일에선 알파테스트 성능이 좋지 않기 때문에 내장 모바일셰이더엔 알파테스트 셰이더가 없음
        • 알파블렌딩을 사용하도록
    • 이미지 기반 라이팅
      • 텍스쳐에 라이팅 결과를 미리 넣어놓고, 라이팅 대신 텍스쳐를 사용하는 기법
      • 라이팅 처리를 안해도 되므로 성능 향상 가능 → Unlit Lighting
      • ToonRamp
        • 빛을 바라보는 표면의 컬러 ~ 빛을 등지는 표면의 컬러를 미리 정의해둔 텍스쳐를 사용
        • 카툰 렌더링에서 사용했었던 기법이지만 실사풍에서도 활용 가능
          • ToonRamp , ToonShading 등으로 불림
      • MatCap
        • Material Capture = 현실 세계의 라이팅을 수집해서 캡쳐하기 위한 구체
        • Material Capture를 그대로 텍스쳐로 만들어서 라이팅 결과에 사용
          • ToonRamp와 비슷한 부분이 있음
        • 면의 색과 밝기가 카메라 각도에 따라 달라진다
          • 시점이 고정된 게임 (탑뷰, 쿼터뷰, 백뷰) 등에서 활용하는 것이 어색하지 않음

Shadow

  • 실시간 그림자
    • 쉐도우 맵 기법을 사용함
    • 그림자의 깊이를 저장하는 버퍼를 만든 후 픽셀 셰이더에서 깊이를 비교하는 과정을 거침
    • 넓은 영역을 커버하기 위해 여러 구역으로 나누거나 (cascade), 계단 현상을 없애기 위해 여러번 샘플링하여 필터 처리를 함
    • 비용이 큼
    • Depth Texture
      • 카메라 기준으로 픽셀까지의 깊이를 렌더링
      • 가까울 수록 검은색. 멀 수록 흰색
      • 그림자에 영향을 받는 모든 오브젝트를 렌더링 해야 함
    • 쉐도우 맵
      • 그림자 처리를 위한 별도의 버퍼
      • 그림자를 캐스팅하는 오브젝트를 렌더링 해야 함
    • 뎁스 텍스쳐와 쉐도우 맵 정보를 비교하여 그림자 영역을 계산함
      • 뎁스 텍스쳐와 쉐도우 맵 렌더링 과정에서 드로우콜이 발생함
      • 그림자 결과에 필요한 연산, 분기 처리로 인해 픽셀 셰이더 성능이 소모됨
      • 필요한 오브젝트만 그림자를 활성화 시키는 것이 중요
      • Mesh Renderer - Cast Shadows 옵션
        • 오브젝트의 그림자가 다른 오브젝트에 그림자가 드리워지는지
        • Shadows Only = 최종 화면에는 렌더링되지 않지만 쉐도우맵에는 반영됨
          • 오브젝트는 고품질, 그림자를 만들어내는 오브젝트는 저품질로 사용하여 성능 절약
      • Mesh Renderer - Receive Shadows 옵션
        • 오브젝트에 다른 오브젝트의 그림자를 받게 되는지
        • Shadow Type
          • Hard Shadow = 경계가 딱딱하게 끊겨보임
          • Soft Shadow = 경계가 부드러움. 픽셀 셰이더 부담이 늘어남
        • Resolution = 쉐도우 맵의 해상도
        • Shadow Distance
          • Edit → Project Settings → Quality → Shadow Distance
          • 카메라에서 그림자가 그려지는 거리.
          • 값이 낮을 수록 멀리 있는 그림자는 맺히지 않음
          • 대신 쉐도우 맵에 렌더링해야 하는 범위가 줄어들기 때문에 품질이 좋아짐
    • Shadow Distance를 낮게 잡고 라이트 맵을 활용할 수 있음. 단 동적인 오브젝트 그림자 표시가 불가
    • 멀리 떨어진 오브젝트 그림자 처리가 필요하면 Cascaded Shadow Maps 기법 활용 가능
      • 거리에 따라 쉐도우 맵을 여러 개 만들어서 사용하는 기법
      • Parallel Split Shadow Maps라고 부르기도 함
      • 가까운 그림자 해상도가 깨져보이는 현상을 줄일 수 있음
      • 드로우 콜을 추가로 먹음. 픽셀 셰이더 부담도 늘어남
      • Edit → Project Settings → Graphics
  • 평면 그림자
    • 텍스쳐로 그림자 형태를 보여줌
    • 지형이 평면이 아니면 어색함
    • 메시 평면 그림자
      • 버텍스 셰이더를 이용해서 메시가 바닥에 평면으로 투영되도록 조작
      • 그림자용 오브젝트를 하나 더 생성. 그림자용 머터리얼로 변경
      • 셀프 쉐도우 처리가 안됨

GI

  • 빛은 표면에 부딪히면 산란되거나 튕겨서 또 다른 표면에 간적접인 영향을 주는 작업을 반복함.
  • 라이트맵과 라이트 프로브로 전역 조명(GI)을 미리 계산할 수 있음
  • 실시간 라이팅은 GI를 계산하지 못함. 실시간 라이팅은 지역 조명(Local Illumination)임.
  • 라이트맵
    • GI 및 그림자를 포함한 라이팅 정보를 미리 연산하여 텍스쳐로 저장하는 기능
    • 라이트맵 텍스쳐에는 라이팅 관련 정보만 담긴다
      • Albedo 같은 오브젝트 고유 색상은 담기지 않음
      • 오브젝트에 빛이 튕겨서 바닥에 튕기는 GI 값이 저장됨
    • 라이트맵이 구워지고 나면 런타임 동안 라이트맵에 기록된 라이트는 변경되지 않는다
      • 배경같은 정적인 오브젝트만 가능 (Static 오브젝트)
      • 디렉셔널 라이트의 Light 컴포넌트에서 Mixed 또는 Baked Mode로 하면 라이트맵 사용
      • 오브젝트에 라이트맵을 적용하려면 Mesh Renderer의 Lightmap Static 플래그 체크
        • 라이트맵을 메시에 적용하려면 별도의 UV채널이 필요
        • UV채널이 없는 메시라면 Model → Generate → Lightmap UVs 체크하여 유니티가 채널 생성하도록 만든다
      • Window → Rendering → Lighting Settings 에서 라이트맵 관련 설정 가능
    • 실시간 GI
      • 실시간 라이트의 GI를 처리해주는 기능이지만 제약이 많음
      • 비용이 크기 때문에 즉각 반영되지 않고 점진적으로 연산되기 때문에, 빠르게 바뀌는 라이트에는 부적합 (태양처럼 서서히 바뀌는 것에 사용)
      • 모바일에서는 감당하기 힘듬
      • Edit → Project Settings → Graphics → Realtime Global Illumination CPU 에서 CPU 사용량 조절
    • 라이트맵 해상도
      • Lightmapping Settings
        • Lightmap Resolution : 텍셀 밀도.
          • Lightmap Resolution이 10, 5x5유닛크기 평면 → 50x50텍셀 = 2500텍셀
        • Lightmap Padding
        • Lightmap Size
          • 라이트맵 텍스쳐 해상도. 총 해상도가 지정한 수치보다 크면 여러 장의 라이트맵으로 나뉜다
          • 여러 장으로 나뉘면 배칭이 깨질 확률이 커진다. 그러므로 드로우콜에 영향이 간다.
          • 라이트맵의 사이즈가 너무 크면 메모리 문제가 발생할 수 있다. 2048이 적당.
    • Directional Mode
      • Directional : 노멀맵 적용. 추가적인 라이트맵을 생성함. 픽셀 셰이더 비용이 추가됨.
      • Non-Directional : 노멀맵 미적용. 라이트맵만 사용함. 리플렉션 프로브는 적용됨.
      • 라이팅-Global Maps 탭에서 라이트맵 결과물 확인 가능
    • Light Mode
      • Mixed : Real-time + Baked 역할을 지님.
        • 라이트맵에도 그림자가 반영되고 런타임 동적 오브젝트에도 그림자 캐스팅이 이루어짐
        • 런타임동안 트랜스폼과 색상 변경이 가능하지만 라이트맵에는 적용되지 않음
        • 씬의 모든 Mixed Light는 Mixed Lighting Mode에 의존함.
          • Window - Rendering - Lighting Settings - Mixed Lighting
          • Baked Global illumination = 라이트맵 사용 여부
          • Light Mode는 Baked Indirect, Subtractive, Shadowmask 옵션이 있음.
        • Baked Indirect
          • GI만 라이트맵에 기록. 그림자는 실시간.
        • Subtractive
          • 전통적인 방식
          • GI, 직접조명, 그림자 모두 라이트맵에 기록.
          • 그림자끼리 겹쳐도 자연스럽지는 않음.
          • 저사양에 적합
        • Shadowmask
          • 그림자 영역을 별도의 텍스쳐로 저장.
          • 그림자끼리 자연스럽게 연결됨.
          • 픽셀 셰이더에서 추가 연산 필요.
          • Baked Indirect에 비해서는 상대적으로 절약.
          • static 오브젝트에 대해서만 shadowmask로 저장. dynamic만 셰도우맵 연산하여 절약.
          • Subtractive 보다는 성능이 필요하지만 괜찮은 품질.
          • shadowmask 텍스쳐에 대한 추가 메모리 필요
          • Edit - Project Settings - Quality - Shadows 에서 shadowmask 설정
            • Distance shadowmask : 근거리는 실시간. 원거리는 shadowmask 사용.
  • 라이트 프로브
    • 라이트맵은 캐릭터가 어느 씬에 있든지 동일한 라이팅이 적용됨
      • 캐릭터가 건물 안에 들어가면 어두워져야 함.
      • Mixed Light 모드를 Baked Indirect로 두면 그림자 연산이 실시간으로 되어 어두워질 수 있으나 성능 문제가 있음
    • 라이트 프로브는 라이트맵처럼 빛을 저장해두었다가 다이나믹 라이트에게 라이트를 반영해줄 수 있는 기능.
      • 라이트맵 : 표면에 맺히는 라이팅 정보를 저장
      • 라이트 프로브 : 라이팅이 지나가는 빈 공간의 지점을 저장
    • 씬에 별도로 설치해주어야 함 : Component - Rendering - Light probe group
    • 라이트 프로브는 단순한 몇개의 숫자 값 데이터
      • 구면 조화 함수를 사용함. 구면을 기준으로 주변 공간의 에너지 분포를 근사하여 표현하는 함수
      • 차수를 반복하여 높은 정밀도 표현이 가능함
      • 게임에서는 일반적으로 많아봤자 3단계.
      • 그래서 정밀도가 높지 않음. 보조용 수단일 뿐임.
  • 리플렉션 프로브
    • 반사 이미지는 기본적으로 스카이 박스 : Environment - Reflections source
    • 리플렉션 프로브는 자연스러운 반사 이미지를 생성함.
    • 오브젝트가 리플렉션 프로브 영역에 들어오면 반사 이미지를 저장된 이미지로 대체.
    • 리플렉션 프로브는 큐브맵 형태로 저장됨
      • Cubemap capture settings - Resolution에서 텍스처 해상도 변경
      • 큐브맵은 6장의 텍스쳐이므로 *6
    • HDR 플래그를 켜면 리플렉션 프로브에 밝은 빛을 반영할 수 있음
      • HDR이 아닌 2D 텍스쳐는 R,G,B,A에 0255 범위의 정수를 01 범위의 실수로 변환.
      • HDR은 0~1 범위를 넘는 실수형 float. 높은 용량이 필요해짐.
      • 유니티에서는 용량 절약을 위한 트릭을 사용하여 용량이 늘어나진 않지만 마하 밴드(부자연스러운 경계)가 생길 수 있음

참고 자료