'Unity3D/Graphics'에 해당되는 글 30건

  1. 2018.12.23 3D 메시 렌더링을 위한 버텍스 트랜스폼 (Vertex Transform)
  2. 2018.12.15 ASTC : 가성비 좋은 텍스처 압축 포맷
  3. 2018.12.15 LWRP(LightweightRenderPipeline) : 렌더링 성능을 위한 경량 파이프라인
  4. 2018.11.03 텍스쳐의 Read/Write Enabled 1
  5. 2018.10.30 유니티의 Mixed Lighting 모드 (라이트맵 모드)
  6. 2016.04.29 유니티에서의 텍스쳐 밉맵과 필터링 (Texture Mipmap & filtering in Unity) 5
  7. 2016.01.12 유니티 UI 위에 3D 오브젝트 그리기 (Rendering 3D objects on Unity's UI) 1
  8. 2016.01.06 저사양 기기에서 스탠다드 쉐이더 사용하기 (Using Standard Shader on low-end devices)
  9. 2016.01.06 유니티에서 ETC2 텍스쳐 사용 시 애매한 사항들 (Ambiguous things by using ETC2 on Unity 4
  10. 2016.01.03 PBS 느낌을 흉내내기 위한 Metallic MatCap 쉐이더 1
  11. 2015.12.09 유니티 5.3 WebGL 업데이트 (UNITY 5.3 WEBGL UPDATES)
  12. 2015.10.08 쉐이더에서 한번에 샘플링 할 수 있는 텍스쳐 갯수는? (How many textures do you sample in Shader?)
  13. 2015.09.30 커스텀쉐이더에서 리플렉션 프로브 사용하기 (Using Reflection Probes in Custom Shader)
  14. 2015.09.23 유니티5의 라이트맵 빌드 시간이 향상됩니다. (라이트맵 분산 빌드 & 점진적 빌드 소개)
  15. 2015.09.01 Unity WebGL 로드맵(Roadmap) (한글)
  16. 2015.08.24 몹의 다양화(variation) 작업을 위한 파츠 컬러 쉐이더(Parts Color Shader) 2
  17. 2015.06.12 유니티 5.1부터는 Unity Analytics가 기본적으로 통합되었습니다.
  18. 2015.06.09 모바일 기기의 Tile Based Rendering(타일 기반 렌더링)과 유니티에서의 주의 사항 #2 : TBR 대응 리소스 제작시 주의점 2
  19. 2015.06.09 모바일 기기의 Tile Based Rendering(타일 기반 렌더링)과 유니티에서의 주의 사항 #1 : TBR의 이해 1
  20. 2015.05.29 Unity5로 리메이크한 Republique가 에셋스토어에 올라왔습니다.
  21. 2015.05.27 화요일은#UNITYTIPS의 날 (TUESDAYS ARE FOR #UNITYTIPS)
  22. 2015.05.27 유니티의 새로운 2D 툴 미리 보기(EARLY ACCESS TO NEW 2D TOOLS)
  23. 2015.05.27 new GUI (ugui) 최적화 팁
  24. 2015.05.27 Unity에서 쿨타임 버튼 구현하기
  25. 2015.05.27 유니티5 렌더링 파이프라인 확장 기능 : 커맨드 버퍼(Command Buffer)
  26. 2015.05.27 물리 기반 셰이딩으로 작업하기
  27. 2015.05.27 실시간 그림자를 싸게 그리자! 평면상의 그림자 ( Planar Shadow for Skinned Mesh) 3
  28. 2015.05.27 유니티 실행 시 그래픽 라이브러리 선택하기. OpenGL이냐 DirectX냐 그것이 문제로다
  29. 2015.05.27 유니티5의 글로벌 일루미네이션
  30. 2015.05.27 Unity3D와 LOD, 그리고 Simplygon 1
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

버텍스 트랜스폼 (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 ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


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 ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


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)입니다. ) 오브젝트를 렌더링 할 때 오브젝트에 영향받는 실시간 라이트들을 쉐이더에 전달해줍니다. 쉐이더에서는 한번에 여러 라이트들을 모두 처리해줌으로써 별도의 드로우콜이 발생하는 것을 방지합니다. 이러한 과정을 통해서 기존의 포워드 렌더링과 비교하여 드로우콜이 줄어들게 설계되어있습니다.


관련 참고 자료 링크 : 


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

Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

텍스쳐의 Read/Write Enabled


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

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

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


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


Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

유니티의 씬의 모든 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 ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

많은분들이 물어보시는 내용 중 하나가 텍스쳐입니다. 그 중 밉맵과 필터링에 대한 내용을 정리해보고자합니다.



밉맵


텍스쳐에서 밉맵이란 텍스쳐에게 있어서 LOD같은 개념입니다. 만일 256x256짜리 텍스쳐가 화면에 그려진다고 생각을 해보죠. 이 텍스쳐가 화면에 굉장히 작은 영역으로 그려져서 실제로는 32x32픽셀만큼의 영역만 그려지는 상황이라면 굳이 256텍스쳐를 바를 필요가 없습니다. 괜히 대역폭을 낭비할 필요 없이 32텍스쳐를 가져가면 충분한 상황일 것입니다.

이런 컨셉이 바로 텍스쳐 밉맵입니다. 텍스쳐 하나 만들면 내부적으로는 여러 크기 단계의 텍스쳐를 만들어 두는 것입니다. 예를 들어서 256텍스쳐를 사용하면 내부적으로는 256,128,64,32,16,8,4,2크기의 텍스쳐를 추가적으로 생성하게 되는 것입니다. 그러고서는 런타임에서 렌더링 시 픽셀쉐이더에서 텍스쳐를 읽어들일 때 상황에 맞는 크기의 텍스쳐를 가져감으로써 실시간 대역폭을 절약하는 것입니다.

이미지 출처 : http://www.tomshardware.com/reviews/ati,819-2.html


이런식으로 하면 런타임 성능은 절약되지만 메모리는 약 30%가 늘어나게 됩니다.(정확한 수치는 아니고 통상적인 수치입니다) 따라서 메모리가 걱정된다면 밉맵을 끌 수도 있습니다. 유니티에서는 기본적으로 텍스쳐가 밉맵을 사용하도록 설정됩니다. 밉맵을 끄려면 텍스쳐별로 밉맵을 사용 안하도록 설정해줘야합니다. 텍스쳐 타입을 Advanced로 두고 Generate Mip Maps를 해제하면 됩니다. 


하지만 3D 게임에서는 특수한 상황이 아니고서는 밉맵을 끄는 것을 권장하지는 않습니다. 성능 문제도 있거니와 지글거림이 발생하여서 시각적인 문제도 발생합니다. ( 반대로 2D 게임에서는 뭉개져보이는 시각적인 문제로 밉맵을 꺼주는 것이 좋을 수도 있습니다.)

이미지 출처 : http://www.tomshardware.com/reviews/ati,819-2.html



필터링


밉맵을 이야기하면 필터링도 함께 이야기 하게될 수 밖에 없습니다. 텍스쳐 필터링은 텍스쳐 설정의 filter mode에서 변경할 수 있습니다.

  • 포인트(Point) : 필터링을 하지 않습니다. 따라서 픽셀이 블럭으로 깨져보이게 됩니다.

  • 바이리니어(Bilinear) : 텍스쳐가 필터링 됩니다. 확대필터와 축소필터 모두 적용 됩니다. 즉 원래 텍스쳐보다 확대되서 그려지든 작게 그려지든 필터링이 되는 것입니다. 

  • 트라이리니어(Trilinear) : 바이리니어가 기본적으로 작동하고, 추가적으로 밉맵 레벨이 바뀌는 구간도 필터링이 됩니다.

바이리니어와 트라이니리어간의 차이점은 밉맵 레벨이 바뀌는 구간이 필터링 되냐 아니냐의 차이입니다. 트라이리니어로 설정하면 밉맵 레벨이 바뀌는 구간도 부드러워집니다. 따라서, 바닥같은데서 시각적으로 자연스러운 렌더링이 되려면 트라이리니어를 사용해야합니다.

이미지 출처 : http://www.tomshardware.com/reviews/ati,819-4.html

또한, Anisotropic filtering을 적용하면 더욱 좋은 품질을 얻을 수 있습니다. Anisotropic은 면의 기울기를 필터링에 반영하기때문에 바닥이나 벽 등에 사용하면 좋습니다. 텍스쳐 인트펙터에서 Aniso Level을 2 이상으로 설정하면 필터링이 적용됩니다. 숫자가 높을수록 필터링이 강하게 작용하고 성능도 그만큼 필요로하게됩니다.

이미지 출처 : http://www.gamespot.com/forums/system-wars-314159282/df-issue-with-blurry-console-texture-filtering-32700608/


다만 이들은 디바이스마다 지원 여부가 다릅니다. 특히, anisotropic 필터링은 많은 기기들에서 사용이 불가능합니다. 유니티를 실행 시 로그캣으로 확인해보면 다음과 같이 기기에서 지원하는 GL 익스텐션 목록들이 출력됩니다.

ex > GL_EXT_debug_marker GL_ARM_rgba8 GL_ARM_mali_shader_binary GL_OES_depth24 GL_OES_depth_texture GL_OES_depth_texture_cube_map GL_OES_packed_depth_stencil GL_OES_rgb8_rgba8 GL_EXT_read_format_bgra GL_OES_compressed_paletted_texture GL_OES_compressed_ETC1_RGB8_texture ...

이 목록 중 "GL_EXT_texture_filter_anisotropic" 항목이 없으면 해당 기기에서 anisotropic 필터링을 지원하지 않는 것입니다.

다만, 텍스쳐 임포트 셋팅에서 aniso level을 설정해놓아도 기기에서 지원하지 않으면 작동하지 않으므로 비정상 작동하거나 하지는 않습니다. 설정을 굳이 1로 돌려놓지 않으셔도 무방합니다. 트라이리니어 역시 마찬가지로 기기에서 지원하지 않는다면 바이리니어로 작동하므로 트라이리니어로 두셔도 비정상 작동하거나 하지는 않습니다.


밉맵 바이어스


낮은 밉맵 단계가 흐려보이는 것이 거슬린다면 낮은 밉맵 단계가 적용되는 기준을 조절해줄 수도 있습니다. 물론 성능 문제상 추천되는 방법은 아닙니다만 밉맵 바이어스를 건드리면 밉맵 레벨이 바뀌는 기준을 바꿔주실 수도 있습니다. 다만, OpenGLES에서는 작동하지 않습니다.

http://docs.unity3d.com/ScriptReference/Texture-mipMapBias.html

따라서 이 기능과 같은 결과를 만들어내시려면 쉐이더에서 건드려주시면 됩니다. tex2D함수 대신 tex2Dbias를 사용하시면 됩니다. 자세한 내용은 대마왕님의 블로그를 참고하세요 :

http://chulin28ho.tistory.com/258

다만 이 역시 기기에서 지원을 해줘야합니다. GL익스텐션 목록에 EXT_shader_texture_lod가 있다면 지원 되는 것입니다.




Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

유니티의 UI 관련하여 빈번하게 문의들어오는 것 중 하나가 UI와 3D 오브젝트들의 위치 관계에 관한 것입니다. 

일반적인 UI는 게임 씬 위에 덧그려지는 방식으로 만들어집니다. 기본적으로 유니티에서 UI 컴포넌트를 생성하면 3D 오브젝트들을 렌더링 후 UI를 렌더링 하게 됩니다. 기본적으로 이러한 방식으로 UI가 화면에 덧그려지게 되지요.

하지만 인게임 말고 로비나 생성창 등에서 UI 위에 파티클을 그린다거나 UI와 UI 사이에 케릭터를 그리는 등의 상황도 필요합니다. 이러한 구성은 어떻게 하는지 문의가 자주 들어오곤 합니다. 

구성 방법이야 여러가지가 있을 수 있겠지만, 우선은 캔버스의 렌더 모드를 스크린스페이스-카메라로 사용해주면 됩니다. 기본으로 캔버스를 생성하면 렌더 모드가 스크린스페이스-오버레이인데, 이는 씬 렌더 후 화면에 덮어서 그리는 UI를 의미합니다. 스크린스페이스-카메라는 캔버스의 깊이를 설정할 수 있어서 3D 오브젝트와의 깊이 관계를 설정할 수가 있습니다. 따라서, 위 화면과 같이 케릭터 배경 창이 있고 케릭터 위에 버튼을 그리고자 하는 경우라면, 깊이가 다른 각각의 두 캔버스를 스크린스페이스-카메라 렌더모드로 만들고, 각각의 캔버스의 평면 거리(plane distance)를 적절한 깊이로 설정한 다음, 사이에 3d 오브젝트를 배치하면 다중 UI 깊이에 대해서 렌더링을 할 수 있습니다.

샘플 프로젝트를 올려두었으니 이를 참고해보시면 도움 되실 것이라 생각합니다 : https://drive.google.com/a/unity3d.com/file/d/0B70AOyGiQJsGT3dDRW9uUldOeTg/view?usp=sharing



Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

제 개인적인 생각으로는 물리기반렌더링(Physically Based Rendering/Shading, 이하 PBR,PBS)이 2016년 국내 게임 시장의 중요 키워드 중 하나가 아닐까 생각합니다.  모바일 기기의 성능들이 많이 발전하고 특히 국내에는 고사양 폰의 보급률이 높아짐에 따라 모바일에서도 PBR을 사용하는 움직임들이 많이 보이고 있습니다. 유니티에서는 스탠다드(Standard) 쉐이더를 통해서 PBR을 처리해주고 있습니다. 이 스탠다드 쉐이더는 유니티5에 적용된 인라이튼(Enlighten)과 연동하여 실시간 GI(Global Illumination) 및 물리 기반 라이팅을 표현해줌으로써 더욱 사실적이고 멋진 그래픽을 표현할 수 있도록 해줍니다. 

하지만 PBS는 복잡한 연산을 거쳐서 빛을 표현해주어야 하다보니 성능을 크게 잡아먹습니다. 물론 게임에서는 이를 나름 간략화 시켜서 사용하고, 유니티 역시 스탠다드 쉐이더에 이를 최적화하여 적용합니다. 또한, PC에서 수행하면 PC용 모드의 스탠다드 쉐이더로 수행되고 모바일 기기에서 수행하면 모바일용으로 더 최적화된 모드의 스탠다드 쉐이더로 수행됩니다. 이 역시 버전을 거듭할 수록 계속 최적화가 진행중입니다. 하지만, 기본적으로 연산 자체가 복잡하기 때문에 씬 전체를 스탠다드 쉐이더로 사용하는 것은 아이폰5, 아이패드에어, 넥서스9 급 이상의 하이엔드 기기를 타겟으로 사용하는 경우에만 권장합니다.

고사양 기기가 많이 보급되어 있긴 하지만 아직도 저사양 폰들이 시장에 많이 존재합니다. 이러한 기기들에서는 씬 전체를 스탠다드 쉐이더로 그리는 것은 무리입니다. 하지만, 스탠다드 쉐이더를 부분적으로만 사용한다면 충분히 보급형 기기에서도 이를 활용 할 수도 있습니다. 예를 들어, 케릭터에게만 스탠다드 쉐이더를 사용하고 배경에는 다른 가벼운 쉐이더를 사용하는 것입니다. 다음 이미지에서는 그러한 예시를 보여주고 있습니다. 로보트들과 드론들에게는 스탠다드 쉐이더를 사용하였고 배경에는 Mobile/Unlit (Supports Lightmap) 쉐이더를 사용하였습니다. 갤럭시 S3에서 60 이상의 FPS로 렌더링 되고 있습니다. 데모를 구글플레이에 올려두었습니다. 다운로드 받아서 확인 가능하십니다. 

링크 : https://play.google.com/store/apps/details?id=com.ozproject.demo2

사용 에셋 : Armored Golem, Sci-fi Flying Droid, Mech Robot Sci-fi, Orbital Reentry Craft - No Interior ( 제가 아트감각이라고는 빵점인 공돌이라 비쥬얼 퀄리티가 좋지 못한 점 너른 양해 부탁드립니다;; )

또한, 저사양 기기에서 스탠다드 쉐이더를 사용하기 위해서는 텍스쳐 슬롯을 가능한 아껴주는 것이 좋습니다. 사용되지 않는 텍스쳐 슬롯은 스탠다스 쉐이더가 알아서 연산을 건너뜀으로써 성능을 절약하 수 있습니다.

예를 들어 다음 우주선은 Emission 맵을 사용하지 않았습니다.

드론에게는 노말맵을 사용하지 않았습니다.

골렘 로보트에게는 Metallic맵을 사용하지 않고 수치로써 단순하게 적용하였습니다.


이러한 식으로 스탠다드 쉐이더를 선택적으로 사용하고 파라미터를 절약한다면 저사양 기기에서도 충분히 스탠다드 쉐이더를 통한 PBR을 사용할 수 있습니다. 감사합니다.


Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


들어가며

유니티에서 안드로이드 타겟으로 개발하면 텍스쳐 압축을 ETC로 사용하게 됩니다. 기존에는 텍스쳐의 기본 압축 포맷이 ETC1이였는데, 최근 5.2.0부터는 RGBA 텍스쳐의 압축 포맷이 ETC2.0으로 바뀌게 되었습니다. 텍스쳐 포맷을 compressed로 두면 내부적으로는 ETC2를 사용하도록 되는 것입니다. 그래서 그런지 ETC 포맷에 관한 문의가 빈번하게 들어오고 있습니다. 그래서, 잘 오해할 수 있는 부분을 정리해드리고자 합니다.


Open GL ES 버전 & ECT 포맷 버전

ETC2 압축 포맷은 ETC1가 확장 및 개선된 포맷입니다. 때문에, 가능하다면 ETC1보다는 ETC2를 사용하는 것이 당연히 좋습니다. (참고로, DXT1, DTX3, DXT5는 이야기가 다릅니다. 서로 품질과 크기의 조율 선택 사항입니다.) 다만 문제는 ETC2는 Open GL ES 3.0 이상에서만 지원됩니다. 갤럭시 S3 이하 등의 구형 기종에서는 Open GL ES 2.0으로 돌아가기 때문에 ETC2를 사용하는 것이 불가능합니다.


Open GL ES 2.0 기기에서의 ETC2 사용

하지만 막상 유니티에서 텍스쳐 포맷을 ETC2로 두고 Open GS ES 2.0 기기에서 구동을 해봐도 텍스쳐가 정상적으로 그려집니다. ETC2가 Open GL ES 2.0에서는 지원이 되지 않기때문에 텍스쳐가 그려지지 않아야 될 것으로 생각되서 혼란스러우실 수도 있겟습니다. 

이미지 출처 : http://chulin28ho.tistory.com/

사실, 기기에서 지원되지 않는 텍스쳐 압축을 사용하는 경우, 텍스처는 비압축 RGBA 32 형식으로 압축된 텍스처와 함께 메모리에 저장됩니다. 따라서 이러한 경우에는 텍스처 압축을 푸는 만큼 불필요한 계산 시간이 발생하게 되며 메모리 공간도 두 배로 필요해집니다. 이것 또한 렌더링 성능에 심각한 악영향을 미칩니다.

설명이 좀 애매하긴 한데 예를 들자면, OpenGL ES 2.0 기기에 ETC2 포맷을 사용하면 작동하는데에는 문제가 없지만 성능상 효율이 떨어지게 됩니다. 왜냐하면 ES 2.0 기기는 ETC2를 지원하지 않기 때문에 32비트x폭x높이 + 원래의 ETC2 사이즈 만큼의 메모리가 늘어나서 압축 지원도 못받으면서 메모리만 늘어나게 되기 때문입니다. 또한 로딩 타임에 텍스쳐를 소프트웨어적으로 압축을 푸는 시간도 추가됩니다.


ETC2의 ETC1 하위 호환성

ETC2는 ETC1의 하위 호환성을 보유하고 있습니다. 때문에 Open GL ES 2.0에서도 ETC2를 읽을 수 있는 것으로 알고 계시는 분도 간혹 계십니다. 충분히 오해할만 하지만, ETC2가 ETC1 하위 호환을 포함한다는 것은 기기에서 ETC1을 ETC2처럼 취급할 수 있다는 것일 뿐입니다. 앞서 말씀드린대로 유니티에서는 ETC2를 ES2.0 기기에서 올리면 23bit RGBA로 사용됩니다. 


알파 유무의 차이

안드로이드 텍스쳐 압축의 기본 값이 ETC2 로 바뀌었습니다만 모든 텍스쳐가 ETC2로 기본 설정 되는 것은 아닙니다. ETC2가 기본이 되는 것은 RGBA 텍스쳐(즉, 알파를 포함하는)입니다. ETC2는 ETC1과는 달리 알파 채널을 포함할 수 있기 때문입니다. RGB만 존재하는(즉, 알파채널이 없는) 텍스쳐의 기본 압축 포맷은 여전히 ETC1입니다. RGB 텍스쳐를 ETC로 사용하기를 원하시면 Texture Type을 Advanced 로 변경 후 Format 설정을 바꿔주셔야 합니다. 

릴리즈 노트에서 언급한 포맷 기본값이 바뀌었다는 내용은 RGBA 텍스쳐에 해당하는 내용입니다. 기존에는 16bit RGBA였는데 이 것이 ETC2로 바뀌었다는 것입니다. 릴리즈 노트가 부실해서 충분히 오해의 소지가 있을 것 같습니다. (아직 매뉴얼 페이지에는 이 내용이 갱신되지 않은 것 같습니다. 사람 사는데가 다 똑같죠 뭐 헤헤 양해좀 굽신굽신)


Open GL ES 2.0에서의 RGBA 텍스쳐

하지만 그렇다고 Open GL ES 2.0에서 RGBA 텍스쳐를 사용하려면 RGBA 16비트 혹은 RGBA32비트만 사용하여야 하는 것은 아닙니다. Compress using ETC1(split alpha channel)을 선택하면 원본의 RGBA가 유니티 내부적으로 두 개의 ETC1으로 갈라서 하나는 RGB, 다른 하나는 알파 채널 정보를 담는게됩니다. 그럼으로써 ETC2를 사용하지 않고도 알파 채널을 사용할 수 있는 꼼수를 부립니다.

다만, 이렇게 되면 실제로는 2 개의 텍스쳐 슬롯이므로 쉐이더에서도 텍스쳐 레지스터를 두 개를 차지하게 됩니다. 예를 들자면 내장(bult-in)쉐이더 중 Sprite-Diffuse.shader를 살펴보면 다음과 같이 메인 텍스쳐를 읽은 뒤 _AlphaSplitEnabled 조건이라면 알파 채널을 별도로 한번 더 읽는 것을 확인 가능합니다.

fixed4 SampleSpriteTexture (float2 uv)

{

fixed4 color = tex2D (_MainTex, uv);

#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED

if (_AlphaSplitEnabled)

color.a = tex2D (_AlphaTex, uv).r;

#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED

return color;

}

단, 이 기능은 현재 릴리즈된 버전에는 스프라이트에 쓰이는 경우에만 작동하고 있으며 차츰 사용처를 확대해나가는 작업이 진행 중입니다.


ETC1이냐 ETC2냐 그것이 문제로다

“어쩌라는거야 그럼 ETC1 쓰라는거야 ETC2 쓰라는거야?” 이쯤 되면 이러한 질문이 나오시게 될 것입니다 :) 저도 사실 여기에 대한 명쾌한 답변을 드릴 수는 없습니다. ES 3.0지원 기기의 점유율이 증가하고 있는 추세긴 하지만 아직까지는 ES 2.0 지원 기기들의 점유율이 더 높은 상태기 때문입니다. (RGB 텍스쳐의 압축 기본 포맷은 ETC1으로 유지하는 이유이기도 합니다.) 이는 유니티 하드웨어 스탯 사이트에서 확인 가능합니다. 

다만, ETC2를 사용하면 그 만큼 더 메모리를 절약하고 더 높은 퀄리티를 낼 수 있는 것은 자명한 일입니다. 때문에 제 개인적인 생각으로는 주 타켓 기기를 무엇으로 삼냐에 따라 선택을 달리해야 할 것 같습니다. 물론, 보급형 기기를 타겟으로 하는 가벼운 게임이라면 ETC1을 포기해서는 안될 것입니다. 하지만, 고퀄리티 고성능 기기 게임이라면 ETC2를 사용하고 ES 2.0 기종에서는 낭비되는 메모리만큼 다른 부분에서의 품질을 과감히 포기하는 방향도 그리 나쁜 선택만은 아닐 것입니다. 참고로, SystemInfo.graphicsDeviceVersion를 통해 ES 버전을 확인 가능하고 QualitySettings.SetQualityLevel를 통해서 퀄리티 변경이 가능합니다.


SA

다음 링크들을 같이 참고해보시면 더 추가적인 정보들을 확인 하실 수 있을 것입니다:



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

Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


들어가며

물리기반렌더링( Physically based rendering, PBR, Physically based shading, PBS, 이하 PBR, PBS)이라는 키워드가 떠오른 것은 꽤 오래 되었고 이미 PC나 콘솔에서는 널리 적용되었습니다. 최근 출시한 메탈기어솔리드5는 그 정점을 보여주고 있습니다.

하지만, 모바일이 주력 시장인 국내 게임 시장에서는 관심 밖이였지요. 하지만 최근들어 국내에서도 PBR에 대한 관심이 많아지고 있습니다. 이러한 관심의 가장 큰 이유는 모바일 하드웨어의 성능 발전일 것입니다. PBR이 기본적으로 소모하는 비용을 충분히 감당한 만큼의 성능을 내는 기기들이 보편화 되고 있는 것입니다. 비단 최신 기종이 아니더라도 캐릭터에게만 PBS를 사용하는 등 부분적으로만 사용하여 성능을 절약할 수도 있습니다. 하지만, PBS 쉐이더를 사용하지 않고도 PBS 느낌을 낼 수 있다면 더욱 많은 성능을 절약할 수 있을것입니다. 물론, 초실사 렌더링을 지향하는 게임이라면 100% PBS로 렌더링을 해야겠지요. 하지만 대부분의 모바일 게임들은 그렇지가 않습니다. 근래의 모바일 게임에서 PBS사용하고자 함은 좀 더 멋진 그래픽을 표현하고자 함이 주 목적일일 뿐이지 꼭 물리적으로 정확한 그래픽을 표현하고자 함이 주 목적은 아니라고 생각합니다. 특히, 그 초점은 금속 재질 표현에 맞춰져 있습니다. 비 금속 재질보다는 금속 재질에 촛점을 맞춰서 금속 부분과 금속이 아닌 부분을 함께 표현하고자 함이 목적인 것으로 생각됩니다. 

이러한 관점을 기준으로 느낌을 내는 쉐이더를 만져보보았습니다. 그 결과물인 PBR 대응 머티리얼 텍스쳐들을 그대로 사용하는 가벼운 쉐이더를 소개해드릴까 합니다. MatCap 쉐이더에 Metallic factor를 적용해서 PBS같은 느낌을 내는 방식입니다. 다음 스크린샷 이미지에서 렌더링 되고 있는 케릭터와 발판은 PBR 대응으로 만들어진 텍스쳐들을 그대로 사용하고 있으며 실시간 라이팅이 아닌 MatCap 텍스쳐 기반으로 처리되어있습니다. 데모를 구글 플레이스토어에 올려놓았으므로 기기에 설치하여 확인해보실 수 있습니다. 

다운로드 링크 : https://play.google.com/store/apps/details?id=com.ozproject.demo1



MatCap 쉐이더

우선, MatCap 쉐이더를 기반으로 하고 있으므로 MatCap 쉐이더에 대해 간략하게 설명을 드리도록 하겠습니다. MatCap 쉐이더는 모바일에서 유용하게 사용할 수 있는 이미지 기반 라이팅 중 하나입니다. MatCap은 Material Capture의 약자인데, Material Capture는 현실 세계의 라이팅을 수집해서 캡쳐하기 위한 구체를 의미합니다. 보통 CG 영상이나 이미지에서 실사 렌더링을 위한 라이팅 참고 자료로 사용하기 위한 용도로 만들어집니다.

MatCap 쉐이더는 이러한 Material Capture를 그대로 텍스쳐(이를 MatCap 텍스쳐라 부릅니다)로 만들어서 라이팅 결과로 활용하는 것입니다. 이를 이용하면 재질에 따른 라이팅을 미리 텍스쳐로 만들어놓기 때문에 실시간으로 라이팅 연산을 않고도 실사적인 라이팅을 처리할 수 있습니다. 

출처 : https://shaderforge.userecho.com/topic/416166-it_matrix/

MatCap 쉐이더는 유니티에 내장된 쉐이더는 아니지만 구글링이나 유니티 에셋스토어에서 쉽게 구할 수 있습니다. 이 글에서 소개하고자 하는 쉐이더는 에셋스토어에 있는 MatCap 쉐이더를 기반으로 작업하였습니다. 쉐이더 링크 : https://www.assetstore.unity3d.com/en/#!/content/8221


Metallic

PBS에서의 특징중 하나는 금속과 비금속을 수치로 표현하는 것입니다. 빛이 사물에 닿으면 일부는 흡수되었다가 방출되고 나머지는 바로 반사되 튕겨나갑니다. 이 중 흡수되었다가 방출출되는 빛이 일반적으로 말하는 디퓨즈 영역이고 바로 튕겨나가는 빛이 일반적으로 말하는 스페큘라 영역이 되는 것입니다.

금속은 빛이 닿으면 이러한 디퓨즈 영역이 없이 완전 반사가 일어납니다. 즉, 표면이 금속에 가까울수록 반사의 비중이 높고 금속이 아닌 비전도체에 가까울 수록 디퓨즈의 비중이 높아집니다. 

이미지 출처 : http://docs.unity3d.com/kr/current/Manual/StandardShaderMaterialCharts.html

PBS 머리티얼에서는 이를 반영하여 얼마나 금속에 가까운지의 정도를 나타내는 Metallic 텍스쳐를 사용합니다.

기존의 MatCap 쉐이더는 MatCap 텍스쳐를 한 장만 사용합니다. 이를 Metallic 텍스쳐를 사용하기 위해서 MatCap 텍스쳐를 두 장을 사용하도록 변경해줍니다. 금속성이 0인 경우의 MatCap 텍스쳐와 완전 금속인 경우의 MatCap 두 개의 MatCap을 사용하여 Metallic에 따라 비중을 반영해주면 되는 것입니다. 


파라미터

또한, AO맵 역시 반영해주어야합니다. AO는 빛이 차폐되는 정도를 나타내주는 것이므로 라이팅 연산 최종 단계에서 곱해주는 것으로 간단히 처리 해주면 됩니다. Emissive는 발광하는 부분이므로 라이팅 연산 최종 단계에서 더해주는 것으로 간단히 처리 해주면 됩니다. 최종적인 파라미터들은 다음과 같습니다.

코드

쉐이더 코드는 다음과 같습니다. 데모에 쓰인 리소스는 에셋스토어에서 유료로 판매되고 있는 리소스이므로 프로젝트를 공유드리지 못하는 점 양해 바랍니다. 

Shader "MatCap/Bumped/Textured Metalic" {

Properties {

_MainTex ("Base (RGB)", 2D) = "white" {}

_AOMap ("AO (RGB)", 2D) = "white" {}

_AOFactor ("AO Factor" , Range(0.0,2.0)) =1.0

_BumpMap ("Normal Map", 2D) = "bump" {}

_MetalicMap ("Metallic (RGB)", 2D) = "white" {}

_MetalicMultiply ("Metallic Multiply" , Range(0.0,10.0)) =1.0

_EmissiveTex ("Emissive (RGB)", 2D) = "black" {}

_EmissiveMultiply ("Emissive Multiply" , Range(0.0,10.0)) =1.0

_MatCapDiffuse ("MatCap Diffuse (RGB)", 2D) = "white" {}

_MatCapReflect ("MatCap Reflect (RGB)", 2D) = "white" {}

_MatCapReflectMultiply ("MatCap Reflect Multiply" , Range(0.0,10.0)) =1.0

}

Subshader {

Tags { "RenderType"="Opaque" }

Pass {

Tags { "LightMode" = "Always" }

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

#pragma fragmentoption ARB_precision_hint_fastest

#include "UnityCG.cginc"

struct v2f {

float4 pos : SV_POSITION;

float4 uv : TEXCOORD0;

float3 c0 : TEXCOORD1;

float3 c1 : TEXCOORD2;

};

uniform float4 _MainTex_ST;

uniform float4 _BumpMap_ST;

v2f vert (appdata_tan v) {

v2f o;

o.pos = mul (UNITY_MATRIX_MVP, v.vertex);

o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);

o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);

float3 n = normalize(v.normal).xyz;

float3 t = normalize(v.tangent).xyz;

float3 b = normalize(cross( n, t ) * v.tangent.w);

float3x3 rotation = float3x3( t, b, n );

o.c0 = mul(rotation, normalize(UNITY_MATRIX_IT_MV[0].xyz));

o.c1 = mul(rotation, normalize(UNITY_MATRIX_IT_MV[1].xyz));

return o;

}

uniform sampler2D _MainTex;

uniform sampler2D _BumpMap;

uniform sampler2D _AOMap;

uniform sampler2D _MetalicMap;

uniform sampler2D _MatCapDiffuse;

uniform sampler2D _MatCapReflect;

uniform sampler2D _EmissiveTex;

uniform float _MatCapReflectMultiply;

uniform float _AOFactor;

uniform float _EmissiveMultiply;

uniform float _MetalicMultiply;

fixed4 frag (v2f i) : COLOR {

fixed4 tex = tex2D(_MainTex, i.uv.xy);

fixed4 ao = tex2D(_AOMap, i.uv.xy);

fixed4 metalic = tex2D(_MetalicMap, i.uv.xy);

fixed4 si = tex2D(_EmissiveTex, i.uv.xy);

fixed3 normals = UnpackNormal(tex2D(_BumpMap, i.uv.zw));

half2 capCoord = half2(dot(i.c0, normals), dot(i.c1, normals));

fixed4 diff = tex2D(_MatCapDiffuse, capCoord*0.5+0.5) * tex;

fixed4 refl = tex2D(_MatCapReflect, capCoord*0.5+0.5);

refl.a = 1;

refl *= _MatCapReflectMultiply;


fixed4 ret = lerp( diff, refl, saturate(float4(metalic.rgb * _MetalicMultiply,1)));

return ret * lerp( 1, ao, _AOFactor) + si * _EmissiveMultiply;

}

ENDCG

}

}

Fallback "VertexLit"

}


한계

간단한 쉐이더다보니 많은 한계점을 가지고있습니다. 보시다시피 이는 정확한 PBS는 아닙니다. 많은 부분들이 생략되어 있고 라이팅 재질 느낌은 전적으로 MatCap 텍스쳐에 의지하고 있습니다. 때문에, MatCap 텍스쳐를 어떤 것으로 사용하느냐에 따라 품질이 크게 좌우됩니다. 또한, 씬 별로 라이팅이 다르다면 씬 별로 MatCap 텍스쳐를 만들어 줘야 합니다. MatCap 텍스쳐를 어떻게 만드냐에 따라 결과가 달라질 것입니다. 

MatCap 방식은 한 쪽에서 바라보는 라이팅만 표현이 가능하기 때문에 카메라 각도가 고정되어 있는 경우에만 유효하다는 제약이 있습니다.그러나, 대부분의 모바일 게임들은 탑 뷰(Top View), 쿼터 뷰(Quarter View), 백 뷰(Back View) 등 카메라의 방향이 고정된 채로 게임이 진행되는 경우가 대부분이므로 모바일서는 큰 제약 사항이 되지는 않을 것입니다.



Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


Unity 5.3 릴리즈는 WebGL를 지원하는 네번째 버전입니다. 5.0에서부터 WebGL을 프리뷰(Preview)로 처음 탑재하고 난 이후에 수 많은 사항들이 개선되어 왔습니다. 몇 가지 사항들을 공유드리고자 합니다. 이 글은 유니티 공식 블로그의 UNITY 5.3 WEBGL UPDATES를 번역 및 요약한 글입니다. 정확한 내용을 원하신다면 원문을 읽어보시길 바랍니다.


5.3에서의 바뀐 점

  • WebGL에서 스탠다드쉐이더가 데스크탑 퀄리티의 리플렉션을 사용합니다. 기존에는 유니티 WebGL은 OpenGL 2.0을 사용하는 모바일용으로 간략화된 버전의 스탠다드 쉐이더를 사용했었습니다. 이제는 데스크톱과 동일한 리플렉션을 사용하여 고품질의 재질 결과를 표현해줍니다.
  • 부드러운 그림자(Soft Shadow)를 지원합니다.
  • 귀하의 서버가 설정이 되어 있지 않더라도 유니티 WebGL은 압축을 지원합니다.기존에는 WebGL은 gzip 압축된 파일을 제공하기 위해서는 서버에 셋팅이 필요했습니다. 그렇지 않으면 압축되지 않은 데이터를 사용하여 긴 시간의 다운로드가 필요했습니다. 하지만 이제는 유니티의 WebGL은 자동적으로 gzip 압축된 데이터를 제공합니다. 이는 클라이언트단의 자바스크립트에서 압축 해제를 수행합니다. 이로 인해 압축 처리 딜레이가 조금 발생하긴 하지만 거대한 다운로드 시간을 피할 수 있습니다. 배포 사이즈에 대한 자세한 내용은 이 문서를 참고 바랍니다.
  • 데이터 파일이 LZ4 압축 상태로 메모리에 존재합니다. 유니티 5.3에서는 에셋 데이터가 메모리에 LZ4 압축된 상태로 메모리에 올라갑니다. 에셋이 로드 될 때에만 압축이 해제됩니다. 이는 에셋 데이터가 적은 메모리를 차지해서 메모리 낭비될 일이 적어짐을 의미합니다.
  • WebGL 빌드 파일을 다른 url로 이전하는 것이 쉬워졌습니다. 빌드 프로세스 과정에서 생성되는 파일들은 index.html에 의해 참조됩니다. 이로인해 빌드 데이터를 외부 호스팅 솔루션으로 배치하는 것이 쉬워졌습니다. 빌드 아웃풋 파일 이전의 자세한 내용은 이 문서를 참고 바랍니다.
  • 웹캠을 지원합니다. 5.3에 WebCamTexture 클래스가 추가되었습니다. 이는 getUserMedia API를 지원하는 브라우저에서 사용 가능합니다. 
  • WebGL이 유니티 클라우드 빌드가 가능합니다.
  • 문서들을 개선하였습니다. 5.3을 위해서 WebGL 문서가 대폭 개선되어 많은 정보들이 추가되었습니다.
  • 수 많은 버그들이 수정되었습니다. 5.3에서는 5.2에 비해 28개의 WebGL 버그가 수정되었습니다. 외에도 WebGL에서 간접적으로 영향을 주는 수 많은 버그들이 수정되었습니다. 또한, 5.2.x 패치 릴리즈 사이클 동안 수정된 WebGL 버그 수정 사항들이 포함되어 있습니다.


WebGL이 공식 지원 타겟이 되었습니다.

WebGL은 공식 지원하지 않는 프리뷰(Preview) 상태였습니다. 5.3부터는 WebGL에서 프리뷰 레이블을 제거하고 공식 지원 대상이 되었습니다. 따라서, 프리미엄 서포트 및 엔터프라이즈 서포트는 WebGL을 커버합니다.

5.0에서 WebGL이 탑재되고나서 5.3까지 오면서 시간이 꽤 지났고 시간이 지날 수록 브라우저 기술도 발전해왔습니다. 예를 들어 마이크로소프트는 새로운 브라우저인 엣지를 윈도우즈10에 탑재하고, 이 브라우저는 asm.js를 지원함으로써 익스플로러 11에서는 불가능했던 컨텐츠들을 지원할 수 있게 되었습니다. 

그간 WebGL이 많은 개선이 되었긴 했지만 갑자기 모든 기능들이 WebGL에서 잘 동작한다는 의미는 아닙니다. 또한, 네이티브 빌드만큼의 성능이 나온다거나 모든 브라우저에서 아무 문제 없이 동작한다는 것은 아닙니다. 시간이 지남에 따라 개선은 계속 이루어 질 것이며 지금 시점에서는 공식 지원을 시작하기 적절한 시기인 것으로 판단한 것입니다. 현재의 작업 상태와 제한 상황을 문서에 명시하였으므로 이를 참고 바랍니다.


브라우저 제조사들과의 작업

유니티의 WebGL은 브라우저가 제공하는 웹 기술에 의존적일 수 밖에 없습니다. 주요 브라우저 제조사들과 긴밀하게 작업을 하고 있으며, 이들은 빠른 속도로 기술을 발전시키고 있습니다. 모질라의 파이어폭스, 마이크로 소프트의 엣지, 구글의 크롬 등의 주요 브라우저들과 유니티의 호환 작업이 지속적으로 이루어지고 있어서 빠른 속도로 개선이 되고 있습니다.


시도해보세요!

WebGL로의 익스포트는 웹 게이밍의 미래라고 믿고 있습니다. 귀하의 게임을 유니티의 WebGL로 배포해보십시요. Heroes Of Paragon, Spider Box, Big Buck Hunter 등 이미 몇 개의 사례들이 있습니다. (역주 : 씨발 애석하게도 이 예시들의 게임은 망할 게등위(게임산업진흥에관한법률)때문에 국내에서는 플레이가 불가능합니다.시작하기 위해서는 다음 절차를 따르십시요. 



Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


쉐이더를 작성하다보면 텍스쳐를 많이 사용하는 기능을 만들 때도 있습니다. 대표적인게 바로 지형 스플래팅이죠. (유니티의 터레인은 아무도 쓰지 않는다는 슬픈 전설이...) 스플래팅의 개념을 모르시는 분든을 위해서 간략히 설명 드리자면, 스플래팅은 여러 텍스쳐를 서로 블렌딩해서 표현해주는 방식을 뜻합니다. 이렇게 하게 되면 작은 크기의 텍스쳐들을 가지고도 넓은 지역을 표현해줄 수가 있어서 텍스쳐 메모리를 상당히 절약할 수 있게 됩니다. 

이미지 출처 : http://blog.naver.com/sorkelf/40151055590

예전에 GPU의 쉐이더 성능이 좋지 않은 시절에는 이러한 스플래팅을 처리하기위해 여러번 덧그리는 멀티 패스로 구현해야만 했습니다. 하지만 요즘은 쉐이더에서 여러장의 텍스쳐를 한번에 읽어서 싱글 패스로 처리할 수가 있습니다. 하지만 문제는 쉐이더에서 한번에 읽을 수 있는 텍스쳐의 갯수가 제한 되어 있다는 것입니다. 만일 쉐이더에서 너무 많은 쉐이더를 샘플링 하면 원하는대로 작동하지 않게 됩니다. 근데, 이 현상이 인지하기 쉽지도 않습니다. 쉐이더 컴파일 오류가 나는 것 도 아니고 오브젝트가 보라색으로 칠해지는 것 도 아니도 그냥 몇 개의 텍스쳐가 읽히지 않고 무시되어 버립니다. 이러한 현상은 OpenGL에서 제한하는 텍스쳐 수가 다르기 때문입니다. OpenGL 공식 스펙에 이러한 숫자가 정해져 있긴 하지만 디바이스마다도 또한 차이가 발생합니다. 때문에컴파일 단계에서 이러한 제한을 탐지할 방법이 없습니다. 그러다보니 실제 렌더링 과정에서 텍스쳐 샘플링 갯수 제한을 넘게 되면 더 이상의 텍스쳐 샘플링은 무시가 되고 렌더링이 되는 것입니다.

OpenGL에서 쉐이더에서 한번에 샘플링 할 수 있는 텍스쳐 갯수는 MAX_TEXTURE_IMAGE_UNITS로 정의 되어 있습니다. (유니티에서 확인해볼 수 있는 수치는 아닙니다.)이를 문서에서 확인해보면 ES2는 8, ES3에서는 16으로 나옵니다. 즉, OpenGL ES2에서는 쉐이더에서 한번에 최대 8개의 텍스쳐를 사용할 수 있고, OpenGL ES3에서는 쉐이더에서 한번에 최대 16개의 텍스쳐를 샘플링 할 수 있다는 말이 됩니다. (물론 이 값은 특정 디바이스에서는 다른 값일 수도 있습니다.)

이미지 출처 : iOS 개발자 라이브러리

그렇다고 쉐이더이 인스펙터에 텍스쳐 슬롯을 8개 만들면 정상적으로 쓸 수 있다는 말은 또 아닙니다. 유니티는 서피스 쉐이더로 작성하고나면 유니티는 자동적으로 추가적인 정보들을 처리해줍니다. 그러는 과정에서 유니티에서 사용하는 텍스쳐가 추가적으로 쓰일 수 있습니다. 이는 쉐이더의 variant에 따라서 다르므로 완성된 코드를 확인하여야 합니다. 쉐이터를 선택 후 인스펙터에서 Compile and show code를 선택합니다. 

그러면 유니티가 서피스 쉐이더를 기반으로 추가적인 코드들과 variant들을 만들어내어 완성한 후 최종 컴파일에 사용한 코드를 보여줍니다. 코드를 확인해보면 각각의 variant에 따른 사용 텍스쳐 갯수를 주석으로 확인해볼 수 있습니다. 이 예시의 쉐이더는 서피스 쉐이더에서 8개의 텍스쳐를 사용하고 있었고 인스펙터에서 설정하고 있는 텍스쳐도 8개뿐이지만 실제로 생성된 결과물을 보면 조건에 따라 10개의 텍스쳐를 사용하고 있음을 알 수 있습니다. 

이러한 경우에는 ES 2.0에서의 한계치인 8을 넘어버리기 때문에 원하는 모습대로 렌더링이 되지 않을 수 있습니다. 때문에, 작성하고 있는 쉐이더에서 텍스쳐를 6개 이상 사용할 경우에는 variant에 따른 실제 텍스쳐 사용 갯수를 확인하여야 합니다.

맺음말 적당한게 생각 안나네요. 걍 끘! 안녕~



Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


게임 그래픽에서 반사의 표현은 매우 중요한 시각적 요소 중 하나입니다. 주변 환경의 반사를 표현함으로써 금속 재질을 처리할 수 있기 때문입니다. 판타지물의 투구라든가 SiFi물의 로보트 등 금속 재질에서의 반사 표현 여부는 그래픽 퀄리티에 큰 영향을 미칩니다.

유니티5에서 라이팅 관련 기능이 강화되면서 리플렉션 프로브(Reflection Probe) 기능이 추가되었습니다. 이름에서 유추할 수 있듯이, 라이트 프로브(Light Probe)처럼 특정 지점들의 리플렉션을 미리 연산해서 프로브에 저장해놓는 것입니다. 

라이트 프로브 및 라이트맵(Lightmap)이 정적(static)인 오브젝트와 Baked 또는 mixed로 설정 된 라이트만 반영하는 것과 마찬가지로, 리플렉션 프로브 역시 정적인 오브젝트들만 반영한다는 등의 제약점들이 있긴 합니다. 하지만 부분 부분 마다의 반사를 제대로 반영할 수 있다는 점은 크나큰 매력이 될 것입니다. 자세한 사용법은 메뉴얼 및 블로그 글을 참고하세요. (PHYSICALLY BASED SHADING IN UNITY 5: A PRIMER, 리플렉션 프로브, 리플렉션 프로브 개요리플렉션 프로브 사용하기, 물리 기반 쉐이딩으로 작업하기) 메뉴얼의 글은 조만간 한글로 업데이트 될 예정입니다.

사실, 반사의 표현은 유니티5에서부터만 쓰게된 것은 아닙니다. 전통적으로 큐브맵(Cubemap)을 사용하여 쉐이더에서 반사를 표현해왔습니다. 유니티에 내장(Built-in)된 기존 쉐이더(Legacy Shader)에서도 이러한 큐브맵 반사들을 지원해왔습니다. (메뉴얼 : 반사 쉐이더 Family

하지만 기존의 쉐이더들은 특정 큐브맵을 명시적으로 지정해줘야만 하고 위치마다 다른 환경을 반영하는 리플렉션 프로브를 적용하지는 않습니다. 때문에, 내장 쉐이더에서 리플렉션 프로브를 사용하기 위해서 스탠다드 쉐이더(Standard Shadder)를 이용하여야합니다. 하지만, 저가의 보급형 모바일 기기에서는 스탠다드 쉐이더를 사용하기에는 성능의 부담이 존재합니다.

따라서, 모바일에서 리플렉션 프로브를 사용하기 위해서는 내장되어있는 기존 쉐이더나 스탠다드 쉐이더 대신, 쉐이더를 직접 작성한 커스텀 쉐이더를 사용하여야 합니다. 사실, 리플렉션 프로브도 근본적인 형태는 큐브맵입니다. 큐브맵을 미리 프로브별로 미리 구워놓고 가지고 있는 것이지요. 오브젝트의 위치에 따라서 적절한 큐브맵을 리플렉션 프로브로부터 가져오게 되는 것입니다. 

여차 저차 서론이 길어졌습니다만 결론만 말씀드리면 큐브맵을 인스펙터 창에서 지정해서 받아오는 대신 유니티에서 사전 정의해놓은 uniform sampler를 사용하여 큐브맵을 읽어들이면 된다는 것입니니다.

유니티가 리플렉션 프로브로 들어오는 큐브맵을 unity_SpecCube0로 사전 정의해놓았습니다. 이를 사전 정의된 UNITY_SAMPLE_TEXCUBE()로 읽어들입니다. 사실, UNITY_SAMPLE_TEXCUBE()는 texCUBE()를 디파인 한 것 뿐이지만 모바일 외 다른 플랫폼에서도 정상 작동을 하기 위해서는 UNITY_SAMPLE_TEXCUBE()로 사용하는 것이 좋습니다. 어찌되었든 리플렉션 프로브의 큐브맵으로부터 읽어들인 값에다가 리플렉션 프로브의 HDR 계수인 unity_SpecCube0_HDR의 r속성을 곱해주어서 반사 이미지의 최종 결과물을 얻습니다. 서피스 쉐이더를 작성하는 경우에는 surface 아웃풋의 Emission으로 이 값을 설정해두면 됩니다.

o.Emission = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, rv).rgb * unity_SpecCube0_HDR.r;

이름이 unity_SpecCube가 아니라 뒤에 0가 붙었다는 것을 보면 unity_SpecCube1도 존재한다는 것을 유추하실 수 있을 것입니다. unity_SpecCube0_HDR도 마찬가지로 unity_SpecCube1_HDR이 존재합니다. 이를 설명하기 위해서 오브젝트의 Mesh Renderer 인스펙터를 살펴보겠습니다. Reflection Probes 드롭다운을 내려보면 항목 중 Blend Probes라는 항목이 있는 것을 확인 가능합니다. 말 그대로 리플렉션 프로브를 블렌딩 해준다는 의미입니다. 오브젝트가 각기 다른 리플렉션 프로브 영역으로 이동 할 시 리플렉션 이미지가 갑자기 틱 하고 바뀌는 것을 방지하기 위하여 블렌딩을 해주는 것입니다. 

그러기 위해서는 쉐이더에서 큐브맵 두 개를 읽어야한다는 의미가 됩니다. unity_SpecCube1을 unity_SpecCube0와 같이 읽어들인 다음 unity_SpecCube0_BoxMin.w값으로 보간해주면 됩니다. 원래는 이런 블렌딩 기능을 UNITY_SPECCUBE_BLENDING 디파인으로 묶어놓아야 합니다. 하지만 모바일 타겟에서는 UNITY_SPECCUBE_BLENDING 디파인이 꺼져있기 때문에 해당 디파인에 종속적이지는 않게 두었습니다. 사실, 모바일에서 성능을 생각한다면 블렌딩 기능은 사용하지 않는 것이 좋습니다. 다만, 금속 느낌이 강한 재질이여서 이미지가 툭툭 바뀌는 모습이 크게 거슬린다면 블렌딩 연산을 사용할 수 밖에 없을 것입니다.

fixed3 blendTarget = UNITY_SAMPLE_TEXCUBE(unity_SpecCube1, rv).rgb * unity_SpecCube1_HDR.r;

o.Emission = lerp(blendTarget, o.Emission, unity_SpecCube0_BoxMin.w);

그러고 나면 리플렉션 프로브가 자연스럽게 반영되는 모습을 확인 할 수 있습니다. 다음 영상은 리플렉션 프로브를 사용하는 커스텀 쉐이더를 적용한 드론의 모습을 보여주고 있습니다. 

모두 유료 에셋을 사용한 관계로 프로젝트를 공유해드리지 못하는 점 양해 바랍니다. 다만 제작한 쉐이더의 전체 코드는 다음과 같습니다.

Shader "Example/WorldRefl Normalmap Refl" {

Properties{

_MainTex("Texture", 2D) = "white" {}

_BumpMap("Bumpmap", 2D) = "bump" {}

_Metalic("Metalic", Range(0,1)) = 0.5

_MetalicMap("Metalicmap", 2D) = "white" {}

}

SubShader{

Tags{ "RenderType" = "Opaque" }

CGPROGRAM

#pragma surface surf FakeMetal noshadow nolightmap 

struct Input {

float2 uv_MainTex;

float2 uv_BumpMap;

float3 worldRefl;

INTERNAL_DATA

};


struct SurfaceOutputCustom

{

fixed3 Albedo;  // diffuse color

fixed3 Normal;  // tangent space normal, if written

fixed Alpha;    // alpha for transparencies

fixed3 Emission;

fixed3 Metalic;

};


sampler2D _MainTex;

sampler2D _BumpMap;

sampler2D _MetalicMap;

fixed _Metalic;


half4 LightingFakeMetal(SurfaceOutputCustom s, half3 lightDir, half3 viewDir, half atten) {

half NdotL = saturate(dot(s.Normal, lightDir));

half diff = NdotL * 0.5 + 0.5; // Half-Lambert

half4 c;

c.rgb = s.Albedo *_LightColor0.rgb * (diff * atten);

c.a = s.Alpha;

c.rgb *= 1 - s.Metalic;

return c;

}


void surf(Input IN, inout SurfaceOutputCustom o) {

o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;

o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));

float3 rv = WorldReflectionVector(IN, o.Normal).xyz;

o.Emission = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, rv).rgb * unity_SpecCube0_HDR.r;

//#if UNITY_SPECCUBE_BLENDING

fixed3 blendTarget = UNITY_SAMPLE_TEXCUBE(unity_SpecCube1, rv).rgb * unity_SpecCube1_HDR.r;

o.Emission = lerp(blendTarget, o.Emission, unity_SpecCube0_BoxMin.w);

//#endif

o.Metalic = tex2D(_MetalicMap, IN.uv_MainTex).rgb * _Metalic;

o.Emission.rgb *= o.Metalic.rgb;

}

ENDCG

}

Fallback "Diffuse"

}

이는 메탈 느낌에 초점을 맞춘 유사 PBS라고 볼 수 있겠습니다. 물론 PBS는 전혀 아니지만 모바일에서 저렴한 비용으로 PBS의 느낌적인 느낌을 내기에는 적당하지 않나 싶습니다 :)


Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Unity5에서 라이트맵 빌드시 많은 시간이 소요된다는 불편을 호소하시는 분들이 많이들 계십니다. 유니티 또한 이를 인지하고 있으며 라이트맵 빌드를 개선시키기 위해 다방면으로 노력을 하는 중입니다. 이 일례로 현재 작업중인 라이트맵 LAN 분산 빌드(Distributed LAN lighting build)와 점진적 빌드(Progressive Lightmap Baking) 기능을 알려드리고자 합니다. 


라이트맵 LAN 분산 빌드(Distributed LAN Lightmap Build) 

이트맵을 빌드하는 과정은 많은 시간이 소요됩니다. 품질 설정에 따라 빌드 시간이 다르긴 하지만 고품질의 결과를 얻고자 할 수록 더 많은 시간이 필요할 수 밖에 없습니다. 품질을 낮추지 않으면서 빌드 시간을 단축 시키는 유일한 방법은 더욱 고성능의 PC를 사용하는 것입니다. 라고 말씀드린다면 너무 무책임한 말이 되겠지요 :) 아마 대부분은 이미 충분히 고성능의 컴퓨터에서 작업을 하고 계실 것이기때문에 유니티에서는 분산 빌드 시스템 도입을 작업중입니다. 백지장도 맞들면 낫다는 말이 있듯이, 라이트맵을 하나의 컴퓨터로만 빌드하는 것이 아니라 여러 PC에서 함께 연산하는 것입니다. 에이전트 컴퓨터에서 라이트맵을 빌드할 시, LAN으로 연결되어 있는 다른 컴퓨터에 작업 단위를 나누어서 할당해줍니다. 그 후 컴퓨터들은 각자 작업을 거친 후 다시 작업물을 에이전트 컴퓨터로 모아서 최종 마무리를 거치면 라이트맵이 완성 되는 것입니다. 이리하면 컴퓨터가 많을 수록 라이트맵 빌드 시간이 획기적으로 단축 될 수 있게 됩니다. (아래 이미지는 여러 컴퓨터와 연결 된 에이전트 컴퓨터의 빌드 화면을 보여주고 있습니다.)

참고 영상 링크 : https://www.youtube.com/watch?v=W7aaM9M0YWo&feature=youtu.be


점진적 빌드(Progressive Lightmap Baking)

앞서 말씀드렸다시피 현재 라이트맵을 빌드하면 그 결과를 보기 전까지는 긴 시간이 소요됩니다. 씬을 수정 하 긴 시간 라이트맵을 빌드하고 다시 씬을 수정하는 반복 과정을 거치면서 작업하는 것은 매우 고통스러운 시간이 될 것입니다. 이를 보완하기위해 라이팅 인스펙터에서 자동(Auto) 빌드 기능을 제공해주고 있습니다. 하지만, 라이트맵 빌드 자체가 오래걸리기 때문에 완벽한 해결책이 되지는 못합니다. 따라서, 유니티에서는 라이트맵이 저해상도에서 고해상도로 빌드 되는 과정을 미리 보여주며 빌드하는 기능을 작업 중입니다. 마치 GIF 이미지를 열었을 때 저해상도의 이미지로 보여줬다가 로딩됨에따라 원본 해상도의 이미지로 보여주는 과정을 생각하시면 될 것 갑습니다. 이렇게 함으로써 씬 변경 후 대략적인 라이트맵 결과를 빨리 확인하고 다시 씬 구성을 바꾸는 등 씬 작업 이터레이션을 효율을 획기적으로 개선시킬 수 있게 됩니다. (아래 이미지는 빌드 버튼을 누른 후 시간이 지남에 따라 보여지는 모습을 나타냅니다.)

참고 영상 링크 : https://drive.google.com/a/unity3d.com/file/d/0B11iL4IgOgWLUTJWU1ZXOXFheTA/view


계획

다만 앞서 말씀드렸다시피 이 기능들은 아직 작업중입니다. 기본적인 기능은 완료 된 상태이긴 하지만 실제 릴리즈까지 가기 위해서는 아직 손 봐야 할 부분이 많습니다. 따라서 구체적인 스케쥴이나 기능 탑재 버전등의 세부 계획에 대해서는 말씀드리기가 힘듭니다. 다만, 개발자 여러분들의 불편사항을 없애기위해 최선을 다하고 있으며 라이트맵 빌드 또한 개선중에 있다는 것을 말씀드리고자 합니다. 감사합니다.


Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

올해 3월, WebGL의 프리뷰(preview) 버전이 Unity 5.0에 포함되어 릴리즈되었습니다. 그 후, 구글은 크롬 브라우저에서의 NPAPI 지원을 중단하였습니다. (기본값) 이는 더 이상 크롬 브라우저에서 Unity Web Player를 실행할 수 없다는 것을 의미합니다. 때문에, 그 대안으로 WebGL을 사용해야만 합니다. 이미 일부 개발자들이 WebGL로 게임을 배포하였습니다만, 기존 Web Player 게임을 WebGL로 포팅하는 데 애로 사항을 겪고 있는 것으로 알고 있습니다. 이는 Web Player와 WebGL은 완전히 다른 플랫폼이기 때문입니다. 웹 브라우저에서 게임을 돌린다는 궁극적인 목적은 동일하지만, 기반 기술은 완전이 다른 플랫폼입니다. 또한 WebGL은 매우 빠르게 성장하고 있기 때문에 시간이 지날 수록 지원 기능들과 상황이 시시각각 다를 것으로 생각합니다.

이러한 이유로, WebGL에 대한 우리의 계획에 대한 이야기를 할 까 합니다. 이 글은 유니티 공식 포럼의 WebGL 로드맵 글을 번역 및 요약한 글입니다.

주의 : 이 글의 모든 정보는 현재의 WebGL 생태계와 계획에 의존하고 있습니다. 이 모든 것들이 미래에는 어떻게 바뀔 지 장담하지는 못합니다. 기능들에 대한 구체적인 릴리즈 날짜를 보장하지는 못합니다. 또한, 일부는 진행하지 않기로 결정 할 가능성도 있습니다. 따라서, 이 글에서 버전이 언급되는 것은 현재의 계획일 뿐이며 미래를 보장하는 것은 아닙니다. (적고보니 보험광고같은 느낌이;;;)


Unity 5.1의 WebGL

5.1에서의 가장 큰 이슈중 하나는 5.0에서 발견 된 버그들을 수정하는 것입니다. 게다가, IL2CPP팀은 지난 몇 개월 간 IL2CPP의 무수히 많은 문제점들을 고치느라 매우 바쁜 시간을 보냈습니다. WebGL은 스크립트 런타임으로 IL2CPP를 사용하다보니 이로 인해 WebGL 플랫폼의 품질도 좋아지게 되었습니다.

또한, Unity 5.1에서의 WebGL 사용자들에게 중요한 이슈 중 하나는 텍스쳐 압축입니다. 텍스쳐 압축을 통해서 전송량 및 용량을 절약하고, 런타임중에 압축을 풀어 GPU 친화적인 DXTn 텍스쳐로 사용합니다. 일반적으로 텍스쳐가 사이즈를 차지하는 주 된 에셋이기 때문에 이를 통해서 WebGL 컨텐츠의 배포 용량을 획기적으로 줄일 수 있게 됩니다.


"프리뷰(Preview)" 레이블

요즘, 프리뷰(preview) 레이블을 언제 제거할 것인가에 대한 질문을 많이 받습니다. 하지만 이에 대해서는 확답을 드릴 수가 없는 상황입니다. 우리는 플랫폼이 안정화 되었다고 판단되어야만 프리뷰 레이블을 제거할 것입니다. 이는 유니티에서의 기술적인 문제 뿐만 아니라 WebGL의 기술 생태계 역시 충분히 준비된 시점을 의미합니다. 그러기 위해서는 브라우저의 개발사들이 우리가 원하는 기능을 구현해주어야만 합니다. 때문에 우리가 기능을 다 구현했다고 프리뷰 레이블을 제거하는 것은 말이 안되는 행위입니다. 우리는 주요 웹 브라우저 업체와 이야기중이고, 우리가 원하는 기능을 위해 최선을 다하고 있다고 믿고 있습니다.


메모리 이슈

현재의 Unity WebGL에서의 가장 큰 이슈중 하나가 메모리 이슈입니다. 특히 크롬 브라우저에서 Unity WebGL을 돌릴 경우 메모리가 낭비되는 현상이 있습니다. 

- 유니티의 WebGL 콘텐츠를 위해서는 연속된 공간의 메모리 블럭이 필요합니다. 이 사이즈는 WebGL Player 셋팅에서 설정할 수 있습니다. 이 사이즈는 리소스를 로드하면서 필요한 메모리 공간 만큼 충분히 확보되어야 합니다. 메모리 프로파일러를 통해서 공간이 어떻게 쓰이고 있는 지 디버깅 가능합니다. 이는 웹브라우저의 힙(heap) 메모리에 연속적인 공간이 필요합니다. 브라우저의 메모리가 낮게 잡혀있거나 힙이 파편화(fragmented)되어 있는 경우 메모리 할당이 실패해버립니다.

- 브라우저는 자바스크립트(JavaScript)를 파싱하는 과정에서 많은 메모리를 사용합니다. Unity WebGL Player로부터 파생된 자바스크립트 코드는 다른 것에 비해 매우 큰 메모리 공간을 소모합니다. 그리고, 자바스크립트 VM은 이러한 코드들을 분석하는 과정에서 매우 많은 양의 메모리를 필요로합니다. 득히, 크롬 V8은 때때로 이러한 이슈때문에 크래시가 일어나곤 합니다. 반면 파이어폭스는 asm.js로 AOT 컴파일을 수행하기 때문에 비교적 적은 메모리를 사용합니다.

이러한 이유들로 메모리 문제는 가장 큰 이슈이긴 합니다만 고치는 것이 쉽지가 않습니다. 현 시점에서는 우리가 할 수 있는 것은 결과 코드 사이즈를 줄이는 것 뿐입니다. 우리는 사내 워크샵에서 이러한 문제를 고민하였으며, 배포 사이즈를 1.2M까지 줄이는 데 성공하였습니다. 코드 스트리핑을 강화하고 컴파일러 설정을 개선하고 불필요한 코드를 제거 하는 등의 작업을 하였으며 이는 계속 개선해 나갈 것입니다. 이러한 개선 사항들은 5.2에 적용될 것으로 예상합니다. 또한 빌드에 포함된 코드 모듈을 시각적으로 확일 할 수 있는 툴을 만들고 있습니다. 얼마나 많은 코드들이 어떤 것에 의해 생성이 되었는 지 등을 확인할 수 있게 됩니다. 이 툴은 빨라야 5.3은 되어야 적용 될 것입니다. 이를 통해서 빌드 결과 사이즈를 줄일 수 있을 것입니다. 결과물의 코드 사이즈가 줄어들면 코드 파싱 시간이 줄어드는 만큼 구동 시간이 빨라지게 될 것입니다.

궁극적으로는, 브라우저의 기술 발전이 메모리 이슈를 훨씬 더 많이 해결할 수 있을 것으로 기대하고 있습니다. 모든 브라우저들이 64비트로 만들어지고 있으며 더 큰 메모리 공간을 사용할 수 있게 됩니다. 또한, 더 중요한 것은, 모질라, 구글, 마이크로소프트가 웹어셈블리(WebAssembly)라 불리우는 신기술을 만들고 있다는 것입니다. 이는 asm.js를 바이트코드(ByteCode) 포맷으로 패킹함으로써 매우 효과적으로 네이티브 코드로 컴파일 될 수 있게 합니다. 이를 통해서 로드 시간과 메모리 오버헤드와 배포 사이즈를 획기적으로 개선시킬 수 있습니다.


데이터 압축

앞서서 메모리 이슈를 언급했 던 것 처럼, 배포 사이즈도 Unity WebGL에서 중요한 이슈입니다. 배포 사이즈는 다운로스 시간과 메모리 사용량에 영향을 미칩니다. 사용할만한 다운로드 속도가 나오려면 데이터를 압축한 상태로 전송해주어야 합니다. 현재로써는 http 프로토콜이 지원하는 gzip 압축에 의존하고 있습니다. 하지만, 이는 종종 서버 사이드의 작업이 필요로하기때문에 불편함이 따릅니다. 게다가, gzip은 현대의 압축 알고리즘에 비해 효율성이 떨어집니다.

미래에는(스케쥴상으로는 Unity 5.3), Unity 자체적으로 에셋 데이터 파일 압축을 모든 플랫폼에서 지원할 것입니다. http 압축에 의존해있던 것을 제거하고 자체적인 압축을 코드상으로 구현할 것입니다. 이로써 WebGL 콘텐츠를 더 빨리 받아볼 수 있게되고, 데이터가 메모리상에 압축된 상태로 머무르는 것이 가능해집니다. 이로 인해서 에셋 로딩 속도도 빨라지고 메모리 대역폭도 줄어들게 됩니다.


성능(Performance)

작년에 작성 한 WebGl 벤치마크 포스팅에서 브라우저와 네이티브 런타임의 성능 비교를 하였습니다. 어떤 영역에서는 괜챦은 성능을 보였었고, 어떤 영역에서는 훨씬 큰 성능 차이를 보여주었습니다. 우리는 이러한 성능 차이를 줄이고자합니다. 큰 성능 차이는 SIMD 및 멀티-스레딩(multi-threading)을 활용함으로써 줄일 수 있습니다. 애석하게도 현재 시점의 WebGL에서는 이들을 사용하는 것이 불가능하지만 다음과 같이 개선 될 예정입니다.

- SIMD : SIMD.js를 통해서 자바스크립트에서 SIMD를 지원할 수 있습니다. 모질라, 구글, 마이크로소프트 모두 이를 지원할 계획을 가지고 있습니다. 현재 다른 플랫폼들이 SIMD를 이용해서 퍼포먼스를 향상시키듯이 WebGL도 SIMD를 활용하면 퍼포먼스가 향상될 것입니다.

- 공유 배열 버퍼(Shared Array Buffers) : 공유 배열 버퍼(Shared Array Buffers)는 WebWorker(자바스크립트에서의 멀티스레드와 같은 역할)가 메모리를 공유할 수 있게 합니다. 이로 인해 기존 멀티스레드 코드를 자바스크립트로 컴파일하는 것이 가능해집니다. 모질라에서는 이미 Unity WebGL 콘텐츠를 멀티스레딩으로 돌리는 것에 성공하였고, 구글 역시 공유 배열 버퍼를 지원할 계획이라고 발표하였습니다.


모바일 지원

모바일에서 WebGL을 지원할 것이냐는 질문도 많이 받고 있습니다. 현재 우리는 모바일에서 구동하는데 방해가 되는 작업을 따로 하고 있지는 않지만, 모바일 최적화를 위한 작업도 따로 하고 있지는 않습니다. 때문에, 고성능의 최신 안드로이드 기기에서는 잘 돌아가겠지만 대부분의 보급형 기기에서는 그렇지가 않을 것입니다. 앞서 언급한 성능 향상 작업들이 모바일에서도 효과를 발휘한다고는 기대하지 않고 있습니다. 때문에, 모바일 지원에 대한 일정에 대해서는 말씀 드릴 것이 없는 상황입니다.


빌드 시간

또 다른 이슈로는 WebGL 빌드가 오래 걸린 다는 것이 있습니다. 모질라는 현재 엠스크립튼(Emscripten) 컴파일러 툴체인을 네이티브 코드로 옮기는 작업을 진행중입니다. 이를 통해서 빌드 시간이 상당히 개선될 것으로 기대하고 있습니다. 이미 Unity 5.1에서는 자바스크립트 최적화가 네이티브로 옮겨진 버젼의 엠스크립튼(Emscripten)이 탑재되서어 5.0에 비해 빌드 속도가 개선되었습니다.


오디오

다른 플랫폼들은 오디오 시스템을 FMOD를 이용하는 반면, 현재의 Unity WebGL은 Web Audio를 기반으로 자체적인 오디오 시스템을 구현하였습니다. 때문에, 현재로써는 기본적인 플레이백(playback)이나 볼륨 및 피치(pitch) 조절 등이 지원 될 뿐 그 이상의 고급 기능은 지원되지 않습니다. 이러한 상황은 Unity WebGL의 스레드 지원이 가능하기 전 까지는 변치 않을 것입니다. 스레드가 지원되면 FMOD 라이브러리를 WebGL로 컴파일 할 수 있게되고 원하는 모습에 가까워 질 수 있습니다. 하지만, Web Audio(“Audio Workers”)의 스펙인 오디오 데이터 워커 API 또한 필요합니다. 하지만 이 기능이 구현 된 브러우저는 아직 존재하지 않습니다. 따라서, 완전한 오디오 기능이 지원되려면 오랜 시간이 걸릴 것으로 예상합니다.


그래픽

WebGL은 OpenGL ES 2.0에 기반을 두고 있는 자바스크립트 API입니다. OpenGL ES 2.0은 많은 제약 사항이 있기 때문에 다른 플랫폼에 비해 제한된 그래픽 기능을 가지고 있음을 의미합니다. 또한, 유니티는 현재 어떤 그래픽 API에서 어떤 그래픽 기능을 사용하는 지에 대해 결정하는 부분이 하드코딩으로 되어있습니다. 이 과정 중에서 OpenGL ES 2.0은 모바일에서 돌아간다고 가정하고 있습니다. 하지만 WebGL은 고성능 데스크톱 GPU에서 구동될 수가 있으므로 이러한 가정은 더 이상 유효하지가 않습니다. 이로 인해 원래 가능한 것 보다는 시각적인 퀄리티가 떨어지는 결과를 초래합니다. 특히 스탠다드 쉐이더와 그림자등이 대표적인 예가 될 것입니다. 우리는 이러한 하드코딩을 걷어내고 쉐이더 품질 등을 프로젝트 별 설정할 수 있는 기능을 만들고 있습니다.

또한, GDC 2015에서 우리는 WebGL 2.0(OpenGL ES 3.0 기반)에서 돌아가는 Unity5의 프로토타입을 시연한 바 있습니다. 이는 WebGL의 그래픽 성능을 한 단계 끌어올린 것입니다. Unity 5.2에서는 WebGL 2.0 지원이 실험적으로 적용될 것입니다. 다만, 브라우저들이 실제적으로 이 API를 올해 안에는 릴리즈 버전에 포함하지는 않을 것으로 생각합니다.


브라우저 지원

현재로써는, Unity WebGL은 크롬, 파이어폭스, 사파리를 지원합니다. 주요 브라우저 중 마이크로소프트의 브라우저는 제외 된 상태입니다. 인터넷 익스플로러의 현재 버젼이 WebGL을 지원하긴 하지만 Web Audio에 대한 지원이 빠져있습니다. 또한, 성능도 썩 좋지 않다보니 우리의 공식 지원 대상에서 제외되었습니다.

마이크로소트의 새로운 브라우저인 Edge에서는 이러한 점이 개선됩니다. Edge는 IE를 대체하여 Windows10에 기본 브라우저로 탑재됩니다. Edge에서는 Web Audio가 탑재되고 asm.js가 지원되어서 높은 성능으로 Unity 콘텐츠를 구동시킬 수 있습니다.


비디오

Unity의 MovieTexture 클래스는 현재 WebGL에서 지원되지 않습니다. MovieTexture는 현재 WebGL의 오디오 솔루션에서 오디오 플레이백이 불가능한 관계로 현재로써는 이에 대한 계획이 없는 상태입니다. 게다가, 브라우저의 html5 비디오 기능을 이용하면 완벽한 비디오 텍스쳐 솔루션을 Unity WebGL에서 손쉽게 이용 가능합니다.


네트워킹

현재 System.IO.Sockets.*과 UnityEngine.Network.*는 WebGL에서 동작하지 않으며 앞으로도 마찬가지 일 것입니다. 이는 플랫폼에서 보안상의 문제로 IP 소켓에 직접 접근하는것이 불가능하기 때문입니다. 대신, WWW는 이용 가능합니다. 또한, 자바스크립트를 이용하여 Web Socket이나 WebRTC를 통합하는 방법도 있습니다. 아니면, Unity 5.1부터 내장되어있는 멀티플레이어 기능을 활용할 수도 있고, 포톤(Photon)이나 스마트폭스서버(SmartFoxServer)같은 서드 파티 라이브러리를 활용하는 방법도 있습니다. 이들 모두 WebGL에서 WebSocket을 이용합니다.


쓰레드

앞서 성능 섹션에서도 언급하였듯이, 브라우저가 공유배열버퍼(Shared Array Buffer)기능을 지원하게되면 우리는 내부 엔진 코드에 멀티 스레딩을 적용할 예정입니다. 또한, 최종적으로는 사용자 System.Threading.*등을 이용하여 사용자 코드에서 멀티스레딩을 활용할 수 있도록 하는 것이 우리의 목표입니다. 하지만, 공유배열버퍼(Shared Array Buffer)는 GC가 필요한 일부 기능들을 제공하지 않는 등 꽤 까다로운 문제들이 있습니다. 우리는 이 문제를 풀기위한 고민을 계속 하고 있으며 언젠가는 기능이 구현 될 것입니다. 다만, 엔진 내부에 멀티스레딩이 적용 된 이후에나 될 것입니다.



Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


Variation

게임에는 기본적으로 플레이어가 존재하고, 그 플레이어에게 방해가 되는 장애물이 존재합니다. 퍼즐류가 아닌 이상 이러한 장애물은 몹 또는 몬스터의 형태로 나타나게 되고, 게임의 재미를 좌우하는데 결정적인 역할을 하는 존재가 됩니다.

이미지 : 엔젤 스톤

이러한 몹의 종류가 적으면 사용자는 금새 지루해지게 됩니다. 때문에, 개발자는 매우 다양한 모습과 패턴을 갖도록 만들어야 합니다. 예를 들어 좀비 형태의 몹을 만든다 가정했을 때, 모자를 쓴 좀비, 창을 든 좀비, 뚱뚱한 좀비 등등 좀비라는 큰 카테고리 안에서도 다양한 모습의 좀비를 만들어내야 합니다. 이를 흔히 "다양화(variation)시킨다" 라고 표현합니다.

이미지 : PVZ

하지만 이 때 문제는 리소스의 용량 크기입니다. 완전히 각각의 종류마다 완전히 새로운 리소스로 따로 따로 제작하게되면 리소스의 크기가 커질 수 밖에 없게 됩니다.때문에, 이러한 다양화(variation) 작업을 거칠 때 리소스를 최소한으로 사용하는 방법을 사용해야만 합니다.


부품 모듈화

그 방법 중 하나가 각각 파츠별로 모듈화 시키는 방법입니다. 각 파츠별로 부품 모듈을 만들어 놓고 프라모델이나 레고처럼 모듈을 조립하여 다양한 모습을 만들어 내는 것입니다. 예를 들어 로보트를 팔, 다리, 몸통, 총구 등으로 나누어 여러 종류로 제작한 뒤 이를 조립하여 다양한 종류의 로보트를 만들어 내는 것입니다. 보통 로보트나 비행기 등 강체로 이루어진 것 들이 이러한 조립식으로 많이 만들어집니다. 에셋 스토어에서도 마찬가지로 이러한 조립식 에셋들을 많이 찾아볼 수 있습니다. 

이미지 : 에셋스토어


텍스쳐 변경

또 다른 방법으로는 텍스쳐만 바꾸는 방법도 존재합니다. 동일한 메시에 텍스쳐만 바꿔서 다른 모습을 연출해주게 되는 것입니다. 주로 인간형 케릭터에게 의상을 다양화 시킬 때 이러한 방법을 사용합니다.  

이미지 : 에셋스토어


파츠 컬러

이 글에서 소개할 또 다른 방법으로는 파츠 컬러를 변경하는 방법입니다. 앞서 방금 언급한 텍스쳐를 바꾸는 방식이나 메시 모듈들 조립하는 방식과는 달리, 메시와 텍스쳐는 교체하지 않고 하나의 텍스쳐와 하나의 메시만 사용하는 방식입니다. 때문에 앞서 언급한 방식들에 비해서 리소스를 크게 아낄 수 있습니다. 다만, 텍스쳐를 통채로 바꾸는 것이 아니기 때문에 전체적인 패턴과 모양은 그대로 유지하기 때문에 한계가 있습니다. 대신 전체적인 혹은 부분적인 색상을 바꿀 수 있기 때문에 주로 의상의 염색 시스템에서 활용되기도 합니다. 

이미지 : 레이더즈

또한 다양화 작업 말고도 RTS나 MOBA 등 대전 게임에서 팀 구분을 위한 색상으로 활용될 수도 있습니다.

이미지 : 스타크래프트2

이 글에서는 이러한 파츠 컬러 방식을 구현하는 것에 대하여 자세히 설명을 해볼까 합니다. 기본 원리는 간단합니다. 메시의 기본 albedo 색상 외 컬러를 변경할 마스크 영역을 준비합니다. 단순한 파츠 컬러 적용 여부를 나타내는 크레이스케일 값이기만 하면 되기 때문에 albedo 텍스쳐의 알파 채널에 이를 활용하면 됩니다. 물론, 대신 불투명 오브젝트에만 적용이 가능해진다는 제약은 있습니다.

그 예로 다음과 같은 로보트 Kyle에 이를 적용하여 보겠습니다.

이 모델은 불투명으로 만들어져 있어서 실제로는 다음과 같은 albedo 텍스쳐의 RGB만 사용합니다.

이 텍스쳐의 Alpha 채널을 다음과 같이 칠하여 파츠 컬러의 마스킹으로 사용하도록 합니다. 다음 이미지는 GIMP툴에서 보여주는 Alpha 채널의 모습입니다. 검은색이 1을 나타내고 회색의 체크무늬가 0인 영역을 의미합니다.

이 Alpha 채널의 역의 값을 마스킹으로 사용하여 (위 이미지의 회색 체크무늬 영역이 파츠 컬러가 되도록) 적용하는 것이 최종 목표입니다. 샘플 프로젝트를 올려두었습니다. 이 링크를 통해 다운받으셔서 확인해보실 수 있습니다.

쉐이더가 적용 된 마테리얼에서 Parts Color 색상을 변경할 수 있습니다.

이 색상을 바꾸면 이에 따라서 로봇의 파츠 컬러가 변경되어 보여집니다.


이 방식은 원리도 간단하기 때문에 쉐이더 코드도 크게 복잡하지 않습니다. 샘플 프로젝트에 포함 된 LambertUseColorPart.shader를 살표보도록 하겠습니다.

27번째 라인에서 albedo 텍스쳐의 값을 읽은 뒤 29번째 라인에서 alpha 채널의 값을 뒤집어줍니다. 이는 작업자의 성향에 따라 파츠 컬러 적용 값을 1로 둘것인가 0으로 둘 것인가에 따라 다릅니다. 머티리얼의 Invert Parts Color Mask 체크 여부에 따라 수행 여부가 달라집니다.

35번째 라인이 이 쉐이더의 핵심 구문입니다. 파츠컬러의 마스킹 값에 따라서 흰색과 지정된 색상 값을 보간합니다. 그후 그 값을 albedo와 곱하여 최종 albedo 값을 결정합니다. 결과적으로, 마스크가 1이면 albedo에 지정 컬러를 곱한 결과가 됩니다. 반대로 마스크가 0이면 원래의 albedo 색상을 그대로 사용합니다.

만일 머티리얼에서 Interpolated Parts Color가 체크되어 있다면 35번째 라인 대신 33번째 라인이 수행됩니다. 이는 albedo값과 지정 색상 값을 바로 보간합니다. 이는 흰색과 보간한 뒤 albedo에 곱해주는 결과와는 미묘한 차이를 가집니다.

다음 로보트의 몸통 이미지는 각각 33번 라인과 35번 라인을 수행 했을 때의 차이를 보여주고 있습니다. 첫 번 째 이미지는 35번 라인이 수행된 모습입니다. albedo와 곱하여 사용한 결과이기 때문에 albedo 자체에 새겨놓은 AO 라이팅이나 얼룩등이 그대로 살아있습니다. 만일 텍스쳐 자체에 라이팅이나 AO등을 새겨놓는 식으로 작업한다면  Interpolated Parts Color가 비활성화 되어야합니다.


이 두 번 째 이미지는 그 반대로 33번 라인이 수행된 모습입니다. albedo와 바로 보간해서 사용하기 때문에 마스크가 1인 영역은 albedo 텍스쳐의 모습을 완전히 없애버립니다. 첫 번 째의 이미지와는 달리 몸통에 페인트가 덧 칠해진 것 같은 느낌입니다. PBS 대응 텍스쳐는 albedo에 AO 및 라이팅이 포함되어서는 안되기 때문에, 만일 PBS 대응으로 작업하는 이미지라면 이 모습이 용이할 수도 있습니다. (예를 들어서 금속 물질 위에 칠해져있는 페인트가 벗겨져 있는 모습이라면) 



한계

이 쉐이더는 알파채널을 이용하여 한번에 한가지 색상만 적용합니다. 웬만한 경우에서는 한 가지 색상만 적용해줘도 크게 무리는 없습니다. 만일 푸품 모듈화 방식과 같이 사용한다면 더 다양한 조합을 만들 수도 있을 것입니다.

하지만 단일 메시만 사용하는 경우라면 단일 색상만 적용 가능 한 것이 조금 답답할 수도 있겠습니다. 그러한 경우에는 albedo 텍스쳐의 alpha 채널을 마스크로 활용하는 것이 아니라, 아예 마스크 텍스쳐를 별도로 제작하면 더욱 다양하게 활용이 가능합니다. 색상을 4가지를 지정하고 마스크 텍스쳐의 r,g,b,a 채널 모두 각각의 마스킹으로 활용하면 4가지 색상을 적용할 수 있습니다. 다만 추가적인 텍스쳐가 사용되므로 약간의 성능 감소는 있을 수 있습니다. 그렇다 하더라도 잘만 조작하면 적은 텍스쳐 메모리만으로도 훨씬 다양한 다양화(variation) 효과를 볼 수 있기 때문에 충분한 가치가 있습니다.

쉐이더 작성이 익숙하신 분이라면 쉐이더를 직접 수정하여 사용하시면 되고, 그렇지 않은 분이시라면 에셋 스토어에서 받아서 사용하시면 됩니다. 이 에셋이 제가 설명드린 것과 비슷한 작동을 수행하는 쉐이더를 사용하고 있습니다. ( 사실, 컬러 4개 지정하는 쉐이더를 올릴 까 했는데, 이 에셋이 유료로 판매되는 것을 보고나니 상도덕(?) 상 그러지를 못하겠네요;;)

이미지 : 에셋스토어


샘플 프로젝트 다운로드 링크 : https://drive.google.com/file/d/0B70AOyGiQJsGY2dEakU4Y2Q4WWs/view?usp=sharing



Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

유니티 5.1부터는 unity analytics가 기본적으로 통합되었습니다. 더 이상 플러그인을 사용하지 않아도 됩니다. 오히려 기존 플러그인은 제거하시는 것을 추천합니다. 다만 이로 인해서 사용법이 조금 바뀌었습니다.https://analytics.cloud.unity3d.com > 좌측 메뉴 INTEGRATION > 우측 상단의 5.1 에서 확인 가능합니다.

이 내용을 참고로 바뀐 점을 요약해보자면 다음과 같습니다.

우선, 프로젝트 아이디를 터이상 코드로 적지 않습니다. 대신 플레이어 셋팅에 아이디를 적게 되었습니다. 따라서 UnityAnalytics.StartSDK() 함수가 제거됩니다.

또한, 인터페이스 이름이 변경되었습니다. 기존 UnityAnalytics 에서 UnityEngine.Analytics 으로 변경되었습니다. 따라서 커스텀 이벤트를 사용하고 계신다면 이 부분 코드를 바꿔주셔야합니다.

이 이외에는 바뀐 부분이 크게 없어서 이 부분만 신경써주시면 무리 없을 듯 합니다.





Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

"모바일 기기의 Tile Based Rendering(타일 기반 렌더링)과 유니티에서의 주의 사항 #1 : TBR의 이해"에서 이어지는 글입니다. 


앞선 글에서 설명드린 바와 같이 모바일에서는 타일 단위로 쪼개서 렌더링하는 방식을 사합니다. 그러다보니 전통적인 렌더링 방식에서와는 조금 다른 주의 사항이 몇 가지 존재합니다. 


알파블렌딩(Alpha Blending) VS 알파테스트(Alpha Test)

전통적으로 데스크톱 게임의 리소스에는 알파블렌딩보다 알파테스트의 사용이 권장되어왔습니다. 불투명한 철망이라든가 찢어진 옷감같은 경우는 불투명하기때문에 비싼 블렌딩 연산을 사용하기보다는 알파테스트를 사용함으로써 픽셀 연산도 절약하자는 의도였습니다. 하지만 모바일의 TBR(Tile Based Rendering, 타일 기반 렌더링)에서는 정반대로 알파테스트보다는 알파블렌딩의 사용이 권장됩니다. 

알파테스트처리를 하기 위해서는 픽셀쉐이더에서 동적분기(if문)가 사용됩니다. 데스크톱에서는 쉐이더의 동적 분기가 고속으로 처리되지만, 모바일에서는 동적 분기 성능이 취약합니다. 때문에 알파테스트는 쉐이더의 성능윽 하락시키는 원인이 됩니다.

게다가, TBDR(Tile Based Deferred Rendering, 타일 기반 지연 렌더링)에서는 픽셀 차폐의 고속 처리를 깨트립니다. 앞서 언급했다시피 TBDR에서는 여러 드로우콜의 버텍스 쉐이더 결과를 모아두었다가 은면 제거(Hidden Surface Removal)를 거친 뒤 실제 보이는 픽셀만 처리합니다. 하지만 이는 알파테스트를 사용하지 않는 완전한 불투명메시일 경우에만 해당됩니다. 알파테스트를 사용하면 버텍스 처리 단계에서는 해당 폴리곤이 차폐 되는지의 여부를 판단할 수 없기때문에 Deferred 처리를 깨트릴 수 밖에 없게됩니다.

유니티에서는 이를 방지하기위해서 완전 불투명 오브젝트들을 모두 렌더링처리한 후에 알파테스트 오브젝트들을 렌더링합니다. 따라서 알파 테스트를 제한적으로만 사용한다면 그렇게 치명적이지는 않습니다. 하지만 애초에 TBDR 칩셋의 구조가 알파테스트 처리에 적합하지 않기 때문에 알파테스트를 사용하지 않는 것이 좋습니다. 그런 이유로, 유니티의 내장 쉐이더 중 Mobile 카테고리에는 알파 테스트 쉐이더가 존재하지 않습니다.

반면에, 알파블렌딩은 데스크톱에 비해서 고속으로 처리가됩니다. 알파블렌딩과정은 출력 내부적으로 대상 버퍼의 읽기/쓰기가 발생합니다. 데스크톱에서는 DRAM에 존재하는 프레임버퍼 전체에 접근해야하기때문에 높은 대역폭을 잡아먹게됩니다. 하지만 TBR에서는 이 처리가 타일 단위로 이루어지고 칩 내부에 존재하는 메모리에서 이루어지므로 고속으로 처리됩니다.


오버드로우

다만 명심해야 할 것은 알파 블렌딩 처리 자체가 빠르다는 것일 뿐이지 오버드로우에서 자유로와진다는 것은 아닙니다. 예를 들어서 넓은 영역의 파티클을 높은 밀도로 뿌리는 것은 여전히 오버드로우 문제를 일으켜서 성능 저하로 직결됩니다. 모바일은 쉐이더 성능이 기종에 따라 천차만별이므로 오버드로우로 인해서 쉐이더 싸이클이 낭비되는 것은 치명적인 문제가 됩니다. 웬만하면 불투명 오브젝트 위주로 리소스를 만들기를 권장합니다. 알파 블렌딩 오브젝트 또는 파티클은 오버드로우를 최대한 피해서 사용하시기를 권장합니다.


로우 폴리곤

너무나 당연한 이야기라 뜬금없어 보일수도 있겠지만 많은 폴리곤을 처리하면 성능이 하락합니다. 게다가, TBDR에서는 많은 폴리곤 처리의 부담이 더욱 큽니다. 앞서 설명드린 버텍스 쉐이더의 결과물들을 담아두는 파라미터 버퍼(Parameter Buffer)의 크기는 당연하게도 무한하지 않습니다. 따라서 이 버퍼가 넘쳐버리면 더 이상의 버텍스 쉐이더 결과물을 받아들이지 못하고 버퍼를 비워줘야합니다. 이 버퍼를 비워주기 위해서는 타일의 픽셀 처리 후 프레임버퍼로 출력하는 사이클을 거쳐야합니다. 때문에, 이론상으로는 TBDR에서는 픽셀의 오버드로우가 발생하지 않아야하지만, 현실적으로는 폴리곤이 많을수록 오버드로우가 발생하게 발생하게 됩니다. 그러므로 오브젝트의 렌더링 퀄리티를 높여야한다면 버텍스를 늘리는 것 보다는 픽셀쪽 연산을 늘리는 것이 오히려 이득일 수도 있습니다.


렌더 텍스쳐(Render Texture)

렌더 텍스쳐를 사용하면 유니티 내부적으로 렌더 타겟(Render Target)을 변경하는 행위를 거치게됩니다. 이러한 렌더타겟을 변경하는 행위는 데스크톱에서도 성능을 잡아먹는 행위가 됩니다. 렌더 타겟을 바꾸기위해서는 CPU가 GPU를 대기하는 과정을 거치게되면서 CPU와 GPU의 병렬 관계가 잠기 깨지는 현상이 발생하기 때문입니다. 

게다가, TBDR에서는 더욱 치명적인 행위가 됩니다. 렌더 타겟을 변경할 시에는 현재 파라미터 버퍼에 쌓여있는 데이터들을 모두 처리해주고 프레임버퍼에 출력합니다. 그 후 다음 렌더 타겟을 위해서 파라미터 버퍼를 비워줍니다. 이런식으로 렌더 타겟을 바꿀 시 deferred 사이클을 추가적으로 처리해줘야 합니다. 때문에 유니티의 카메라에서 타겟 텍스쳐(Target Texture)로 렌더 텍스쳐를 사용하는 경우에는 TBDR의 효율이 떨어지게 됩니다.

이미지 후처리 효과(Image post process Effect)

최근 디바이스들은 컬러 그레이딩이나 블룸 효과 등 이미지 후처리들을 사용할 수 있을 만큼 성능이 좋아졌습니다. 하지만 이러한 이미지 후처리들을 너무 남발해서 사용하면 안되고 필요한 것만 선택적으로 사용해야 합니다. 

우선, 이미지 후처리들은 내부적으로 렌더 타겟을 변경하는 행위를 합니다. 하지만, 더 큰 문제는 대역폭입니다.  물론 픽셀 처리 능력도 관건이지만 대역폭이 더욱 큰 문제가 됩니다. 이미지 후처리들은 현재 렌더링 한 결과를 담고있는 렌더 타겟을 픽셀쉐이더의 입력 텍스쳐로 가져옵니다. 이 때 입력받는 텍스쳐는 칩 내부에 있는 타일이 아니라 공용 메모리에 있는 렌더 타겟을 가져오기때문에 엄청난 대역폭을 잡아먹게 됩니다. (예 : 1080p) 그러므로 이미지 후처리는 신중하게 사용해야 합니다.


카메라 클리어(Clear)

예전의 데스크톱 그래픽카드에서는 한 프레임의 렌더링을 시작하기 전 일부러 화면을 클리어해주지 않고 렌더링을 시작하는 경우도 있었습니다. 하지만 현대의 데스크톱 그래픽카드에서는 반드시 클리어를 해주어야만 하드웨어의 고속 처리를 지원받을 수 있습니다. 이는 모바일 기기의 TBR에서도 마찬가지입니다. 클리어를 수행하여 칩 내부의 버퍼들을 비워줘야만 이후 렌더링 과정을 고속으로 처리할 수 있습니다. 따라서, 유니티의 카메라에서 Clear Flag를 Don’t clear로 두는 것은 데스크톱에서나 모바일에서나 웬만해서는 권장되지 않습니다. 


MSAA

데스크톱에서는 MSAA가 매우 큰 부담이 됩니다. 역시 마찬가지로 대역폭이 가장 큰 원인입니다. 예를 들어 1080p해상도의 화면을 MSAA 2X로 처리하려면 2160p만큼의 대역폭이 필요해집니다. DRAM으로부터 그만큼의 대역폭을 요구한 다는 것은 매우 큰 부담이 되는것입니다. 하지만 TBR에서는 이 역시 칩 내부의 타일에서 이루어집니다. 16x16 혹은 32x32정도에 불과한 타일로 MSAA처리해주는 것은 그리 부담이 되지 않습니다. 

유니티에서 MSAA를 사용하기위해서는 퀄리티 셋팅에서 Anti Aliasing을 2 혹은 4로 선택해주면 됩니다. 다만 개인적으로는, 매우 높은 DPI를 자랑하는 대부분의 모바일 기기에서 안티 앨리어싱이 굳이 필요할 지는 모르겠습니다 :)


프로파일링

유니티5부터 프레임 디버거(Frame Debugger)가 추가되어서 프레임 별 렌더링 과정을 디버깅해볼 수 있게 되었습니다. 이를 통해서 오브젝트의 렌더링 과정이나 배칭 현황을 손쉽게 확인 해 볼 수 있게 되었습니다.  

이미지 출처 : http://docs.unity3d.com/Manual/FrameDebugger.html

하지만 애석하게도 유니티의 프레임 디버거만으로는 드로우콜 별 GPU 퍼포먼스나 세부 상태를 확인하기는 힘듭니다. 다행히도, 칩셋 벤더마다 렌더링 과정을 세부적으로 프로파일링을 해볼 수 있는 툴을 제공해주고 있습니다. 아드레노 칩셋은 아드레노 프로파일러를 통해서, 말리 칩셋은 말리 프로파일러나 DS-5를 통해서, 아이폰은 XCODE를 통해서 프로파일링을 해볼 수 있습니다.

이미지 출처 : http://www.slideshare.net/ozlael/graphics-opt-ndc

다만 문제가 하나 있습니다. TBR 방식을 사용하는 칩셋은 콜 별 설능을 확인하는데 어려움이 없습니다. 하지만 TBDR 방식을 사용하는 칩셋은 콜 별 성능을 직관적으로 확인하는게 사실상 불가능하다는 것입니다. TBDR은 앞서 언급했다시피, 드로우콜 발생 시 픽셀 쉐이더를 즉시 처리하는 것이 아니라 파라미터 버퍼에 결과를 담아둡니다. 그 후 모든 드로우콜을 마치고 나면 그때서야 실제 렌더링을 수행하기 때문에 콜 별 성능을 실질적으로 확인해 볼 수가 없는 것입니다. 따라서 X-code에서 아이폰의 렌더링을 프로파일링 해보면 성능 관련 숫자가 0으로 나오게 됩니다. 0이 아닌 숫자가 나오는 경우도 있지만 이 역시 신뢰할 수 없는 숫자입니다. 대신 프레임 전체에 걸린 성능을 확인해보거나 콜 당시의 사용 텍스쳐 등 주변 정보로 유추해보는 수 밖에 없습니다. 이처럼 아이폰의 프로파일링은 좀 까다로운 편입니다.

이미지 출처 : http://www.slideshare.net/ozlael/graphics-opt-ndc


화면 변화율

TBR에서는 렌더링을 칩 내부의 타일에다 하는 과정은 대역폭을 먹지 않지만, DRAM 영역에 존재하는 프레임 버퍼에 타일을 출력하는 과정에서는 어느 정도는 대역폭이 필요할 수 밖에 없게됩니다. 이러한 대역폭을 조금이나마 절약하기 위해서 말리에서는 트랜잭션 엘리미네이션(Transaction Elimination)이라는 기술을 사용합니다. 타일 별로, 이전 프레임과 화면 결과가 달라지지 않은 타일의 영역은 프레임버퍼를 갱신하지 않고 이전 프레임의 결과를 재활용 하는 것입니다. 그렇게 하면 칩 내부 메모리에서 시스템 메모리로 복사하는 양이 줄어드는 효과를 갖게됩니다. 아래 예시 이미지의 파란 글자 타일이 바로 그 부분에 해당합니다. 

이미지 출처 : http://community.arm.com/

따라서, 고정 카메라를 사용한다면 스카이박스 등의 배경에는 최대한 변화를 피하는 것도 좋은 방법이 될 수도 있습니다. 현실적으로는 3D 게임에는 이러한 조건에 해당하는 경우가 많지는 않을 것입니다. 하지만 2D 게임에는 적합하는 부분이 많은 것입니다.


마치며

TBR에 관한 내용이랑 TBDR에 관한 내용을 같이 언급하긴 하였습니다. 하지만 대부분은 아이폰이나 안드로이드폰만 타겟으로 설정하고 개발할 것이고, 플랫폼마다 데이터를 별도로 제작하지는 않을 것이라 예상합니다. 따라서 현실적으로는 TBR, TBDR 모두 고려대상으로 삼고 이러한 사항들을 인지하면서 개발하여야 할 것이라 생각합니다.

감사합니다.


Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

개요

기술이 발전하고 시간이 지남에 따라 그래픽카드 성능 역시 계속 발전해왔습니다. 그 덕에 더욱 놀랍고 사실적인 그래픽을 표현할 수 있게 되어왔습니다. 그 결과 현대의 PC 및 콘솔 게임의 그래픽은 현실과 그래픽을 분간하기 어려울 지경입니다. 이토록 그래픽 카드는 얼마나 더 멋진 그래픽을 얼마나 더 고속으로 처리할 수 있는 지가 가장 큰 이슈이고 이에 초점을 맞추어서 발전해왔습니다.

이미지 출처 : http://www.slideshare.net/ozlael/unitylightingslide-public

하지만 모바일 기기의 그래픽카드는 조금 다른 행보를 갖습니다.  모바일기기는 항상 전원이 연결되어있는 상황이 아니기때문에 전력 소모가 가장 큰 이슈가 됩니다. 또한 휴대가 용이하게 만들어야 하기 때문에 칩셋을 얼마나 물리적으로 작게 만드냐가 관건입니다. 게다가 물리적으로 작게 만드려면 쿨러를 장착할 수가 없기때문에 발열도 큰 문제가 됩니다. 이러한 모바일 기기의 특징들때문에 모바일에서는 Tile Based Rendering(타일 기반 렌더링)이라는 독특한 방식을 사용합니다. 이번 글에서는 Tile Based Rendering에 대해서 알아보고, 유니티에서 Tile Based Rendering을 고려시 주의점에 대해 다루고자 합니다.


Tile Based Rendering

우선 Tile Based Rendering에 대한 설명에 앞서 데스크톱의 렌더링 과정을 간단히 살펴보겠습니다. OpenGL에서 드로우콜을 날리면 지오메트리 데이터가 버텍스 쉐이더를 거쳐서 트랜스폼된 뒤 레스터화되고 픽셀쉐이더로 넘어가서 픽셀 컬러를 거칩니다. 픽셀쉐이더의 결과물은 바로 프레임 버퍼로 출력이 되면서 필요에따라 블렌딩 처리가 됩니다. 이처럼 드로우콜의 명령이 프레임버퍼까지 전달되는 과정이 한번의 패스로 이루어지고 매번 프레임버퍼 영역 전체가 갱신이 됩니다. 즉, 드로우콜 한번 당 한번에 바로 화면 전체에 렌더링합니다. (그래서 이러한 전통적인 렌더링 방식을 Immediate Mode Rendering이라 부르기도 합니다.)

이미지 출처 : http://www.ntu.edu.sg/home/ehchua/

하지만 모바일에서는 조금은 다른 방식을 사용합니다. 앞서 언급하였듯이 모바일에서는 전력 소모와 물리적 크기 등을 고려해야합니다. 이를 위해서 많은 고려사항들이 반영되며 설계가 됩니다만 그 중 가장 큰 고려 사항중 하나가 바로 대역폭입니다. 대역폭을 넉넉하게 쓰다보면 전력소모가 심해지고 물리적 칩셋 크기도 커집니다. 이는 발열로 이어지게 되는데 당연히 발열을 완화시킬 쿨러를 달 공간도 없습니다. 그래서 모바일에서는 대역폭을 줄이기위해 Tile Based Rendering(이하 TBR)이라는 아키텍쳐를 채용하고 있습니다.

앞서 말했던 것 처럼 전통적으로 데스크톱의 그래픽에서는 드로우콜마다 프레임 버퍼 전체를 갱신합니다. 하지만 높은 해상도의 프레임버퍼 전체를 매 번 갱신하는 것은 높은 메모리 대역폭을 요구하게 됩니다. 따라서 모바일에서는 프레임버퍼 전체를 매 번 갱신하는것이 아니라 타일 단위로 쪼개서 갱신을 하는 방식을 사용합니다. 드로우콜 발생 시 즉시 프레임버퍼에 기록하는 것이 아니라, 칩셋에 내장된 메모리에 존재하는 타일에 렌더링합니다. 이로 인해서 매번 화면 전체를 렌더링 하는 것이 아니라 실제 도형이 그려지는 타일만 렌더링 하게 됩니다.

이미지 출처 : Performance Tuning for Tile-Based Architectures

우선, 프레임버퍼를 일정 크기의 타일로 영역을 나눕니다. (이 타일 크기는 칩셋 벤더마다 차이가 있습니다.) 드로우콜이 발생하면 지오메트리 데이터가 버텍스쉐이더를 거쳐서 트랜스폼을 수행 후 레스터화됩니다. 여기까지는 전통적인 렌더링 방식과 동일합니다만 그 이후부터가 달라집니다. 버텍스 쉐이더의 결과가 바로 픽셀 쉐이더로 넘어가지 않고 타일을 선택하는 과정을 거칩니다. 그 후에 픽셀쉐이더가 수행되고 칩 내부 버퍼에 존재하는 타일에 그려집니다. 그 후 타일들이 완성되면 프레임버퍼에 그려집니다. 이런 식으로 타일 단위로 프레임버퍼를 갱신해주기때문에 적은 대역폭으로도 화면을 렌더링 할 수 있게됩니다.

이미지 출처 : http://wenku.baidu.com/view/85ea8fec998fcc22bcd10dcb.html


Tile Based Deferred Rendering

또한, TBR에서 변형되어 파생한 Tile Based Deferred Rendering(이하 TBDR) 라는 방식도 있습니다. 이 방식은 기본적으로는 TBR입니다. 다만 버텍스 쉐이더에서 트랜스폼 연산을 거치고나서 바로 픽셀 쉐이더로 넘기는 것이 아닙니다. 대신 버텍스 쉐이더의 결과를 중간 데이터를 담는 파라미터 버퍼에 담아둡니다. (이 버퍼를 ImgTec에서는 파라미터 버퍼라 부르고, ARM에서는 폴리곤 리스트라 부르는 등 여러 이름이 존재하지만 편의상 파라미터 버퍼로 통일하여 칭하겠습니다.) 이 파라미터 버퍼에 담은 후 픽셀 쉐이더로 바로 넘기는 것이 아니라, 매 드로우 콜 마다 버텍스 쉐이더의 결과를 계속 담아둡니다. 그 후 모든 드로우콜이 끝나면 그때서야 비로소 타일을 렌더링하고 프레임버퍼에 출력합니다. 그렇게되면 한 타일에 들어오는 모든 폴리곤을 한번에 처리 할 수가 있게됩니다. 이 과정에서 타일의 각 픽셀에는 은면제거가 처리되고 나서 도달하기 때문에 픽셀 오버드로우가 발생하지 않습니다. 이러한식으로 TBR을 지연해서 처리하기때문에 Tile Based Deferred Rendering(타일 기반 지연 렌더링)이라고 불립니다. 

이미지 출처 : Unity: iOS and Android - Cross Platform Challenges and Solutions

예전에는 TBDR 방식이 ImgTec의 PowerVR 즉 아이폰과 아이패드에서만 사용되었으나 최근들어서는 다른 칩셋들에서도 사용되고 있습니다. 하지만 여전히 안드로이드 기기는 TBDR보다는 TBR이 많이 사용되고 있습니다. 따라서 현 시점에서는 TBR을 사용하는 디바이스와 TBDR을 사용하는 디바이스가 공존하고 있는 상태입니다. (타일 기반이 아닌 전통적인 렌더링 기법을 쓰는 디바이스는 점유율이 매우 낮아서 논외로 합니다.)

이처럼 모바일에서는 타일 단위로 쪼개서 렌더링하는 방식을 사용하다보니 몇 가지 주의 사항이 존재합니다. 서론이 좀 길어지긴 했는데 결국 전달고자 하는 내용들은 다음과 같습니다.


이어지는 내용 : 

모바일 기기의 Tile Based Rendering(타일 기반 렌더링)과 유니티에서의 주의 사항 #2 : TBR 대응 리소스 제작시 주의점



Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Unity5로 리메이크한 Republique가 에셋스토어에 올라왔습니다. 그와 함께 유니티 공식 블로그에 "THE REPUBLIQUE REMASTERED IN UNITY 5 LEARN PROJECT IS HERE!"라는 글이 함께 올라왔습니다. 이 글은 유니티 공식 블로그의 글을 번역한 글입니다.

몇 달 전 Camouflaj에서 Remastering Republique: The Journey to Unity 5라는 주제로 영상 및 글을 올렸습니다. 또한 이제는 프로젝트의 일부를 에셋스토어에 공유하였습니다. 이를 통해서 Unity5를 실제 게임에서 어떻게 활용 할 수 있는 지를 확인할 수 있을 것입니다.

이 프로젝트 파일에는 교도소, 중앙 홀, 터미널 등 여러 공간이 포함되어 있습니다. 이를 통해서 리플렉션 프로브, Enlighten, 새로운 에니메이션 및 오디오 기능 등 Unity5의 기능들이 어떻게 사용되고 있는 지 확인할 수 있습니다.


ENLIGHTEN

Republique의 중앙 홀은 스튜디오에서 Unity5로 작업 한 첫번째 공간입니다. 우리(Camouflaj)는 Enlighten을 통해서 매우 놀라운 개선을 이를 수 있었습니다. 특히 씬의 천장에서 내려오는 빛은 매우 큰 효과를 보여주었습니다.


발광(Emissive) 재질은 Unity5에 추가된 것 중 하나입니다. 형광등 재질의 발광 강도(Emission intensity)를 증가시킴으로써 반사광을 만들어 씬을 밝게 조절하는 것을 보실 수 있습니다. continuous baking을 활성화시키면 즉각적으로 결과를 확인하게 됩니다. 이로써 라이팅 작업 시간을 매우 줄일 수 있게됩니다.

다음 이미지는 Emission 값을 보여주고있습니다. 이는 에니메이션 트랙을 추가하여 값을 네이메이션 시킬 수 있습니다. (역주:예를 들어 형광등의 깜빡임 등)

라이팅 탭의 간접광 강도(Indirect Intensity)를 증가시킴으로써 씬이 동적으로 업데이트되고 한층 밝아지는 것을 확인할 수 있습니다. 이는 실시간으로 보여지고 있습니다.

리플렉션 프로브는 주변 환경을 반사시킴으로써 실제같은 금속을 만들 수 있게 해줍니다. 

다음 이미지의 기둥의 표면은 금속으로 되어있습니다. 반사력(reflectivity)를 높임으로써 공간적으로 정확한 반사를 얻을 수 있습니다.

아래 이미지와 같이 부드러움(smoothness)을 줄이면 반사 이미지는 거칠어지고 흐려집니다. 


UNITY5 AUDIO

유니티5의 오디오 믹서는 오디오를 분류하고 출력 버스를 특정지을 수 있습니다. 이로인해 오디오를 더욱 유연하게 전송하고 혼합할 수 있습니다.  ‘Ambience’그룹은 2D와 3D 서브 그룹으로 나뉩니다. 자연적으로, 3D 환경 오디오는 3D 서브 그룹으로 전송되고, 3D 버스로 할당된 오디오는 알맞는 그룹으로 연결됩니다. 각각의 그룹에서는, 볼륨(volume)과 피치(pitch)를 조절할 수 있고 오디오 필터가 적용됩니다. 또한, 개별적 혹은 전체적으로 그룹들을 실시간으로 음소거 시키거나 독주화 시킬 수 있습니다. 오디오 믹서 우측 상단의 ‘Edit on Play Mode’를 클릭하면 게임이 실행되는 동안에 편집이 가능합니다. 그 예로 우리는 게임이 실행되는 동안 ‘SFX Reverb’를  추가하여 발자국을 편집하였습니다.

만족스러운 튜닝을 마친 상태라면 오디오 셋팅을 스냅샷으로 저장할 수 있습니다. 이 스냅샷은 오디오 혼합 상태를 저장하고 다양한 사운드 프로필을 생성하여 유용하게 활용 될 수 있습니다.


MECANIM

플레이할 준비가 되었다면 한번 실행해보십시요. 좌 클릭으로 호프(케릭터 이름)를 움직일 수 있습니다. WASD로 카메라를 컨트롤 할 수 있습니다. 이 프로젝트에는 몇몇의 에니메이션이 포함되어있습니다. 하지만, 메카님 API를 이용하면 메카님 에셋을 생성하고 편집할 수 있는 많은 종류의 툴을 만들 수 있습니다.

이 프로젝트에서는, Hope에 사용된 에니메이터를 재생성하는 버튼이 존재합니다. 스크립트는 Project 폴더에 비슷한 에니메이터를 생성합니다. 

[MenuItem ("Hope/Create Controller")]

static void CreateController () {

// Creates the controller

var controller = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath ("Assets/HopeScriptCtrl.controller");

// Add parameters

controller.AddParameter("Walk", AnimatorControllerParameterType.Bool);

controller.AddParameter("TurnLeft", AnimatorControllerParameterType.Bool);

controller.AddParameter("TurnRight", AnimatorControllerParameterType.Bool);

controller.AddParameter("HalfTurn", AnimatorControllerParameterType.Bool);

// Add StateMachines

var rootStateMachine = controller.layers[0].stateMachine;

var stateMachineStand = rootStateMachine.AddStateMachine("Stand");

// Add States

var stateIdle = stateMachineStand.AddState("Idle");

var stateTurnLeft = stateMachineStand.AddState("TurnLeft");

var stateTurnRight = stateMachineStand.AddState("TurnRight");

var stateHalfTurn = stateMachineStand.AddState("HalfTurn");

var stateWalk = stateMachineStand.AddState("Walk");

stateIdle.motion = AssetDatabase.LoadAssetAtPath("Assets/Animations/Hope Animations/StandingIdleLooking.fbx", typeof(AnimationClip)) as Motion;

stateTurnLeft.motion = AssetDatabase.LoadAssetAtPath("Assets/Animations/Hope Animations/MoveStand90_L.fbx", typeof(AnimationClip)) as Motion;

stateTurnRight.motion = AssetDatabase.LoadAssetAtPath("Assets/Animations/Hope Animations/MoveStand90_R.fbx", typeof(AnimationClip)) as Motion;

stateHalfTurn.motion = AssetDatabase.LoadAssetAtPath("Assets/Animations/Hope Animations/MoveStand180.fbx", typeof(AnimationClip)) as Motion;

stateWalk.motion = AssetDatabase.LoadAssetAtPath("Assets/Animations/Hope Animations/MoveWalk_F.fbx", typeof(AnimationClip)) as Motion;

// Add Transitions

var idle2TurnLeft = stateIdle.AddTransition (stateTurnLeft);

var turnLeft2Idle = stateTurnLeft.AddTransition (stateIdle);

idle2TurnLeft.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, "TurnLeft");

idle2TurnLeft.duration = 0.025f;

turnLeft2Idle.hasExitTime = true;

turnLeft2Idle.exitTime = 0.85f;

turnLeft2Idle.duration = 0.15f;

var idle2TurnRight = stateIdle.AddTransition (stateTurnRight);

var turnRight2Idle = stateTurnRight.AddTransition (stateIdle);

idle2TurnRight.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, "TurnRight");

idle2TurnRight.duration = 0.025f;

turnRight2Idle.hasExitTime = true;

turnRight2Idle.exitTime = 0.85f;

turnRight2Idle.duration = 0.15f;

var idle2HalfTurn = stateIdle.AddTransition (stateHalfTurn);

var halfTurn2Idle = stateHalfTurn.AddTransition (stateIdle);

idle2HalfTurn.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, "HalfTurn");

idle2HalfTurn.duration = 0.025f;

halfTurn2Idle.hasExitTime = true;

halfTurn2Idle.exitTime = 0.85f;

halfTurn2Idle.duration = 0.15f;

var idle2Walk = stateIdle.AddTransition (stateWalk);

var walk2Idle = stateWalk.AddTransition (stateIdle);

idle2Walk.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, "Walk");

idle2Walk.duration = 0.025f;

walk2Idle.AddCondition(UnityEditor.Animations.AnimatorConditionMode.IfNot, 0, "Walk");

walk2Idle.duration = 0.25f;

}

이 프로젝트가 여러분들께 도움이 되셨길 바랍니다. ( 에셋 스토어 링크 : https://www.assetstore.unity3d.com/en/#!/content/34352?utm_source=unity3d&utm_medium=blog&utm_campaign=ASContent_Camouflaj)


*참고 : 최종 버젼의 게임 비쥬얼은 이 프로젝트와 약간은 달라질 수도 있습니다.



Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

본 포스팅은 유니키 공식 블로그 TUESDAYS ARE FOR #UNITYTIPS를 번역한 글입니다.


Unity를 사용하며 얻은 유용한 팁이 있다면 #unitytips 해쉬 태그로 트위터로 트윗하거나 페이스북으로 공유해주세요. 깔끔한 에디터 워크플로우 혹은 성능 최적화를 위한 규칙 등 어떠한 것도 상관 없습니다. 꼭 새로운 기능이거나 최고급 정보일 필요는 없습니다. 무엇이든 공유할 만한 팁이 있다면 공유해주세요. 그 시작을 위해 개인적으로 베스트 10 #unitytips를 공유합니다.

1. 유니티가 플레이모드 중일때 가끔 이를 잊어버리는 경우가 있습니다. ‘Preferences’ > ‘Colors >  Playmode Tint로 기억하기 쉬운 색으로 설정하세요.
2. 쉽게 카메라를 포지셔닝할 수 있습니다. 씬뷰에서 화면 앵글을 원하는 상태로 놓고 메인 카메라를 선택합니다. 그리고 Align With View를 선택하거나 Ctrl/Cmd + Shift + F 를 누르면 씬뷰에서 바라보는 방향 및 위치로 메인 카메라가 셋팅됩니다.

3. 프로젝트 뷰에서 에셋스토어의 에셋을 검색할 수 있습니다. 프로젝트 뷰에서 검색할 단어를 입력한 뒤 Search를 Assets에서 Asset Store로 변경하세요. 그러면 에셋스토어를 열지 않고도 에셋스토어의 에셋을 미리 볼 수 있습니다.
4. 오브젝트를 회전시킬 시 Ctrl/Cmd를 누른채로 조작하면 스냅되어 회전합니다. 이는 오브젝트를 움직일 때도 마찬가지입니다. 스냅 기본값은 Edit 메뉴의 Snap Settings에서 변경할 수 있습니다.

5. 또 다른 스내핑 트릭입니다. 오브젝트를 선택하고 이동 시 V키를 누른채로 조작하면 버텍스 스내핑이 가능합니다. 이는 특히 모듈화된 지오메트리로 레벨을 구축 할 시 유용합니다.

6. 인스팩터 창의 컴포넌트에 있는 파란 물음표 책 버튼을 누르면 해당 컴포넌트의 도움말을 볼 수 있습니다.
7. 플레이모드에서 재생하고 테스트 중 컴포넌트의 완벽한 값을 발견했나요? 컴포는트에 있는 작은 톱니바퀴 모양의 아이콘을 눌러서 Copy Component를 선택하세요. 플레이모드를 빠져나온 후 그 값을 붙여넣을 수 있습니다.

8. Layers 버튼을 이용하여 특정 레이어의 오브젝트들을 보여주거나 숨길 수 있습니다.

9. 프로파일러 창에서 그래프가 너무 난잡한 경우에는 확인하고싶은 데이터(드로우콜, 스크립트, V싱크 등)의 색상이 칠해진 사각형을 끄고 킴으로써 보고싶은 데이터만 확인할 수 있습니다. 
10. 유니티 에디터의 레이아웃 배치를 편집하고 저장할 수 있습니다. Windows > Layouts > Save / Delete Layouts


Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
2D 기능 개발은 Unity에서도 매우 중요한 이슈입니다. 따라서 최근에 2D 팀을 확장하였습니다. 많은 인력들이 팀에 참여하게 됨으로써 더 직관적이고 강력하면서도 유연한 2D 기능을 개발할 수 있게 되었습니다. 2D 팀에서는 알파 버전을 공개함으로써 여러분의 피드백을 반영하며 개발하는 프로세스를 갖기로 하였습니다. Unity Pro 사용자는 이러한 기능들을 먼저 만나보실 수 있습니다. 여러분의 피드백으로 인해 개발 기간을 단축 시킬 수 있고 정말로 여러분이 원하는 것에 더욱 집중할 수 있을 것이라 기대하고 있습니다.



Bitbucket에서는 최신 버전 릴리즈와 함께 기술 데모, 프로토타이핑 툴, 문서 초안 등을 미리 만나보실 수 있습니다. 

2D팀은 기능을 매우 멋지게 만드는 데 힘을 쏟기위해 QA와 UX에 대한 인력이 별도로 존재합니다. 여러분의 제안이나 이슈 제보 및 피드백을 리파지터리의 Issues Section에 올려주시고 2D 기능 향상에 도움을 주십시요.

현재 알파 버젼에서 미리 확인해볼 수 있는 기능들은 다음과 같습니다.
-타일 맵 에디터
-9분할 스프라이트
-스마트 스프라이트
-ETC1 압축
-마스킹
-스프라이트 패킹 개선
-기타 등등 

감사합니다!

(본 포스팅은 Unity 공식 블로그의 EARLY ACCESS TO NEW 2D TOOLS를 번역 및 요약한 글입니다.)


Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
Draw call을 줄이는 것은 최적화 시 가장 중요한 요소중 하나입니다. Unity의 새로운 UI는 이러한 Draw call을 줄이는 것이 매우 용이하게 되어있습니다. 이 글에서는 new GUI의 Draw call에 대해 다루고자합니다.

new UI는 Button, Image 등 UI 컴포넌트를 추가 시 자동으로 Canvas하위로 배치되게 되어있습니다. 
유니티 UI의 Draw call은 이 Canvas 단위로 이루어집니다. Canvas 하위의 UI 컴포넌트들에 쓰이는 이미지가 SPrite packing되어 있다면 모두 하나의 Draw call로 처리가 가능합니다. 그러므로 유니티의 UI시스템을 이용하면 Draw call을 절약할 수 있어서 성능 향상에 매우 도움이 됩니다. 하지만 유니티의 UI를 이용한다고 해서 무조건 Draw call이 하나만 발생하는 것은 아닙니다. 성능 향상을 위해서 숙지하고 있어야 할 사항을 몇 가지 이야기하고자 합니다.


Draw call

그래픽 최적화를 위해서는 Draw call을 줄이는 것이 중요합니다. 이는 UI 역시 마찬가지입니다. 유니티의 UI에서는 Canvas마다 각자의 Vertex buffer를 가지고 있기때문에 Canvas가 두개면 최소 두번의 Draw call이 일어납니다. 이는 Canvas하위에 Canvas가 존재하는 경우에도 마찬가지입니다. 만일 Canvas안에 Canvas를 추가하는 경우에도, 씬 바로 하위의 Canvas는 하나 뿐이지만, 최소 두 개의 Draw call이 발생합니다.
위에도 언급했지만 하나의 Draw call이 되기 위해서는 사용되는 이미지들이 atlas 처리가 되어있어야합니다. 즉, Sprite packing되어있는 sprite들을 사용해야합니다. 다만, atlas 페이지가 두 개 이상으로 나뉘어져있으면 경우에 따라서 두 개 이상의 Draw call이 일어날 수도 있습니다. 따라서 Packing Tag를 잘 나누는 것이 중요합니다.

다만, Canvas 별로 Draw call이 일어난다고해서 무조건 모든 UI요소들을 하나의 Canvas에 몰아넣는 것이 꼭 좋은 솔루션인것만은 아닙니다. 동적으로 반응하는 버튼이나 Fill Image등을 처리하기위해서는 매 번 갱신 비용이 발생하기 때문입니다. Runtime시에 변경되지 않는 이미지와 실시간으로 변경되는 이미지가 같은 Canvas에 존재하면 변경되는 이미지 하나를 처리하기 위해서 Canvas의 Vertex buffer를 갱신하기때문에 쓸데없는 갱신 비용이 발생합니다. 따라서, Draw call을 하나 희생하더라도 정적인 이미지들은 별도의 canvas로 빼두는 것이 효율적일 수도 있습니다. 


Text

Text는 별도의 Texture를 사용하기때문에 별도의 Draw call로 발생합니다. 이 텍스쳐는 font별로 만들어지므로 font의 종류를 줄이는 것이 Draw call을 줄이는 방법이기도 합니다.

따라서 아래의 씬에서는 UI가 차지하는 Draw call이 3입니다. 아이콘 이미지들은 패킹되어있는 상태라서 모두 합쳐서 1개의 Draw call을 차지합니다. 텍스트는 3개가 존재하지만 사용된 폰트는 2개라서 2개의 Draw call을 차지합니다. 따라서 총 3개의 Draw call이 발생합니다.


Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

RPG든 액션이든 어떠한 장르에서든 스킬 시스템이 있는 게임은 쿨타임이란 것이 존재합니다. 그리고 이러한 쿨타임은 시각적으로 보여 줄 시 다양한 형태로 표현하지만 크게두 가지 형식으로 표현합니다. 시계/반시계 방향의 부채꼴 또는 수직/수평으로 영역을 채우는 이미지로 표현하는 것입니다.

유니티의 이전 버젼에는 이러한 버튼을 표현해주는 기능이 없어서 직접 구현하거나 플러그인을 사용하여야 했습니다. 하지만 유니티 4.6부터 GUI 시스템이 대폭 개선되어 많은 기능들이 추가되었었습니다.

이 새로운 UI 시스템에서는 이미지를 다루는 기능 역시 대폭 향상되었습니다. Filled 타입 이미지가 추가되었고 이로 인해 손쉽게 쿨타임을 표현해줄 수 있게 되었습니다. 


일단 Hierarchy > Create > UI > Button 으로 버튼을 추가하면 Canvas란 것이 생기고 그 아래 Button이 생긴 것을 확인하실 수 있습니다. 이 Button의 Inspector을 확인해보면 Image와 Button 컴포넌트가 있습니다. 이 중 Image컴포넌트의 Source Image를 원하는 이미지로 바꿔줍니다. 이 때 Source Image는 Sprite 타입의 텍스쳐만 사용 가능합니다. 이미지를 설정 후 Image Type을 Filled로 바꾸면 하위 항목 중 Fill Amount 슬라이더가 생깁니다. 이 슬라이더 값을 0에서 1로 조절 시 이미지가 이에 따라 채워지는 영역이 생깁니다. 쿨타임에 맞게 이 값을 업데이트 해주면 쿨타임 버튼이 되는 것입니다.

다만, 보통은 원래 이미지의 색으로 채워지는 영역 배경에 흑백 또는 어두운 이미지가 존재합니다만 Filled image의 배경 기능은 없습니다. 따라서 같은 위치에 배경을 추가해줘야 합니다. 배경은 단순 이미지만 배치하면 되므로 Create > UI > Image로 이미지를 추가하여 Source Image를 설정해준 뒤 Color을 조절하여 어두운 이미지로 만들어줍니다. Hierarchy에서의 순서가 곧 렌더링 되는 순서이므로 이를 변경하면 됩니다. 배경으로 삼을 이미지를 버튼의 위로 배치하시면 버튼이 배경 위에 그려집니다.

이제 버튼 추가는 다 되었으므로 스크립트를 추가해줘야합니다. 스크립트에서는 쿨타임 값에 맞게 Fill Amount를 조정해주는 동작을 합니다. 또한, 쿨타임 중일 시에는 버튼이 동작하지 않도록 해주어야 합니다. 이러한 내용을 스킬 시스템이나 로직 스크립트에서 직접 컨트롤을 해주어도 되지만 쿨타임 버튼만을 담당하는 스크립트를 만들어두면 버튼을 추가할 때 마다 훨씬 개발하기 수월해질 것입니다.

Project 창에서 우클릭 > Create > C# Script로 스크립트를 하나 생성합니다. 저는 이름을 CoolTimeButton이라고 지었습니다. 스크립트의 내용을 다음과 같이 채웁니다.

버튼이 채워지는 값은  쿨타임 값을 0~1로 변환하여 Image.fillAmount로 설정해주면 됩니다. 쿨타임 동안은 버튼이 작동하지 않도록 하는 것은 Button 컴포넌트의 enable을true/false로 설정해두면 됩니다. 이러한 제어를 매 Update()마다 처리해주는 것입니다. 

스크립트를 완성 후 버튼에 스크립트를 추가해줍니다. 

그럼 이제 버튼이 눌렸을 시 CoolTimeButton 스크립트의 메소드를 호출하도록 하면 됩니다. 이를 위해서 버튼의 Button 컴포넌트 하단의 On Click() 에 있는 +버튼을 눌러서 콜백을 추가해줍니다. Realtime Only 콤보박스 하단의 오브젝트 항목에 버튼 오브젝트 스스로를 설정해주면 Realtime Only 콤보박스 옆의 No Function 콤보박스가 활성화됩니다. 그 콤보박스에서 CoolTimeButton.ResetCooltime()을 설정해줍니다. 이리함으로써 버튼이 눌리었을 시 CoolTimeButton의 ResetCooltime()이 호출되어 쿨타임이 다시 카운팅됩니다. 쿨타임중일 시에는 CoolTimeButton.Update()에 의해 비활성화 처리되므로 버튼을 눌러도 아무런 반응을 하지 않습니다. 쿨타임 자체의 처리는 이제 완료가 되었습니다. 버튼에 연결된 스킬이나 아이템의 스크립트는 마찬가지로 On Click()의 +버튼을 한번 더 눌러서 콜백을 연결해주면 됩니다(아래 예시에서는 SkillNotify). 콜백은 이토록 여러개를 설정 할 수 있어서 다양한 작업을 처리할 수 있습니다.

이처럼 유니티의 새로운 UI기능을 사용하면 UI 작업을 훨씬 수월하게 처리할 수 있습니다. 새로운 UI 시스템의 더 많은 것을 알고싶으시면 공식 튜토리얼 및 에셋스토어 예제를 참고 바랍니다. 감사합니다.


Unity New GUI 튜토리얼 : 

http://unity3d.com/learn/tutorials/modules/beginner/ui

에셋스토어 예제 : 

https://www.assetstore.unity3d.com/en/?gclid=CjwKEAiAjsunBRCy3LSlz_PJqCgSJACJY7yKSmdicbryiDMHiOPX-rMfA-bLLkmb7SBsnl0VSAkYLxoCcNTw_wcB#!/content/25468

Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Unity5에는 스탠타드 셰이더(Standard Shader), 실시간 글로벌 일루미네이션(Real-time Global Illumination), 리플렉션 프로브(Reflection Probe), 향상된 라이트맵(Lightmap) 워크플로우 등 시각적으로 두드러지는 새로운 기능들이 대거 추가가 되었습니다. 뿐만 아니라 성능 및 색공간 등에 관한 내부적인 개선 사항들도 많습니다.

그리고 또 다른 개선사항으로는 기능 확장성에 대한 것을 들 수 있겠습니다. 많은 고민을 통해 커맨드 버퍼(Command Buffer)라는 기능을 추가하였습니다. 이 기능을 사용하면 렌더링 관련 명령 목록을 만듦으로써 렌더링 파이프라인을 확장시킬 수 있습니다.
사실 원래는 그래픽스에서 커맨드 버퍼라는 것은 저수준(low-level)의 컨셉입니다. 어플리케이션에서 DirectX나 OpenGl 같은 그래픽스 API에 명령을 주면 커맨드 버퍼라는 곳에 명령이 쌓이고 GPU는 여기서 정보을 꺼내가서 렌더링을 합니다. 본 포스팅에서 말하는 커맨드 버퍼도 이와 비슷한 개념입니다. 다만 GPU에게 “레지스터 X에게 Y값을 셋팅해”라는 저수준의 명령이 아니라, “이 메시를 저 매터리얼을 사용해서 그려” 등의 명령 목록을 만들 수 있는 고수준(high-level)의 개념인 것입니다.

스크립트에서 커맨드 버퍼를 생성하여 렌더링 명령 설정할 수 있습니다. 이 명령을 카메라 렌더링 과정에서 많은 용도로 사용이 가능합니다. 예를 들자면, 디퍼드 렌더링 과정 중 G버퍼가 끝난 후 추가적인 오브젝트 렌더를 하거나, 일반적인 스카이박스 렌더 후 구름들 추가하는 등 다양한 작업들을 할 수 있게 됩니다. 자세한 내용은 공식 메뉴얼을 참고해주세요.


예시들

그 활용 예로 블러된 굴절 효과를 들 수 있습니다. 스카이박스와 일반적인 불투명 오브젝트가 렌더링 되고나면 현재 렌더된 이미지는 임시적인 렌더 타겟에 저장됩니다. 불투명 오브젝트 앞에 위치한 유리 오브젝트에 설정한 셰이더에서는 이 이미지를 굴절시켜 보여주면 이러한 효과를 얻을 수 있습니다. 이는 shader GrabPass does와 흡사한 개념입니다만 그 보다는 더 많은 커스터마이징 작업을 할 수 있게 됩니다.
또 다른 예로는 커스텀한 디퍼드 라이트(custom deferred light)를 들 수 있습니다. 일반적인 포인트 및 스팟 라이트가 아닌 튜브나 구체 모양 등의 라이트를 표현 할 수 있는 것입니다. 일반적인 디퍼드 셰이딩 라이트 과정이 끝난 후, 커스텀한 라이트를 라이트 버퍼에 그려줄 수 있습니다.
마지막으로 디퍼드 데칼(deferred decal)을 예로 들겠습니다. 전통적인 방식의 데칼은 대상 면의 형태를 그대로 따와서 별도의 메시를 만들어서 얹어주는 방식입니다. 그러다보니 Z파이팅 문제라던가 라이팅 이질감 등의 부작용이 존재합니다. 그러나 디퍼드 데칼은 G버퍼에 박스모양의 볼륨으로 데칼을 직접 그려넣는 방식입니다. 이 과정은 디퍼드 라이팅이 그려지는 방식과 흡사합니다만 라이팅 연산 대신 G버퍼에 텍스쳐만 그려넣는 것입니다. 이렇게 되면 Z파이팅 문제도 사라지고 라이팅도 자연스럽게 적용됩니다.
데모를 다운 받아서 확인해보실 수 있습니다. 코드도 복잡하지 않습니다. 이 행위들을 하는 스크립트는 약 100줄의 코드만으로 이루어져있습니다. 
데모 다운로드 : 


Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

본 포스팅은 유니티 공식 블로그의 "Working with Physically Based Shading" a practical approach"를 번역한 것입니다.




유니티5 베타를 사용중이시라면 에셋 스토어에서 바이킹 마을 데모를 다운받아서 확인해보실 수 있습니다. 이 데모에서는 유니티5에서 씬의 조명을 어떻게 구성해야 하는지에 대한 안내를 받을 수 있습니다.


견본 환경 만들기

텍스쳐링과 셰이더 설정을 알맞게 결정하기 위해서는 간단한 신을 다양한 라이팅 셋업으로 테스트해보는 것을 추천합니다. 이는 각기 다른 스카이박스와 라이팅 등 모델의 조명에 연관되는 것 들을 의미합니다. 유니티5를 열면 빈 씬이 기본적으로 절차적으로 생성 된 하늘이 포함 된 것을 확인 가능합니다. 이는 기본 환경광과 반사 셋팅을 포함하고 있습니다. 
템플릿 환경에서는 다음 사항들이 준비되어있어야합니다.
- HDR 카메라 렌더링
- 몇 개의 리플렉션 프로브
- 라이트 프로브의 그룹
- HDR 하늘텍스쳐와 재질(material)들의 셋. 이 프로젝트에 포함된 하늘은 유니티를 위해 커스텀하게 만들어진 것이 사용되었습니다.( 제작자:Dutch Skies 360)
- HDR 하늘 색과 강도가 맞춰진 미색의 방향성 광원(directional light)


스카이 텍스쳐 파라미터 조절

대부분의 하늘 텍스쳐는 이미지 자체에 태양 및 플레어를 포함하고 있습니다. 하지만 이는 다음 이유들로 인해 잠재적인 문제가 됩니다.
- 디렉셔널 라이트의 방향을 잡을 시 텍스쳐에 그려진 태양과 맞춰야만 하는 제약이 생깁니다.
- 강렬한 스페큘라 하이라이트떄문에 반사된 태양과 스페큘러 핫스팟이 겹쳐버립니다. 
- 미리 그려진 태양의 반사가 그림자 영역에서 가려지지 않습니다. 이는 어두운 영역에다 어색한 반짝임을 만들어버립니다.
때문에, 태양의 하이라이트, 플레어, 태양광 및 HDR 값은 하늘 텍스쳐 외부(주로 디렉셔널 라이트)에서 편집되어야합니다. 


물리 기반 셰이딩 재질 

유니티5의 스탠다드 셰이더는 스페큘러 컬러와 메탈릭 워크플로우 둘 다 제공하고 있습니다. 둘 모두 표면에 반사하는 색을 정의하고 있습니다. 스페큘러 워크플로우에서는 색상이 직접적으로 명시됩니다. 반면 메탈릭 워크플로우에서는, 디퓨즈 색상과 메탈릭 값의 조합으로  색상이 만들어집니다. 

에셋 스토어에서 캘리브레이션(Calibration) 씬을 받아보실 수 있습니다. 이 씬은 각종 측정용 차트를 포함하고 있습니다. 바이킹 프로젝트에서는 스페큘러 컬러 워크플로우로 만들어졌는데, 이 차트를 참고하며 진행하였습니다.
스페큘러 워크플로우에서는 반사광의 스페큘러 색상을 직접 선택 할 수 있습니다. 반면 메탈릭 워크플로우에서는 재질이 조명될 때 메탈처럼 작동할 것인지에 대한 선택을 할 수 있습니다. 두 워크플로우 모두 최종적으로는 동일한 결과를 만들어 낼 수 있습니다. 따라서 작업 진행을 스페큘러 워크플로우로 할 것인지 메탈릭 워크 플로우로 할 것인지는 순전히 작업자의 기호에 맞추어 선택하면 됩니다. 

스페큘러 값 차트:

메탈릭 값 차트:


재질 설정하기

재질을 만들 시, 테스트 용도의 깨끗한 재질을 만들어 두면 유용합니다. 이 재질에다 측정용 챠트로부터 색상 등의 값을 적용합니다. 그 후 텍스쳐를 적용한 결과와 비교해보면 재질 본래의 느낌을 확인해볼 수 있습니다.


텍스쳐 제작의 전통적인 방법

바이킹 마을 데모에 쓰인 텍스쳐들은 사진 등의 데이터로부터 스캔한 디퓨즈/알베도, 스페큘러, 노말맵 등을 사용합니다. (제공 : Quixel텍스쳐에 디테일을 추가할 때는 주의할 점이 있습니다. 예를 들자면, 일반적으로는 텍스쳐에 미리 AO나 그림자 등의 라이팅을 적용해서 그려넣기도 합니다. 하지만 물리 기반 렌더링에서는 엔진에서 모든 라이팅을 제공해주기때문에 텍스쳐에 미리 그려넣으면 안됩니다. 사진을 수정하는 작업은 이러한 리터칭이 많이 들어가야해서 PBS 스캐닝 된 데이터보다 부담이 큽니다만 Quixel Suite Allegorithmic Substance Painter를 이용하면 이러한 과줭이 한결 수월해집니다.


스캔 데이터

PBS 대응하여 스캔된 데이터는 편집하기가 좀 더 편합니다. 알베도, 스페큘러, 매끄러움 등이 이미 분리된 데이터이기때문입니다. PBS 데이터를 만들어 주는 소프트웨어가  유니티 프로필에 대응되어있다면 더욱 좋을것입니다. 


재질 예시


바이킹 마을 씬은 많은 적절한 메모리의 텍스쳐를 사용하여 많은 양의 컨텐츠를 나타내고 있습니다. 그 예로 10미터 크기의 나무 크레인 모델을 살펴보겠습니다.


예시 1: 크레인 오브젝트는 2개의 재질을 가집니다. 2개의 디퓨즈 텍스쳐, 1개의 스페큘러-매끄러움 텍스쳐, 2개의 오클루전 텍스쳐, 2개의 디테일 텍스쳐

예시 2: 방패 프랍은 1개의 재질을 가집니다. 1개의 디퓨즈맵 텍스쳐, 1개의 스페큘러(specular)-매끄러움(Smoothness) 텍스쳐, 1개의 오클루전 텍스쳐를 가집니다. 디테일 텍스쳐는 없습니다.

알베도 텍스쳐 : 스페큘러 워크플로우에서는, 알베토 텍스쳐는 표면에 반응하는 디퓨즈 라이트의 색상을 나타냅니다. 왼쪽 이미지 (크레인)에서는 너무 그렇게 높은 디테일을 필요로하지는 않습니다. 반면 오른쪽 텍스쳐 (방패)는 높은 디테일을 포함하고 있습니다.

크레인의 디퓨즈맵은 나무의 색상으로 평범하게 이루어져있습니다. 디테일도 그냥 적당한 정도로만 이루어져있습니다. 반면 오른쪽에 있는 방패의 이미지는 높은 디테일을 가지고 있습니다.

크레인 재질의 디퓨즈 색상 값

스페큘러 : 논-메탈(비전도체)는 비교적 어두운 그레이스케일의 스페큘러 색상을 가집니다. 반면 메탈은 더 밝고 고유 색상을 띄는 스페큘러를 가집니다. ( 녹슬거나 기름때나 먼지가 있는 부분은 메탈릭이 아닙니다.) 

좌측은 메탈을 표현하기 위한 크레인의 스페큘러 맵(메탈릭 셰이더를 사용하지 않음. 스페큘러 워크플로우). 우측은 방패의 스페큘러 텍스쳐

나무의 표면은 전반적으로 스페큘러가 거의 없다시피합니다. 따라서 나무의 스페큘러는 텍스쳐를 사용하는 대신 단순 생삭값으로 대체합니다.

매끄러움(Smoothness)은 PBS 재질의 핵심 속성중 하나입니다. 이는 재질의 상태, 변화, 결점, 디테일 등을 표현하고 물체가 오래되었는 지 등에 대한 시각적인 힌트를 제공해줍니다. 크레인의 경우, 거칠기가 재질 전반적으로 동일하기때문에 따로 텍스쳐로 사용되지 않고 단순한 값으로만 대체될 수 있습니다. 그 덕에 텍스쳐 메모리를 절약할 수 있습니다.


크레인에 있는 나무의 매끄러움. 텍스쳐 대신 단순 값으로 사용함.

메탈의 표현하기 위한 크레인의 매끄러움 맵(메탈릭 셰이더를 사용하지 않음. 스페큘러 워크플로우). 우측 이미지는 방패의 매끄러움 맵을 나타냄. 나무와 메탈 표면이 공존.

오클루전(Occlusion)은 얼마나 표면이 돌출되었는지에 대한 정보를 빛에 반응하여 나타냅니다. 앰비언트 오클루전(Ambient Occlusion)은 표면의 디테일과 높이를 주변광과 반사광 등으로 표현을 해줍니다. SSAO(Screen Space Ambient Occlusion)을 사용하는 것도 염두해두어야합니다. SSAO와 AO를 같이 사용하게되면 배로 어두워지는 경향이 생길 수 있습니다. AO맵으로 크랙이나 접합부 등을 강조하는데 쓰일 수도 있습니다. 게임이 SSAO나 라이트맵의 AO를 쓰는 경우엔 적합한 용도가 될 것입니다.

첫번째 이미지는 라이트맵 AO, 두번째 이미지는 오클루전 텍스쳐, 세번째 이미지는 디퓨즈의 오클루전, 네번째 이미지는 이미지 효과 SSAO를 보여주고 있습니다.



부가 텍스쳐와 해상도


부가(secondary) 텍스쳐는 디테일의 레벨을 증가시켜주거나 재질의 변화를 제공하는데 쓰일 수 있습니다. 디테일 마스크 속성을 사용하는 것으로 마스킹 될 수 있습니다.

크레인의 디퓨즈 텍스쳐는 비교적 낮은 해상도로 이루어져 있습니다. 이러한 경우 부가 텍스쳐로 표면의 디테일을 부여해줄 수 있습니다. 디테일맵은 타일링되어 표면에 전반적으로 반복되어 집니다. 때문에 낮은 해상도로도 높은 디테일을 표현해줘서 메모리를 절약 할 수 있습니다.

부가 알베도맵과 노말맵은 저해상도의 디퓨즈와 노말맵을 보완해줍니다. 아래 이미지는 크레인의 표면 비교. 왼쪽은 부가 텍스쳐 사용. 오른쪽은 사용 안함.


에셋 스토어 링크 : https://www.assetstore.unity3d.com/#!/content/29140




Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

* 이 글은 Game Development Forever에도 동일하게 기재되었습니다. (http://gamedevforever.com/326)

* 샘플 프로젝트를 받아서 확인해보세요 : https://github.com/ozlael/PlannarShadowForUnity

그래픽을 표현하는데 있어서 그림자는 매우 중요합니다. 그림자가 존재함으로써 사물의 공간상 위치를 인지하기 쉽게 만들어주고 입체감을 더해주게됩니다. 그림자는 입체 공간을 구성하는데 있어 거의 필수적인 요소라해도 과언이 아닙니다.

이는 게임에 있어서도 마찬가지입니다. 그런 이유로 모든 3D 게임에는 어떠한 형태로든 그림자가 존재합니다. 그리고 당연히 유니티에서도 그림자 기능을 제공해줍니다. 사용법 역시 체크박스만 몇 번 해주면 되는 식으로 되어있어 손쉽게 그림자를 활성화시킬 수 있습니다. (공식 메뉴얼 : http://docs.unity3d.com/Manual/class-MeshRenderer.html)

유니티에서 제공해주는 그림자는 그림자맵(ShadowMap) 기법을 사용하고있습니다. 이 기법은 모든 굴곡에 대응하고 자기 그림자(self-shadow)가 처리되는 등 높은 퀄리티를 보여주고 있지만 문제는 성능입니다. ShadowMap 기법은 그림자의 깊이를 저장하는 버퍼를 만들고난 뒤 픽셀 셰이더(Pixel Shader)에서 깊이를 비교하는 과정을 거칩니다. 또한, 넓은 영역을 커버하기위해 여러 구역으로 나누기도하고(cascade) 계단 현상을 없애기 위해 여러번 샘플링하여 필터링을 처리하는 등 부가적인 행위들이 추가됩니다. 그러다보니 랜더링 비용 중 그림자가 많은 부분을 차지하게 됩니다. PC에서는 이러한 셰도우맵 기법을 사용하기에 충분한 성능이 나오지만 모바일 장치에서 셰도우맵을 사용하는 것은 무리입니다.

이 글에서는 유니티에서 기본적으로 제공해주는 쉐도우맵 방식을 사용하지 않고 좀 더 저렴한 방식으로 그림자를 표현하는 것에 대하여 이야기하고자 합니다.


원형(circle) 평면 그림자

성능 문제때문에 모바일 장치에서는 그림자를 동그라미로 간단하게 표현하는 것이 일반적입니다. 원형의 텍스쳐를 사용하는 평면을 만들고 그걸 케릭터 밑에 배치하는 것이지요. 형태는 너무나도 간단하긴하지만 이 마저도 없는것과 있는것의 차이는 큽니다. 

이미지 : 레이븐

구현도 간단하거니와 비용도 많이 들지 않습니다만 평면이 아니고서는 표현이 불가능하다는 단점은 있습니다. 산이나 계단같이 평면이 아닌 구역에서는 그림자가 평면인게 티나기때문에 이질감이 있는 것이지요. 하지만 요즘 대부분의 모바일 게임은 평면상에서 이루어지고, 탑뷰등 카메라가 고정되어있으므로 널리 쓰이고 있습니다.

화면상에 작게만 보이거나 그림자 영역이 티가 많이 나지 않으면 이러한 간단한 동그라미 형태만으로도 충분합니다. 하지만 케릭터가 클로즈업되는 등 그림자의 화면 비중이 커지면 다소 어색해보일수도 있습니다. 이러한 경우는 케릭터 전체 크기의 동그라미가 아닌 발 크기의 동그라미 두개를 각각의 발 위치에 붙여서 이질감을 완화시킬 수도 있긴 합니다.

그래도 아직 부족해보이긴 합니다. 엄밀히 말하자면 바닥과 발 사이의 AO를 표현해준 것이지 케릭터 전체의 그림자를 표현해준 것이 아니기 때문입니다. 완벽해보이려면 케릭터의 형태 그대로를 따라서 바닥에 그림자가 맺혀야합니다. 즉, 실시간 그림자가 필요합니다.


렌더 타겟 텍스쳐(Render Target Texture)

렌더 텍스쳐를 활용하면 손쉽게 이를 해결할 수 있습니다.렌더 텍스쳐는 카메라가 바라보는 장면을 화면에 바로 그려주는 것이 아니라 텍스쳐로 그려주는 기능을 제공해줍니다. ( 공식 메뉴얼 : http://docs.unity3d.com/Manual/class-RenderTexture.html) 그러기 위해서는 오브젝트에 정상적으로 그리는 메시 오브젝트 외에도 그림자로 그릴 용도의 메시 오브젝트를 추가해서 그림자로 그려줘야합니다.

우선 메인 카메라 외에 그림자용 카메라를 생성합니다. 그림자용 레이어를 추가해서 그림자용 메시만 그리도록 설정해놓고 Target Texture를 하나 생성하여 설정해줍니다. 그림자용으로 추가한 메시 역시 레이어를 설정해놓고 검은색으로 드리도록 매터리얼을 설정해줍니다. 그러면 Target Texture에는 다음과 같이 그림자 오브젝트가 그려집니다. 

케릭터 주변의 적절한 위치에 평면을 설치한 후, 그림자용으로 만든 Target Texture를 평면에 셋팅해주면 그림자가 완성됩니다.

Target Texture의 해상도만 높으면 텍스쳐 필터링덕에 자연스레 부드러운 그림자(soft shadow)도 가능해진다는 이점이 있습니다. 하지만 현실적으로는 메모리 문제 때문에 해상도를 높게 잡을 수가 없습니다. 그러다보니 타겟 텍스쳐의 해상도문제로 그림자에 계단 현상이 발생하게 됩니다. 또한, 케릭터가 점프하는 등 에니메이션에 따라 일부 영역을 벗어나면 그림자가 잘려나가는 등 부작용이 많으므로 완벽한 해결책이 되지는 못합니다.


메시 평면 그림자

여기서 잠깐 그림자가 무엇인지에 대해 짚고 넘어가고자 합니다. 우리가 일반적으로 생각하는 그림자라는 것은 어떤 사물에 의해서 빛이 차폐되는 현상을 뜻합니다. 즉, 빛이 사물에 닿으면 그 사물 뒤의 영역에는 빛이 닿지 않는 것이지요. 

이미지 출처 : http://news.mynavi.jp/column/graphics/020/?route=blog

그 말인 즉슨, 어떤 캐릭터의 그림자를 평면에 표현하기위해서는 케릭터의 형태를 빛의 방향으로 평면에 투영시켜서 그리면 된다는 것입니다. 이 경우 메시의 원형으로 그대로 그리는 것이 아니라 빝의 방향으로 평면에 투영시켜주는 버텍스 변형이 필요합니다. 따라서, 이 메시는 유니티에서 기본적으로 제공해주는 셰이더 말고 커스텀한 셰이더를 사용하여야 합니다. 이 그림자를 표현해주기 위한 셰이더를 만들기 위해서는 약간의 수학을 끼얹어야 합니만 복잡한 수학은 아니므로 큰 걱정은 안하셔도 됩니다. 

일단, 다음과 같이 어느 한 점 P가 있고 광원 방향이 L이라고 하였을 때, 평면에 점P가 투영되는 위치는 점P'입니다. 점P와점P'을 이으는 선을 H라 하고 점P를 평면과 수직으로 이으는 선을 O라고 합니다. 선분 H와 O의 각을 θ라고 하였을 때 cosθ는 L과 단위화된(normalized)O의 내적입니다(L은 이미 단위화 되어있음). 이미 O는 평면과 수직인 상태이므로 최종적으로 cosθ와 L.y는 동일합니다. 그러면 아래 그림과 같이 빗변을 L, 맞변을 L.y로 삼는 빨간 삼각형을 이룰 수 있습니다. 

그러면 삼각비에 의해서 L:Ly = H:O가 되고 L은 단위화되어 길이가 1이므로 H = O/L.y 입니다. 즉, P'= P + O/L.y입니다. 이를 셰이더 코드로 표현하면 다음과 같습니다.

float4 vPosWorld = mul( _Object2World, v.vertex);

float4 lightDirection = -normalize(_WorldSpaceLightPos0); 

float opposite = vPosWorld.y - _PlaneHeight;

float cosTheta = -lightDirection.y; // = lightDirection dot (0,-1,0)

float hypotenuse = opposite / cosTheta;

float3 vPos = vPosWorld.xyz + ( lightDirection * hypotenuse );

o.pos = mul (UNITY_MATRIX_VP, float4(vPos.x, _PlaneHeight, vPos.z ,1));  

이 셰이더를 적용하여 검은색으로 그리면 오브젝트의 실루엣을 따르는 그림자가 평면에 나타나게됩니다. 확실히 단순한 동그라미로만 그리는 것 보다는 자연스러운 그림자에 가깝게 보입니다. 

모델 : https://www.assetstore.unity3d.com/kr/#!/content/22840


보완

그러나 현 상태만으로는 한가지 문제가 있습니다. 그림자를 좀 더 자연스럽게 보이게 하기 위해 알파블렌딩(alpha blending)을 적용하면 다음과 같이 그림자 내부에 아티팩트가 생깁니다. 평면에 오브젝트를 블렌딩하여 그릴 때 같은 위치에 여러 면이 겹쳐져서 그려지기 때문입니다. 아래 그림에서는 몸통이나 머리 위에 팔이 덧그려지면서 실루엣 내 그림자 농도가 달라지는 현상이 생겨버립니다.

이러한 형상을 없애려면 이미 그림자가 그려진 픽셀에는 덧 그려지지 않게 하는 작업이 필요합니다. 이를 위해서는 스텐실(stencil) 버퍼를 활용하면 됩니다. 스텐실 버퍼는 일종의 마스킹(masking)의 개념입니다. 이미 그려진 픽셀은 또 그려지지 않도록 마스킹처리하는 것입니다. ( 공식 메뉴얼 : http://docs.unity3d.com/Manual/SL-Stencil.html) 그림자를 그리는 셰이더의 pass에 다음과 같이 스텐실을 사용하도록 선언해주면 중복된 픽셀에 그리지 않게 됩니다.스텐실을 적용하고나면 다음과 같이 블렌딩을 적용해도 아티팩트 없이 동일한 농도의 그림자를 그릴 수 있게됩니다.

Stencil {

Ref 0

Comp Equal

Pass IncrWrap

ZFail Keep

}


주의사항

이토록 평면이라는 전제만 존재한다면 그림자를 비교적 좋은 성능을 보장하면서도 손쉽게 표현 할 수 있는 방법들이 많습니다. 다만, 메시 그림자같은 경우는 메시를 한번 더 그리게 되므로 폴리곤이 적은 그림자용 메시를 별도로 마련하는 것이 좋을 것입니다. LOD를 사용한다면 LOD용 모델을 사용하면 되므로 추가적인 작업의 부담은 생기지 않을 것입니다. 또 다른 주의점으로는, 자기 그림자(self shadow)가 처리되지 않는다는 단점은 존재합니다만 어짜피 라이팅이 간단한 모바일에서는 대부분 자기 그림자를 원치 않을 것입니다. 



Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

여러분들도 아시다시피 유니티는 멀티플랫폼 엔진입니다. 그러다보니 당연히 빌드 결과물 외 에디터에서도 OpenGL과 DirectX 둘 다 지원을 합니다. 하지만 유니티를 실행하면 기본적으로는 실행 OS에 맞게 기본적으로 그래픽 API를 설정해줍니다. 즉, MAC이나 Linux로 수행하면 OpenGL모드로, MS Windows에서 수행하면 DirextX모드로 실행됩니다. 


OpenGL 모드 강제 지정

사실, 미미한 차이긴 하지만 OpenGL과 DirectX의 렌더링 결과가 다르긴 합니다. (프로그래머는 몰라요. 아티스트의 매의 눈으로 봐야 알아요;;) 따라서 개발하다보면 에디터에서도 OpenGL모드로 보면서 작업하고 싶을때가 있습니다. Windows에서도 OpenGL 모드로 보여줄 수 있습니다만 유니티 메뉴의 설정 창 어디에서 이를 변환해주는 항목은 존재하지 않습니다. 유니티 수행 시 커맨드라인 실행 인자로 "-force-opengl"을 주면 OpenGL 모드로 수행됩니다. 매번 cmd로 커맨드라인 열어서 수행하실 필요는 없고 바로가기에 인자로 추가해주시면 됩니다. Unity바로가기 아이콘 우클릭하신후, 대상(T) 항목의 맨 뒤에 "-force-opengl"을 붙여주시면 됩니다. 

그러고나서 유니티를 실행하면 상단 타이틀에 OpenGL모드로 수행되는중이라는 표시가 뜹니다.

마찬가지로 DirectX 9이나 DirectX11 모드로 강제 지정이 가능합니다. (자세한 내용은 공식 메뉴얼을 참고해주세요.) 


DX11 on DX9 GPU

그런데 가끔 DirectX 모드로 수행 시 화면의 색이 원래와는 다르게 나오는 경우가 존재합니다. 특히 노말맵을 쓰는 모델이 티가 많이 나는데, 확인해보면 노말맵이 전제적으로 붉은 느낌으로 변해버린 것을 발견하실 수 있습니다.

이러한 경우는 DirectX의 버젼이 맞지 않아서 발생하는 문제입니다, PC는 DirectX 9까지만 지원하는 GPU를 장착하고 있는데, 유니티는 DirectX 11 버젼으로 수행하기때문에 문제가 발생하는 것입니다. 때문에 타이틀바 상단에 DX11 on DX9 GPU라고 표시가 뜹니다.

이는 프로젝트 셋팅에서 DX11을 사용하지 않도록 설정해주면 해결이 됩니다. File > Build Settings > PC, Mac & Linux Standardalone > Player Settings > Other Settings > Use Direct3D 11 체크를 해제해주시면 됩니다.




Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

라이팅 작업 흐름을 비약적으로 개선시켜줄 수 있는 실시간 글로벌 일루미네이션(real-time Global illumination)은 Unity5의 새로운 그래픽 기능 중 하나입니다. 이에 초첨을 맞추어 이야기하고자합니다. 이 글은 유니티 블로그에 있는 "GLOBAL ILLUMINATION IN UNITY 5"글을 번역 및 요약한 글입니다.

글로벌 일루미네이션( Global illumination, 이하 GI)은 물리적인 현상에 기반한 빛의 이동에대한 시물레이션 결과입니다. 즉 3D 공간에서 빛이 면에 부딪혀서 어떻게 이동하는 지를 시물레이션 하는 것입니다. 이로 인해 게임의 사실성을 부각시켜 줄 수 있습니다. GI 알고리즘은 광원으로부터 직접 오는 빛 뿐만 아니라 다른 재질의 면에 반사되서 오는 간접 조명도 취합해서 계산합니다. 하지만 일반적으로는 간접 조명은 게임에서 실시간으로 연산하기에는 무리였습니다.

GI를 식으로 표현하면 다음과 같습니다. 특정 점의 라이팅은 표면 점의 원래 라이팅인 Le과 부수적인 라이팅의 합으로 이루어집니다. Li는 반구 조명을, p는 반사된 조명을 나타냅니다.

이를 처리하기 위하여 쓰이는 대표적인 알고리즘중 하나는 path tracing입니다. 이는 CGI나 영화에서 널리 쓰이고 있습니다. 하지만 화면에 있는 라이팅, 매터리얼 등의 회면 전체의 이미지를 구성하는데 필요한 모든 정보가 매번 연산되어야합니다. 그러다보니 게임같은 실시간 렝더링에서 사용하기에는 적합하지 않습니다. 이에 대한 대안으로, 이미지 전체를 갱신하지 않고 노이즈로 처리해서 성능을 높이는 방법도 존재합니다. 하지만 노이즈가 티나도록 깜빡거림이 생기는 등의 부작용이 존재합니다.

이 외에도 GI를 처리하기 위한 많은 방법들이 연구되어왔지만, 대부분은 하이엔드 데스크탑 수준의 GPU와 많은 용량의 메모리가 필요합니다. 따라서 모바일을 비롯하여 다양한 플랫폼에서 사용될 수 있는 방안이 필요합니다.


Enlighten

Enlighten(이하 인라이튼)은 이미 배틀필드4, 메달 오브 아너 워파이터 등 여러 AAA급 게임에서 사용되어 검증이된 뿐만 아니라 모바일에서까지 GI를 가능케해주는 훌륭한 솔루션입니다. 기본적인 시각적 정보들(예를 들어 위의 식에서 우항의 적분 부분)이 미리 연산되어 있으면 실시간 으로 라이팅 소스를 변경 처리하는 것이 가능해집니다.

인라이튼은 다음 사항들을 동적으로 변경하는 것을 가능케 합니다.

  • 라이트 소스
  • 환경 라이팅
  • 머티리얼 속성

GI가 사뮬레이션되는 지오메트리는 정적이어야합니다. 하지만 동적 오브젝트는 라이트프로브에 의해서 동적으로 라이팅이 변경 될 수 있습니다. 이 라이트프로브는 실시간으로 정적인 오브젝트의 GI를 업데이트 할 수 있습니다. 이를 위해 인라이튼은 실시간으로 GI를 시뮬레이션 하기 위한 데이터를 미리 연산해놓습니다. 이 데이터는 OSX, Windows, Linus, iOS, 안드로이드, iOS 등등의 다양한 플랫폼의 런타임 모듈에서 사용됩니다.

인라이튼은 다음 사항들을 만들어냅니다.

  • 실시간 라이트맵
  • 실시간 라이트프로브
  • 실시간 큐브맵

다음 예시 이미지들은 Enlighten을 이용하여 그려진 화면입니다. 라이팅들은 완벽한 동적 라이팅으로 셋팅되어있고 변화가 즉각적으로 이루어집니다. 

이 이미지는 밝은 낮을 나타냅니다. 태양이 더 강하고 높이 위치합니다.하늘은 더 푸르고 밝습니다.

흐린 날씨의 경우에는 환경 라이팅이 우중충하고 채도가 낮습니다. 태양의 세기는 약해졌습니다. 앰비언트 라이팅 위주입니다.

마지막으로 해질녘 노을빛의 느낌을 내는 모습입니다. 

이 테크닉을 사용함으로써 하루 동안의 시간 흐름을 표현할 수 있습니다. 이로 인해 게임이 매우 사실적이게 보여 줄 수 있습니다.


인라이튼 사전 연산 (precompute)

정적인 지오메트리는 GI 솔루션 시스템에서 효과적으로 관리됩니다. 사전 연산(precompute) 단계에서 인라이튼은 씬을 여러 시스템 태스크로 쪼갠 후 이를 방대한 병렬 파이프라인에서 나누어 연산합니다. 사전 연산 과정을 거친 후 시스템 태스크간의 지오메트리 연결 정보가 구성됩니다. 이 정보들은 실시간으로 간접광을 제어하는데 사용 될 수 있습니다. 이어한 덕에 벽의 파괴나 문이 열리는 상황 등 라이팅이 변화되는 상황이 반영 될 수 있습니다.

인라이튼 런타임 

인라이튼은 데스크탑PC나 차세대콘솔 뿐만 아니라 하이엔드 모바일 장치에서도 작동합니다. 이는 CPU 스레드에서 비동기적 연산으로 돕니다만, 모바일에선 동적 라이팅과 그림자의 GPU 연산이 이슈다보니 모바일에서는 처리 가능한 동적 라이팅의 갯수가 제한됩니다. 하지만 발광색(emissive) 변경은 자유롭습니다. 발광색의 정보는 비록 저해상도이긴 하나 인라이튼에 인코딩된 정보로 연산을 하기 때문입니다.

모바일 장치(ARM 태블릿)에서 작동하는 영상:


베이킹(Baking)

어떤 게임들은 라이팅을 미리 굽는(baking) 과정이 매우 적절한 선택이 될 수 있을 것입니다. 유니티5에서는 라이트소스, 발광 재질, 환경 라이팅 등이 baked 및 리얼타임으로 태그 될 수 있습니다. baked는 이전버젼(4.x)와 같은 방식으로 베이킹 되는 것 의미합니다. 동적 라이트는 인라이튼 런타임에서 처리합니다. baked와 real-time은 이질감 없이 합성됩니다.

유니티5의 라이트맵은 여러 컴포넌트로 나뉩니다. 직접광, 간접광, 직접광 지향성, 간접광 지향성, AO 등 5개의 라이트맵으로 나뉘어집니다. 이 라이트맵 컴포넌트들은 실시간으로 합성하게 됩니다. 또한, 이는 에디터에서 컨트롤 가능합니다. 예를 들어 간접광만 증가시키는 것이 불과 몇 초 안에 이루어질 수 있습니다.


라이팅 워크플로우(workflow)

인라이튼은 실행중인 게임안에서만 실시간 GI를 제공하는 것은 아닙니다. 에디터에서 작업하는 과정에서도 실시간 GI가 이루어집니다. 인라이튼의 주요 장점 중 하나는 아티스트에게 엄청나게 개선된 워크플로우를 제공해준다는 것입니다. 이는 라이팅 작업이빠른 이터레이션으로 이루어 질 수 있기 때문입니다. iterative모드가 추가됨으로써 명시적으로 굽는 과정 필요가 없어졌습니다. 씬의 사전 연산 정보들이 실시간으로 구워지고, 사용자가 이 과정중에 일일이 개입할 필요가 없습니다. 에디터는 지속적으로 씬의 변경 사항 확인하여 자동적으로 라이팅 정보를 반영해주는 작업을 수행합니다. 대부분의 작업은 즉각적으로 반영됩니다.

라이팅 워크 플로우 영상 :





Posted by ozlael
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

이 글에서는 Unity에서 LOD(Level of Detail)을 사용하는 것에 대한 이야기를 해볼까 합니다 Object LOD에 촛점을 맞추고 있으며 Shader LOD와는 별개의 이야기입니다.


LOD란 무엇인가?

여러분이 게임 광고를 기획한다고 가정해보죠. 아리따운 누나가 대규모의 적들과 전투하는 켄셉을 잡고 모델을 섭외합니다. 역시 게임 광고 모델은 뭐니뭐니해도 아이유지요. 하지만 문제는 예산이네요. 예산을 최대한 절약 할 방법이 없을까 고민해봅시다.

이건 어떨까요? 기본적으로 모델을 아이유를 캐스팅하지만 하지만 굳이 클로즈업을 해도 되지 않는 장면이라면요? 아이유와 똑같이 생겼지만 아이유보다는 몸값이 저렴한 신봉선으로 대체해도 되지 않을까요? 클로즈업이 필요한 씬에서는 아이유를, 그렇지 않은 씬에서는 신봉선을 이용한다면 광고료를 아낄 수 있지 않을까요? (물론 실제로는 말도 안되는 소리입니다 ㅋ ) 이러한 전략이 바로 Level of Detail의 컨셉입니다. 

이미지 : TIG, 마영전

게임같은 Real-time Rendering에서는 최대한 렌더링 비용을 절약하는 것이 중요합니다. 화면에 작게 그려지는 모델을 그리는데 비싼 비용을 지불 할 필요가 없는 것이지요. 때문에, 카메라와 가까이 있거나 큰 오브젝트는 높을 퀄리티로 그리고, 멀리 떨어져 있거나 작은 오브젝트는 낮은 퀄리티로 표현하거나 연산처리하여 퍼포먼스를 향상시킬 수 있습니다. 이러한 기법을 Level of Detail(이하 LOD)이라 부릅니다. 말 그대로 디테일의 단계를 두어서 상황에 맞게 표현하는 것이지요.이러한 컨셉은 Material LOD, Terrain LOD, Mesh LOD, Bone LOD 등 여러 방면에서 쓰입니다. 이 중 Mesh 를 LOD 시키는 것에 대한 이야기를 할까 합니다.

https://www.assetstore.unity3d.com/kr/#!/content/8855


Unity에서의 LOD

LOD는 3D 게임 개발에 있어서 꼭 필요한 기능이고 당연히 Unity에서도 제공을 합니다. 다만 Pro Version에서만 제공이 되고 있어서 Free Version에서는 사용이 불가능합니다.(2015년 1월 기준) 사용법도 간단합니다. 오브젝트에 LOD Group 컴포넌트를 추가하고 LOD 0에는 가까이서 보일 오브젝트를 설정해주고, LOD 1에는 멀리서 보일 오브젝트를 설정해주면서 조절해주면 됩니다. 카메라 아이콘을 슬라이드해가면서 바로바로 확인하면서 편집이 가능합니다. 

자세한 설명은 공식 메뉴얼을 참고해주세요. (공식 메뉴얼 :  http://docs.unity3d.com/Manual/LevelOfDetail.html)


LOD 툴

유니티로 게임을 개발 할 때 에셋스토어를 활용하면 게임 모델을 손쉽게 구할 수 있습니다. 판타지, 레이싱, SF등 다양한 장르의 배경 및 케릭터 오브젝트들이 있어서 다운로드만 받으면 즉시 사용이 가능합니다. 유료는 물론 무료 모델들도 많이 있어서 저도 에셋스토어에서 다운로드받아 게임 제작을 하고 있습니다. 하지만 몇 가지 문제점들이 존재합니다.

첫째, 모든 리소스들이 LOD 시스템을 대응하고 있지는 않다는 것입니다. 이러한 경우는 Low quality 모델을 따로 생성해줘야 하지요. 아티스트팀이 따로 존재한다면 아티스트가 직접 Low quality 모델을 제작해줄 수도 있겠지요. 하지만 아티스트가 아예 없거나 일손이 모자란다면 자동으로 모델을 만들어주는 툴이 필요할겁니다. 

둘째, PC 및 콘솔 대응 리소스들도 많이 있습니다. 모델 하나가 만 단위가 넘는 폴리곤을 가지고 있는 경우도 허다합니다. 타겟이 PC 및 콘솔이라면 케릭터에 그정도를 투자할 수도 있을 것입니다. 하지만 모바일 타겟이라면 거의 불가능에 가까운 무거운 모델들일 것입니다. 이러한 모델들을 모바일에서 사용 할 수 있을 정도로 폴리곤을 줄여주는 툴이 필요할겁니다.

셋째, 파츠가 많이 나뉘어져 있는 모델도 존재합니다. 건물 하나가 문, 지붕 벽 등 몇 파츠로 나뉘어져 있거나, 애초아 드럼통 벽돌 등 다른 오브젝트등을 조합해서 하나의 배경 프랍으로 사용해야 하는 경우도 존재할 것입니다. 이러한 경우 메시 및 매터리얼의 갯수 등 상황에 따라 드로우콜이 늘어나게 됩니다. 드로우콜이 많아지면 성능이 느려지기 때문에 이 오브젝트들을 하나의 메시와 매터리얼로 만들어줘는 툴이 필요할겁니다. (드로우콜이 성능에 왜 영향을 미치는 지는 나중에 따로 설명하는 시간을 갖도록 하겠습니다.) 

이러한 이유들로 많은 LOD 관련 툴들이 존재합니다. 당연히 에셋스토어에도 LOD 툴들이 많이 있습니다.


Simplygon

그 중 Simplygon(심폴리곤이 아니라 심플리곤입니다 ㅋ)을 소개할까 합니다. Simplygon은 무료입니다. 몇 가지 제약이 있긴 합니다만 기본적으로는 무료입니다. 일단 에셋스토어에서 다운로드후 Import하시면 Unity 메뉴 Window에 Simplygon 항목이 생깁니다.(https://www.assetstore.unity3d.com/kr/#!/content/10144)


이제 실제로 사용하는 예를 보여드릴까 합니다. 우선 에셋스토어에서 모델을 하나 받습니다. 

https://www.assetstore.unity3d.com/kr/#!/content/10739

언니가 무섭고 이쁘긴 한데 폴리곤이 너무 많습니다. 세상에나 2만7천 폴리곤이라니 모바일에서는 전혀 써먹지를 못하겠네요.

이제 이 모델을 Simplygon으로 폴리곤을 반토막 내볼까 합니다. 모델을 선택 후 Simplygon 창을 클릭하면 다음과 같이 네개의 하위 탭이 존재합니다. (가입이 안되어있으시면 계정 등록을 하시면 됩니다. 일단 무료 계정으로 가입하셔도 충분합니다.) 반토막낼거니 Quick Start탭의 Reduction 항목에 50으로 입력사고 아래의 노란색 마크 버튼을 클릭합니다.

그러면 모델 데이터를 Simplygon서버에 보내고 받아오는 과정이 이루어지면서 Manage Jobs 탭의 톱니바퀴 아이콘이 움직입니다. Manage Jobs 탭을 열어 Download Assets Automatically를 체크해줍니다.

 그리고나서 status 상태가 100%이 되면 처리된 에셋을 자동으로 import하고 LODs 폴더에 처리된 모델이 생깁니다. 

확인해볼까요? 일단 육안으로는 큰 차이를 모르겠네요.

하지만 폴리곤 수를 보면 2만7천 폴리곤이였던 모델이 1만3천 폴리곤으로 확 줄었습니다. RPG처럼 케릭터가 많이 나오는 게임에서는 사용이 불가능하겠지만 대전격투게임처럼 케릭터가 적게 나오는 게임에서는 쓸만하겠네요.

한번 더 줄여볼까요? 이번엔 원본에서 3%로 설정해서 돌려본 결과입니다. 원본과 비교해보면 얼굴이 많이 못생겨지긴 했지만 전체적인 실루엣은 어느정도 유지해주고 있습니다.

하지만 폴리곤은 540여개로 대폭 줄었습니다.

멀리 두고 비교해보면 두 모델 간의 차이는 눈에 띄지 않을 정도입니다.

이번엔 다른 모델도 한번 살펴볼까요? 이 두 골렘은 작게 해서 보면 차이는 없어보입니다.

https://www.assetstore.unity3d.com/kr/#!/content/13631

하지만 폴리곤은 3배 이상 차이가 납니다.

이런 식으로 SImplygon같은 툴을 이용하면 LOD에 필요한 데이터를 쉽게 생성 할 수 있습니다. 더 나아가서는 PC 및 콘솔을 타겟으로 만들어진 데이터를 모바일에서 사용 할 수 있도록 폴리곤을 줄이는 용도로도 사용이 가능합니다. 물론 툴로 줄이는 것 보다는 아티스트가 이태리 장인정신으로 한땀 한땀 줄여주는 것이 가장 퀄리티는 좋습니다. 하지만 그렇지 못하는 상황이라면 툴의 도움이 정말 요긴하게 쓰일 것입니다. 

SImplygon의 추가적인 사용법은 공식 튜토리얼을 참고해주세요. (https://www.youtube.com/watch?v=qEyPVNGxGb8)


케릭터 LOD

이제 메시는 생성했으니 실제 LOD가 작동하는 케릭터를 만들어보도록 하죠. non-skinned mesh를 이용하는 오브젝트(예를 들면 건물이나 배경 프랍들)의 LOD 처리는 어려울게 없습니다. 하지만, 에니메이션 처리 되는 skinned mesh 케릭터에게 LOD 처리하는 것은 조금 귀챦습니다. 일단 Simplygon으로 생성하기 이전의 원본 mesh와 Simplygon 처리 후의 LOD 메시간의 본 에니메이션 정보는 공유되지 않습니다.

우선, Simplygon으로 생성 한 케릭터 중 Level 0 즉 가까이 있을 때 그릴 용도로 사용 할 케릭터를 씬에 올려놓습니다. 그러면 케릭터 오브젝트 바로 하위에는 본 계층구조 오브젝트와 skinned mesh 오브젝트가 있습니다. 이 메시 오브젝트를 Duplicate하여 사본을 만듭니다. 다른 오브젝트나 prefab에 있는 메시를 가져오면 안됩니다.

 그 후, 새로 만들어진 사본의 Skinned Mesh Renderer의 Mesh를 Level 1 즉 멀리 있을 때 그릴 용도로 사용 할 메시로 바꿔치기해줍니다.

이제 케릭터 오브젝트에 LOD Group 컴포넌트를 추가해주고 LOD 0, LOD 1 각각 메시를 설정해줍니다.

이제 케릭터가 하나의 에니메이터로 에니메이션하면서 거리에 따라 LOD 메시가 바뀌는 것을 확인 가능합니다.


마치며

게임 개발 시 얼마나 최적화를 시키느냐가 중요한 과제중 하나입니다. LOD 처리를 하는 것은 쉬우면서도 효과적인 최적화 방법중 하나입니다. 유니티는 이 기능을 쉽게 사용 가능하도록 제공해주고 있고 에셋스토어에는 보조장치들이 많이 존재합니다.SImplygon은 그 중 하나일 뿐이지 반드시 이것을 사용해야한다는 것은 아닙니다. 다만 LOD를 처리하는 예를 보여주기 위한 수단으로 소개해드린 것일 뿐입니다. 물론, 카메라가 고정되어있는 탑뷰(및 쿼터뷰)에서는 LOD 처리가 필요 없을 수도 있습니다. 하지만 요즘은 모바일 게임들도 탑뷰에서 탈피하는 게임도 많이 있어서 LOD의 필요성이 많아지게 될 것입니다. 

감사합니다.

Posted by ozlael
,