버텍스 트랜스폼 (Vertex Transform)


버텍스 데이터들은 GPU 메모리에 저장되어 있습니다.렌더링을 수행해야하는 시점이 되면 GPU는 메모리로부터 버텍스 정보들을 가져옵니다. 그리고 적절한 위치에 그려주기 위해서 위치 변환 즉 트랜스폼(transform)을 수행해주게 됩니다. (명사형으로 트랜스포메이션(Transformation)이라고 표현하기도 합니다.)


저장되어 있는 버텍스 데이터는 특정 위치가 반영된 데이터가 아닌 메시 형태만 반영되있는 데이터입니다. 하나의 메시가 여러 오브젝트 즉 인스턴스에서 사용되고 인스턴스마다 위치가 다릅니다. 또한 매번 위치가 바뀌기 때문에 원본 메시 데이터에 3D 공간상의 위치를 반영해놓을 수는 없고, 3D 공간의 특정 위치가 반영되지 않는 원점 기준의 메시 모델에 대한 위치 데이터만 가지고 있는 것입니다. 이러한 데이터를 로컬 스페이스(local space)에 존재한다고 표현합니다.





이를 3D 공간상의 특정 위치에 위치시켜줘야 합니다. 만일 오브젝트의 인스펙터에서 트랜스폼 컴포넌트의 position이 -4.5,0.7,3 으로 설정되어 있고 rotation y가 55으로 되어있다면 메시 데이터를 -4.5,0.7,3으로 위치시켜주고 y축으로 55도 회전시켜줘야합니다.

이러한 식으로 월드 스페이스(world space) 혹은 월드 좌표계(world coordinates)로 변환시켜주는 것을 월드 트랜스폼(world transform)이라고 표현합니다.

이 월드 트랜스폼 시에는 메시의 모든 버텍스의 위치와 노멀(Normal)(경우에 따라서 탄젠트(Tangent) 및 바이노멀(Binormal) 데이터 포함)을 월드 트랜스폼 시켜줍니다. 즉, 모든 버텍스의 위치와 노멀 정보가 로컬 스페이스에서 월드 스페이스로 변환되는 과정이 월드 트랜스폼인 것입니다.




월드 스페이스로 변환을 해준 뒤에는 카메라 스페이스(Camera Space)로 또 변환을 해줘야 합니다. 월드 트랜스폼을 거쳐서 모든 오브젝트의 버텍스들이 원래 있어야 할 위치로 위치하게 되었지만 , 이 오브젝트들은 카메라를 통해서 디스플레이가 됩니다. 카메라는 원점(x:0,y:0,z:0)에만 있는 것이 아니고 카메라 역시 고유의 위치와 방향을 가지고 있습니다. 그렇기 때문에 오브젝트의 버텍스들을 카메라에 상대적인 위치로 변환을 해줘야 합니다. 이러한 공간을 카메라 스페이스(Camera Space) 또는 뷰 스페이스(view space)라고 부르고, 뷰 스페이스로 변환하는 과정을 뷰 트랜스폼(view transform)이라고 합니다.




오브젝트들은 3차원 공간에 있지만 최종적으로 오브젝트가 렌더링되어 디스플레이 되는 공간은 2차원입니다. 3D 공간을 2D상의 위치로 매칭시켜주는 과정을 프로젝션(투영,projection)이라고 합니다. (빔프로젝터에서 빛을 쏘아서 스크린에 투영시키는 모습을 연상하면 됩니다.)


이러한 프로젝션 트랜스폼 과정에서는 원근법도 적용이 됩니다. 사람이 세상을 볼 때 멀리 있는 것은 작게 보이고 가까이 있는 것은 크게 보입니다. 또한 거리가 멀어질 수록 많은 사물들이 시야에 들어오게 됩니다. 카메라를 통해서 볼때도 이 역시 마찬가지며 유니티 카메라에서는 뷰 영역을 나타내는 프라미드 볼륨을 확인할 수 있습니다. 이를 뷰 프러스텀(view frustum)이라고 합니다.


이러한 투영을 퍼스펙티브 프로젝션(원근 투영, perspective projection)이라고 부릅니다.

cam_pers.png


또한, 원근법이 적용 된 퍼스펙티브 프로젝션 외에도 원근법이 제거 된 오쏘그래픽 프로젝션(orthographic projection, 직교 투영)도 존재합니다. 카메라와의 거리와는 무관하게 시각적으로 크기의 변화가 없이 보여집니다.

cam_ortho.png


카메라 투영에 관한 설정은 다음 비디오에서 자세하게 확인할 수 있습니다 : https://youtu.be/52g_3CX7avo?t=4m2s



유니티의 쉐이더를 작성할 시 UNITY_MATRIX_MVP 변수는 이러한 변환을 의미합니다. UnityShaderVariables.cginc 파일에 기술되어 있습니다.


더 많은 내용은 "유니티 그래픽스 최적화 스타트업"을 참고하세요








Posted by 오지현 / Unity Korea Lead Evangelis ozlael

댓글을 달아 주세요


ASTC(Adaptive Scalable Texture Compression)


ASTC(Adaptive Scalable Texture Compression)는 ARM에서 개발하고 2012년에 크로노스 그룹에서 OpenGL ES에 표준으로 지정되었습니다. ASTC는 PVRTC나 ETC와 마찬가지로 손실 블록 기반 텍스처 압축 알고리즘입니다.


ASTC는 용량과 품질에 대한 트레이드오프(Trade-off)가 가능합니다. ETC나 PVRTC는 bpp가 한정되어 있는 반면,  ASTC는 8bpp에서 0.86bpp까지 품질을 향상시킬 수 있습니다. 이러한 조절이 가능한 것은 가변 블록 크기를 사용하기 때문입니다. ETC와 PVRTC는 압축되는 블럭의 크기가 4x4로 고정되어 있습니다. 반면, ASTC는 압축되는 기준 블럭의 크기를 4x4에서 12x12 블럭으로 다양하게 선택을 할 수 있습니다. ASTC는 블럭 당 128비트를 사용합니다. 128비트를 4x4블럭에 할당하면 128/16=8bpp가 되는 것이고, 12x12 블럭에 할당하면 128/144=약0.89bpp가 되는 것입니다.

아래 이미지는 좌측으로부터 각각 8bpp, 3.56bpp, 2bpp의 결과를 보여주고 있습니다.

<이미지 출처 : 위키피디아 >


유니티에서도 텍스쳐 압축 포맷을 ASTC로 선택할 수 있고, 4x4에서 12x12까지 블록 크기를 선택할 수 있습니다.


텍스처의 중요도나 복잡도에 따라서 블럭의 크기를 개별적으로 선택할 수 있습니다. 예를 들어거 캐릭터의  텍스쳐같은 경우는 4x4를 선택하고,


파티클같이 이미지가 많이 뭉개져도 상관 없는 경우는 12x12를 선택함으로써 용량을 절약할 수 있습니다.


ASTC는 ETC보다 더 복잡한 인코딩 과정을 거칩니다. 그러다보니 압축 시간이 조금 더 걸리지만 결과적으로는 ETC보다는 ASTC가 일반적인 품질이 좋습니다.

< 이미지 출처 : developer.arm.com>


ASTC는 안드로이드폰과 iOS폰 모두에서 지원하기 때문에 같은 텍스처 압축 포맷을 사용함으로써  퀄리티와 용량에 대한 유지 보수를 한결 더 수월하게 할 수 있습니다.


물론, 문제는 지원 기기의 범위입니다.


안드로이드의 경우는 OpenGL 3.2 이상 혹은 OpenGL ES 3.1 + AEP(Android Extension Pack)이 지원되어야합니다. 삼성 갤럭시 S6 혹은 LG G5 이상의 기기들이 이에 해당합니다. 안드로이드의 경우에는 기종이 정말 다양하게 존재하기 때문에 반드시 주 타겟 기기에서 지원 여부를 직접 테스트해보아야합니다.


iOS는 A8 processor를 사용하기 시작하는 기종부터 사용이 가능합니다. iPhone 6, iPad mini 4가 이에 해당합니다.


따라서, 그 이하의 구형 기종도 지원해야하는 게임이라면 ASTC를 사용하기가 애매할 수도 있습니다. 하지만, 그렇지 않은 경우에는 ASTC를 사용하는 것이 품질과 용량을 모두 절약할 수 있는 좋은 방법이 될 수도 있습니다. 그러한 이유로 최근에 출시하는 많은 3D 게임에서 ASTC를 사용하기 시작하고 있습니다.


관련 참고 링크 :

https://developer.nvidia.com/astc-texture-compression-for-game-assets



더 많은 내용은 "유니티 그래픽스 최적화 스타트업"을 참고하세요

Posted by 오지현 / Unity Korea Lead Evangelis ozlael

댓글을 달아 주세요


SRP(Scriptable render pipeline)

유니티 2018부터 스크립터블 렌더 파이프라인(Scriptable render pipeline, 이하 SRP)이라는 기능을 제공합니다. SRP는 유니티의 렌더링 처리 방식을 C# 스크립트로 제어할 수 있는 기능입니다. 이를 통해서 라이팅 처리 방식을 포함한 전반적인 렌더링 루프를 직접 구성할 수 있습니다. 그렇게 함으로써 기존에 유니티에서 고정해놓은 방식대로만 렌더링하는 것이 아니라, 직접 렌더링 루프를 커스터마이징함으로써 원하는 방식으로 렌더링을 구성할 수 있습니다. 예를 들어서, 새로운 렌더링 기술을 직접 적용할 수도 있고, 특정 하드웨어에 맞게 최적화를 할 수도 있습니다.

유니티의 로우 레벨 렌더링 파트는 유니티가 C++로 구현해놓았습니다. 그리고, 이러한 기능들을 C# 스크립트로 사용하고 제어할 수 있도록 되어 있습니다. 유니티를 사용하는 개발자는 C# 스크립트로 자신만의 렌더 루프를 만들 수 있습니다. 하지만, SRP로 렌더 파이프라인을 직접 구성한다는 것은 많은 지식이 요구되고 많은 작업이 필요합니다. 대신, 유니티는 유니티에서는 SRP를 이용하여 HDRP(HD Render Pipeline)와 LWRP(Lightweight Render Pipeline)을 만들어두어 제공하고 있습니다. SRP로 직접 렌더 파이프라인을 구성하지 않고 HDRP나 LWRP를 이용하여 프로젝트를 진행할 수 있습니다.


HDRP(High Definition render pipeline)

HDRP는 하이엔드 PC나 콘솔등을 고려해서 고품질의 비주얼을 구현하는데 적합한 파이프라인입니다. 유니티의 빌트인에 구현되어있는 디퍼드나 포워드보다 발전되고 개선된 렌더링 기법이 적용되어있습니다. (주석 : Clustered tiled lighting, Fine pruned tiled lighting) 또한, Anisotropy, Subsurface 등 더 많은 재질 표현이 가능합니다.

하지만, HDRP는 기본적으로 요구되는 하드웨어 스펙들이 존재하기 때문에 성능이 낮은 플랫폼에서는 작동하지 않습니다. 즉, 모바일 게임 개발용으로는 적합하지가 않습니다.


LWRP(Lightweight Render Pipeline)

대신, 모바일을 위해서 LWRP라는 파이프라인이 제공됩니다. LWRP는 퍼포먼스에 최적화되어 있고, 모바일이나 VR이나 AR처럼 성능에 민감한 환경에서 높은 성능을 보여줍니다.

LWRP는 기존 파이프라인에 비하여 성능을 위한 특징들이 존재합니다. 이를 통해서 라이팅과 쉐이딩 관련하여 단점들을 해결하여 성능 제약이있는 플랫폼에서 최적화 된 실시간 렌더링 퍼포먼스를 제공합니다.


Multi light Single pass

그 중 가장 큰 특징은 실시간 라이팅들이 싱글패스로 처리된다는 점입니다. 앞서 설명하였듯이 포워드 렌더링에서는 실시간 라이트를 여러개를 처리하기 위해서는 그만큼 덧그리게 됨으로써 드로우콜이 늘어납니다. (주석:이를 멀티 라이트 멀티 패스(Multi light Multi pass)라고 표현됩니다.)

LWRP는 유니티에 기본으로 구현된 포워드 렌더링과는 달리, 여러개의 실시간 라이트를 한번의 패스로 처리하도록 만들어져 있습니다. (주석 : 즉, 멀티 라이트 싱글 패스(Multi light Single pass)입니다. ) 오브젝트를 렌더링 할 때 오브젝트에 영향받는 실시간 라이트들을 쉐이더에 전달해줍니다. 쉐이더에서는 한번에 여러 라이트들을 모두 처리해줌으로써 별도의 드로우콜이 발생하는 것을 방지합니다. 이러한 과정을 통해서 기존의 포워드 렌더링과 비교하여 드로우콜이 줄어들게 설계되어있습니다.


관련 참고 자료 링크 : 
https://blogs.unity3d.com/2018/02/21/the-lightweight-render-pipeline-optimizing-real-time-performance/


더 많은 내용은 "유니티 그래픽스 최적화 스타트업"을 참고하세요

Posted by 오지현 / Unity Korea Lead Evangelis ozlael

댓글을 달아 주세요

텍스쳐의 Read/Write Enabled


텍스쳐의 임포트 셋팅 중 Read/Write Enabled 플래그도 메모리에 영향을 미칩니다. 이 플래그가 활성화되어 있다면 텍스쳐 데이터가 CPU 메모리에 상주하게 됩니다. 원래 텍스처 데이터는 GPU에서 사용할 수 있도록 GPU 메모리에 상주합니다. 하지만, 이 플래그가 활성화가 되면 추가적으로 CPU 메모리에도 상주하게 됨으로써 메모리 사용량이 두 배가 되는 것입니다.

이 플래그는 유저 스크립트에서 Texture.GetPixel 혹은 Texture.SetPixel 등의 코드를 통해 텍스쳐의 읽기 및 쓰기가 가능한지의 여부를 의미합니다. 만일 런타임이나 로딩타임동안 스크립트에서 텍스쳐의 데이터에 접근할 필요가 있다면 이 플래그가 활성화되어야 합니다.

하지만, 대부분의 경우에는 렌더링에 사용되는 텍스처가 스크립트에서 변경될 일이 없으므로 이 플래그를 비활성화 시켜야 메모리를 절약할 수 있습니다. 때문에, 기본적으로는 비활성화되어되어있습니다.


더 많은 내용은 "유니티 그래픽스 최적화 스타트업"을 참고하세요


Posted by 오지현 / Unity Korea Lead Evangelis ozlael

댓글을 달아 주세요

유니티의 씬의 모든 Mixed 라이트는 Mixed Lighting Mode에 의존합니다. Lighting Mode 를 설정하려면, Window Rendering > Lighting SettingsEdit > Window > Lighting을 선택하여 라이팅 창을 열면 Mixed Lighting 섹션이 있습니다. Baked Global Illumination 플래그는 베이킹된 GI 즉 라이트맵 사용 여부를 의미합니다. 라이트맵을 사용하려면 이 플래그가 켜져있으면 됩니다.


Mixed Lighting 의 Lighting Mode는 씬의 라이트들 중 모드가 Mixed로 되어있는 라이트들에 의해서 영향을 받습니다. Baked Indirect, Subtractive, Shadowmask 중 하나를 선택할 수 있습니다. 이 모드에 따라서 라이트맵들은 큰 차이를 보입니다.

(이 글에서는 포워드 렌더링 패스를 기준으로 설명합니다.)


Baked Indirect


이 모드로 설정된 경우는 GI만 라이트맵에 기록되고 그림자는 기록되지 않습니다. 그림자는 베이킹이 되지 않고 런타임동안 실시간으로 연산됩니다. 즉, 그림자는 실시간으로 처리하고 GI만 미리 연산해놓고 싶은 경우 이 모드를 사용합니다. 그림자가 매우 중요하게 다뤄져야할 경우 유용합니다. 하지만 그만큼 그림자 처리 비용이 들어가는 것을 염두해두어야 합니다. 그림자 뿐만 아니라 직접광(Direct lighting)도 리얼타임으로 처리됩니다. 따라서 런타임상에서 라이트의 컬러와 강도(intensity)도 변경 가능합니다. 다음 이미지는 스태틱으로 처리된 건물의 그림자와 케릭터의 그림자가 실시간으로 처리되고 있는 모습을 보여주고 있습니다. 건물 옆의 바위는 실시간 라이팅이 적용되어 스페큘러 하이라이트가 반영되어 있습니다.

< Assets : Lighting Optimisation Tutorial, Survival Shooter Tutorial, by Unity Technologies>


씬 뷰에서 Baked Lightmap 모드로 선택하여 확인해보면 AO등의 GI는 반영이 되어있지만 디렉셔널 라이트의 그림자는 반영이 되어 있지 않습니다.



Subtractive


Subtractive는 정통적인 라이트맵이라고 볼 수 있습니다. 라이트맵에 GI와 직접조명, 그림자 까지 모두 라이트맵에 베이킹됩니다. 이 모드는 스태틱 오브젝트에 대한 라이팅 연산이 라이트맵에 새겨지므로 스태틱 오브젝트에는 실시간 라이팅이 이루어지지 않고 오로지 라이트맵에해서만 라이팅이 이루어지기 때문에 스페큘러가 반영되지 않습니다.

라이트맵에 그려진 라이트는 단순히 텍스처에 그려진 이미지와 다름 없기때문에 실시간 그림자와 자연스럽게 반응하거나 하지는 않습니다. 아래 이미지는 베이킹된 건물의 그림자 위에 케릭터의 실시간 그림자가 그려지는 모습을 보여주고 있습니다. 앞의 Baked Indirect에서 캐릭터와 건물의 그림자가 자연스럽게 연결되는 모습과는 대조되는 현상입니다.

그래서 Subtractive 모드에서는 실시간 그림자의 컬러를 조절할 수 있습니다. Realtime Shadow Color를 변경하여서 그림자 컬러를 조절해줄 수 있습니다. 씬의 전반적인 그림자의 컬러와 어울리는 컬러로 선택해서 그림자의 이질감을 줄여줄 수 있습니다.


씬 뷰에서 드로우 모드를 Baked Lightmap로 확인한 모습입니다. 오브젝트의 라이팅과 그림자, AO까지 모두 라이트맵에 처리가 되어 있습니다.

Subtractive 모드에서는 이처럼 스태틱 오브젝트들의 라이트는 라이트맵에서 다 처리하므로 런타임 동안의 라이팅 오버헤드가 극히 적습니다. 그렇기 때문에 저사양 모바일 디바이스에서 사용하기 적합한 모드입니다.




Shadowmask


Shadowmask모드는 라이트맵을 베이킹 시 그림자 영역을 별도의 텍스쳐로 따로 저장합니다. 이러한 그림자 영역을 따로 저장해놓은 텍스처를 쉐도우마스크라도 부릅니다. 얼핏 보기에는 Baked GI와 비슷하게 보일 수 있습니다. 하지만, 런타임동안 그림자 캐스팅 연산 자체를 수행하지는 않습니다. 대신 쉐도우 마스크로부터 그림자 여부를 판단해서 실시간 그림자와 자연스럽게 합성해줍니다. 따라서, Subtractive 모드와는 달리 스태틱 오브젝트와 다이나믹 오브젝트의 그림자가 자연스럽게 연결됩니다.

다음 이미지에서 건물 그림자와 캐릭터 그림자가 자연스럽게 어울리고 있습니다.



Shadowmask모드는 라이트맵이 적용되는 오브젝트에도 스페큘라 하이라이트가 적용됩니다. 건물 옆바위를 보면 스페큘라 하이라이트가 맺히고 있음을 확인할 수 있습니다. 앞선 Subtractive 모드일 경우와 비교해보면 차이가 명확하게 드러납니다. 이 스페큘라 하이라이트는 리얼타임으로 픽셀쉐이더에서 연산되기 때문에 그 만큼의 추가적인 GPU 성능이 소모됩니다. 이는 BakedGI모드에서도 마찬가지지만, BakedGI와 다른점은 그림자 캐스팅 연산에 대한 비용이 상대적으로 절약된다는 점입니다. 스태틱 오브젝트의 그림자 영역은 미리 쉐도우마스크로 저장되어 있기 때문에 런타임동안 쉐도우맵을 이용한 연산을 수행하지 않습니다. ( 주석 : Distance Shadowmask는 경우에 따라 달라집니다.)동적인 오브젝트에 대해서만 쉐도우맵 연산을 함으로써 그림자 연산에 대한 비용을 절약할 수 있습니다.


씬 뷰에서서 드로우 모드를 Shadowmask로 설정하면 쉐도우마스크가 적용되는 부분만 따로 확인할 수 있습니다.


이처럼, 그림자 연산에 대한 성능 비용이 대폭 감소하기때문에 모바일 디바이스에서 사용하기 적합합니다. Subtractive 모드에서의 시각적인 품질이 아쉽다면 Shadowmask도 고려해볼만 합니다. 다만 물론 Subtractive 모드보다는 성능 비용이 더 필요합니다.  


또한, 쉐도우마스크 텍스처가 추가적으로 만들어지기 때문에 텍스처 메모리가 추가적으로 필요합니다. 라이트캡 텍스처 생성 결과물은 라이팅 윈도우의  Global Maps 탭에서 확인할 수 있습니다. 아래 이미지는 위 씬을 디렉셔널 모드를 Directional로 설정하고 라이팅 모드를 Shadowmask로 라이트맵을 베이킹한 결과물들입니다.

첫번째 행에 보여지는 것들이 본래의 라이트맵입니다. 두번째 행에 보여지는 것들이 디렉셔널 모드로 인해 추가된 텍스쳐입니다. 세번째 행에 보여지는 것들이 쉐도우마스크입니다. 원래는 4장인 라이트맵이 디렉셔널 모드와 쉐도우마스크로 인해서 3배가 되었습니다. 하단에 총 용량이 190.7MB임을 나타내고 있습니다. PC나 콘솔에서는 문제가 되지 않는 크기이지만 모바일 디바이스에서는 메모리 문제를 일으킬 수가 있는 수치입니다. 따라서 모바일을 타겟으로 할 시에는 이러한 점들을 염두해두어야 합니다.


Shadowmask는 Distance Shadowmask 모드로 확장될 수 있습니다. Edit > Project Settings > Quality 를 선택하여 퀄리티 셋팅 창에서 선택할 수 있습니다. Shadows 섹션의 Shadowmask Mode를 Distance Shadmowmask로 설정하면 디스턴스 쉐도우마스크 모드가 활성화 됩니다.


이 모드를 활성화 시켜주면 스태틱 오브젝트도 거리에 따라 쉐도우맵을 적용합니다. 가까운 거리는 쉐도우맵으로 실시간으로 그림자를 처리하고 먼 거리는 쉐도우매스크로 그림자를 처리하는 것입니다.

가까운 거리에 있는 다이나믹 오브젝트에게 스태틱 오브젝트로부터 캐스팅되는 그림자를 고품질로 드리우고 싶을 때 이 모드를 사용할 수 있습니다. 다음 이미지는 Distance Shadowmask 모드에서 건물의 리얼타임 그림자가 캐릭터에게 맺히는 모습을 보여주고 있습니다.


Shadow Distance에 설정된 값 이상의 거리는 Shadowmask를 적용해서 처리합니다. 아래 두 이미지는 그러한 예를 보여주고 있습니다.

카메라가 건물과 멀리 떨어져 있을 시에는 쉐도우맵의 영향이 아닌 쉐도우마스크로 처리가 되어서 그림자가 만들어집니다. 그림자의 경계가 쉐도우마스크의 해상도로 인해 블럭화되는 것을 볼 수 있습니다. 또한, 케릭터는 Shadow Distance보다 멀리 있어서 그림자가 생략됩니다.

카메라가 가까이 가면 쉐도우맵을 이용하는 실시간 그림자로 바뀝니다. 건물 그림자의 경계 품질이 바뀐 것을 확인할 수 있습니다.

이처럼, 스태틱 오브젝트도 실시간 그림자를 캐스팅하게 두고싶다면 디스턴스 쉐도우마스크를 이용하면 성능을 절약할 수 있습니다.


더 많은 내용은 "유니티 그래픽스 최적화 스타트업"을 참고하세요

Posted by 오지현 / Unity Korea Lead Evangelis ozlael

댓글을 달아 주세요