유니티 그래픽스 최적화 스타트업 (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
- 아직 없음 (책 기준.)
- 유니티 공식 문서에서 티어표 확인 가능
- Tier 1
- Use Graphics Settings = 다른 렌더링 파이프라인 사용
- 디퍼드 렌더링(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이 적당.
- Lightmap Resolution : 텍셀 밀도.
- Lightmapping Settings
- 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 : Real-time + Baked 역할을 지님.
- 라이트 프로브
- 라이트맵은 캐릭터가 어느 씬에 있든지 동일한 라이팅이 적용됨
- 캐릭터가 건물 안에 들어가면 어두워져야 함.
- 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에 0
255 범위의 정수를 01 범위의 실수로 변환. - HDR은 0~1 범위를 넘는 실수형 float. 높은 용량이 필요해짐.
- 유니티에서는 용량 절약을 위한 트릭을 사용하여 용량이 늘어나진 않지만 마하 밴드(부자연스러운 경계)가 생길 수 있음
- HDR이 아닌 2D 텍스쳐는 R,G,B,A에 0