Unity 검색

모바일 기기에서 광원 베이킹된 프리팹 사용

그리고 저사양 휴대폰에서 60fps에 도달하기 위한 다른 간편한 트릭

최근 업데이트: 2019년 1월

이 페이지의 내용: MetalPop Games의 소프트웨어 엔지니어 미셸 마틴(Michelle Martin)이 가능한 많은 잠재적 플레이어에 도달할 수 있도록 다양한 모바일 디바이스에서 그들의 새로운 모바일 전략 게임인 Galactic Colonies를 최적화한 방법을 설명합니다.

MetalPop Games는 저사양 디바이스를 사용하는 플레이어가 프레임 속도의 감소 또는 디바이스 과열 없이 대규모 도시를 구축할 수 있도록 하는 데 있어 어려움을 겪었습니다. 그들이 어떻게 뛰어난 비주얼과 강력한 성능 사이의 균형을 이루었는지 살펴보세요.

저사양 기기에서 도시 건축

오늘날의 뛰어난 모바일 디바이스에서도 대규모의 멋진 게임 환경을 안정적인 프레임 속도로 실행하는 것은 여전히 쉽지 않은 일입니다. 오래된 모바일 디바이스에서 대규모 3D 환경 실행 시 60fps를 달성하는 것은 상당히 어렵습니다.

개발자로서 대부분의 사용자가 게임을 원활히 실행할 수 있는 충분한 하드웨어를 갖추고 있다고 가정하고 고사양 핸드폰을 대상으로 개발할 수도 있지만, 그렇게 하면 아직 오래된 디바이스를 사용하는 수많은 잠재적 플레이어가 배제되는 결과를 낳습니다. 이러한 플레이어는 모두 잠재 고객이므로 이들을 제외하지 않는 것이 좋습니다.

Galactic Colonies 게임에서 플레이어는 외계 행성을 식민지로 만들고 수많은 개별 건물로 이루어진 거대한 식민지를 건축할 수 있습니다. 소규모 식민지는 12개의 건물로만 구성되며 대규모 식민지는 수백 개의 건물로 구성될 수 있습니다.

다음은 파이프라인을 구축하기 시작했을 때의 목표 목록입니다.

  • 수많은 건물이 있는 거대한 맵 제작
  • 저렴하거나 오래된 모바일 디바이스에서 빠르게 실행
  • 멋진 광원 및 그림자
  • 간편하고 관리하기 쉬운 프로덕션 파이프라인
모바일 게임의 조명: 일반적인 문제점

게임의 3D 모델이 보기 좋으려면 좋은 조명이 필수입니다. 물론 이는 Unity에서는 구현하기 매우 쉽습니다. 레벨을 설정하고 동적 조명을 배치하면 완성입니다. 만약 성능을 관리해야 한다면 조명을 베이킹하고 포스트 프로세싱 스택(Post Processing Stack)을 통해 SSAO나 다른 보기 좋은 효과를 추가하세요. 완성되었으면 출시하세요!

모바일 게임에서 조명을 설정하려면 대체로 여러 트릭과 해결책이 필요합니다. 예를 들어, 고성능 기기를 겨냥하는 것이 아닌 이상 포스트 프로세싱 효과는 사용하지 않는 것이 좋습니다. 마찬가지로 동적 조명이 가득한 커다란 씬은 프레임 속도를 크게 감소시킵니다.

아무리 데스크톱 PC라도 실시간 조명은 많은 비용이 들 수 있습니다. 모바일 기기에서는 리소스가 더욱 제한적이며 원하는 만큼의 멋진 기능을 모두 추가할 수 없습니다.

광원 베이킹으로 문제 해결

모바일 기기에서 리소스는 크게 제한되며 씬에 화려한 조명을 과도하게 배치하여 사용자의 배터리를 필요 이상으로 소모해서는 안됩니다.

기기를 계속해서 하드웨어의 한계점으로 밀어붙인다면 휴대폰이 과열되며 결국 휴대폰 보호를 위해 자동으로 전원이 꺼집니다. 이런 경우를 방지하기 위하여 실시간 섀도우를 필요로 하지 않는 조명을 전부 베이킹해야 합니다.

모바일용 Unity 광원 베이크된 프리팹 MetalPop Games Galactic Colonies

(많은 양의 건물이 있는 Galactic Colonies의 일반적인 레벨)

Galactic Colonies에서는 게임 세계가 플레이어에 의해 실시간으로 구축되기 때문에 이는 불가능했습니다. 계속해서 새로운 지역이 발견되고, 추가적인 건물이 구축되거나 기존의 건물이 업그레이드되어 효율적인 조명 베이킹이 불가능합니다.

만들 수 있을 때까지 흉내내기

조명 베이킹은 (정적인) 씬에 대해 하이라이트 및 섀도우를 미리 계산하고 해당 정보를 라이트맵에 보관하는 과정을 거칩니다.

이 과정으로 렌더러에게 모델을 더 밝거나 어둡게 만들도록 지시하여 조명의 느낌을 재현합니다.

이 방식으로 렌더링하면 비용이 높고 느린 조명 계산이 이미 오프라인으로 수행되어 런타임에서는 렌더러(셰이더)가 텍스처에서 결과물만을 검색하기 때문에 속도가 매우 빠릅니다.

여기서 단점은 추가 라이트맵 텍스처를 배포해야 하기 때문에 빌드의 크기가 증가하며 런타임에 추가적인 텍스처 메모리가 필요하다는 점입니다.

또한 메시가 라이트맵 UV를 필요로 하며 크기가 약간 커지기 때문에 일정 공간이 소모됩니다.

하지만 전체적으로 속도가 상당히 향상됩니다.

모바일용 Unity 광원 베이크된 프리팹 MetalPop Games

(라이트맵이 있는 건물과 없는 건물)

하지만 사용자에 의해 계속해서 변화하는 동적인 세계에서는 단순히 Bake 버튼을 눌러 베이크를 진행할 수 없습니다.

고도로 모듈화된 씬을 위한 조명을 베이킹할 경우 여러 문제점이 있습니다.

쉽지 않은 프리팹 광원 베이킹

먼저 Unity에서 광원 베이킹 데이터는 저장되어 씬 데이터와 직접 연결됩니다. 개별적인 레벨과 사전 구축된 씬이 있고 동적 오브젝트의 수가 적을 경우 이는 문제가 되지 않습니다. 광원을 사전 베이킹하여 작업을 완료할 수 있습니다.

하지만 레벨을 동적으로 생성할 경우 이 방법은 불가능합니다. 도시 건축 게임에서는 세계가 사전에 생성되지 않습니다. 대신 대부분이 플레이어가 무엇을 어디에 건축할지 결정함에 따라 동적이고 실시간으로 구축됩니다. 일반적으로 이를 구현하기 위해서는 플레이어가 무언가를 건축할 때마다 프리팹을 인스턴스화해야 합니다.

이 문제에 대한 유일한 해결 방법은 관련된 모든 광원 베이킹 데이터를 씬이 아닌 프리팹 안에 보관하는 것입니다.

안타깝게도 사용하고자 하는 라이트맵과 해당 좌표 및 스케일의 데이터는 프리팹으로 쉽게 복사할 수 없습니다.

광원 베이킹된 프리팹을 위한 파이프라인 구축

광원 베이킹된 프리팹을 다루는 안정적인 파이프라인을 구축하기 위한 최선의 방법은 프리팹을 별도의 씬, 즉 여러 개의 씬에 생성하여 필요할 때 메인 게임에 불러오는 것입니다.

각 모듈식 조각은 광원 베이킹되며 필요할 때 게임에 로드됩니다.

Unity에서 광원 베이킹의 작동 방법을 살펴보면, 광원 베이킹된 메시란 사실상 메시에 추가적인 텍스처를 적용하고 약간 밝게 또는 어둡게 하거나 경우에 따라 색을 입힌 것입니다. Unity의 광원 베이킹 과정에서 생성되는 라이트맵 텍스처와 UV 좌표만 있으면 됩니다.

Unity는 광원 베이킹 과정에서 라이트맵 텍스처의 위치를 나타내는 새로운 UV 좌표 세트와 개별 메시에 대한 오프셋 및 스케일을 생성합니다. 이 좌표는 조명을 다시 베이킹할 때마다 변경됩니다.

UV 채널 활용 방법

이 문제를 해결하기 위해서 먼저 UV 채널의 작동 원리와 가장 효과적인 활용 방법을 이해하는 것이 좋습니다.

각 메시는 여러 UV 좌표 세트(Unity에서는 UV 채널로 지칭)를 보유할 수 있습니다. 대부분의 경우 여러 텍스처(디퓨즈, 스페큘러, 범프 등)가 이미지 내에서 동일한 위치에 정보를 저장하기 때문에 하나의 UV 세트로 충분합니다.

하지만 오브젝트가 라이트맵과 같은 텍스처를 공유하여 하나의 큰 텍스처 내에서 매우 특정한 위치의 정보를 찾아야 할 경우 대체로 UV 세트를 추가하여 공유된 텍스처와 사용해야 합니다.

여러 UV 좌표를 사용하는 것의 단점은 추가적인 메모리를 소모한다는 것입니다. 만약 한 세트가 아닌 두 세트의 UV를 사용한다면 메시의 모든 버텍스에 두 배의 UV 좌표가 적용됩니다. 각 버텍스마다 두 개의 부동 소수점 숫자를 추가로 저장하고 렌더링시 함께 GPU로 업로드하게 됩니다.

프리팹 제작

Unity에서 일반 광원 베이킹 기능을 사용하여 좌표 및 라이트맵을 생성합니다. 엔진은 모델의 두 번째 UV 채널에 라이트맵 UV 좌표를 작성합니다. 여기서 참고할 것은 모델을 언래핑해야 하기 때문에 해당 작업에서는 기본 UV 좌표 세트를 사용할 수 없습니다.

박스의 모든 면에 동일한 텍스처가 적용되었다고 가정해보세요. 박스의 개별 면은 동일한 텍스처를 재사용하기 때문에 동일한 UV 좌표를 갖습니다. 하지만 라이트매핑된 오브젝트의 경우 박스의 각 면이 개별적으로 빛과 그림자의 영향을 받기 때문에 동일한 UV 좌표를 사용할 수 없습니다. 각 면마다 라이트맵 내에서 개별 공간과 조명 데이터가 필요합니다. 이로 인해 새로운 UV 세트가 필요합니다.

그렇기 때문에 새로운 광원 베이킹된 프리팹을 설정하기 위해서는 텍스처와 좌표를 잃어버리지 않도록 저장한 다음 프리팹으로 복사하면 됩니다.

광원 베이킹이 끝나면 씬의 모든 메시를 거쳐 실행되는 스크립트로 오프셋 및 스케일 값을 적용하여 UV 좌표를 메시의 실제 UV2 채널에 작성합니다.

메시를 수정하기 위한 코드는 비교적 간단합니다.

Mesh meshToModify = GetComponent().sharedMesh;
Vector4 lightmapOffsetAndScale = GetComponent().lightmapScaleOffset;

Vector2[] modifiedUV2s = meshToModify.uv2;
for (int i = 0; i < meshToModify.uv2.Length; i++)
{
    modifiedUV2s[i] = new Vector2(meshToModify.uv2[i].x * lightmapOffsetAndScale.x +
    lightmapOffsetAndScale.z, meshToModify.uv2[i].y * lightmapOffsetAndScale.y +
    lightmapOffsetAndScale.w);
}
meshToModify.uv2 = modifiedUV2s;

더 구체적으로 말하자면 베이킹 과정에서 메시에 대한 추가적인 최적화 작업을 할 예정이기 때문에 해당 작업은 메시의 원본이 아닌 복사본에 적용됩니다.

복사본은 자동으로 생성되어 프리팹에 저장되며 커스텀 셰이더 및 새로 생성된 라이트맵을 통해 신규 머티리얼을 할당받습니다. 이를 통해 원본 메시는 변경되지 않으며 광원 베이킹된 프리팹은 즉시 사용이 가능합니다.

커스텀 라이트맵 셰이더

커스텀 라이트맵 셰이더로 인해 워크플로가 매우 간단해집니다. 그래픽의 스타일 및 모습을 업데이트하려면 해당 씬을 열어서 만족할 때까지 수정하고 자동화된 베이킹 및 복사 과정을 실행하세요. 이 과정을 마치면 모든 작업이 완료되며 게임은 업데이트된 조명이 적용된 프리팹 및 메시를 사용합니다.

실제 라이트맵 텍스처는 커스텀 셰이더에 의해 추가되며, 렌더링시 라이트맵을 모델에 두 번째 조명 텍스처로 적용합니다.

셰이더는 매우 단순하며 색상 및 라이트맵을 적용하는 것 이외에는 가짜 스페큘러/광택 효과를 계산합니다.

Shader "Custom/LightmappedPrefabWithSpec"
{
  Properties
  {
    _MainTex("Base (RGB)", 2D) = "white" {}
    _Lightmap("Lightmap", 2D) = "white" {}
    _Specmap("Specmap", 2D) = "white" {}
    _SpecularAtt("Glossiness", Range(0.1, 2)) = 0.5
    _SpecularAmt("Specular", Range(0, 1)) = 0.5
  }

  SubShader
  {
    Tags{ "Queue" = "Geometry+1" }
    Pass
    {
      CGPROGRAM

      // Defining the name of the vertex shader
      #pragma vertex vert

      // Defining the name of the fragment shader
      #pragma fragment frag


      // Include some common helper functions,
      // specifically UnityObjectToClipPos and DecodeLightmap.
      #include "UnityCG.cginc"

      // Color Diffuse Map
      sampler2D _MainTex;
      // Tiling/Offset for _MainTex, used by TRANSFORM_TEX in vertex shader
      float4 _MainTex_ST;


      // Lightmap (created via Unity Lightbaking)
      sampler2D _Lightmap;
      // Tiling/Offset for _Lightmap, used by TRANSFORM_TEX in vertex shader
      float4 _Lightmap_ST;

      // Grayscale Map indicating which parts of the models have specular
      // Note: _Specmap_ST is not needed, as this map is using the same
      // UVs as for the _MainTex.
      sampler2D _Specmap;

      // This is the vertex shader input: position, UV0, UV1, normal
      // UV1 (= second UV channel) needed for the lightmap texture coordinates
      struct appdata
      {
        float4 vertex   : POSITION;
        float2 texcoord : TEXCOORD0;
        float2 texcoord1: TEXCOORD1;
        float3 normal: NORMAL;
      };

      // This is the data passed from the vertex to fragment shader
      struct v2f
      {
        float4 pos  : SV_POSITION; // position of the pixel
        float2 txuv : TEXCOORD0; // for accessing the diffuse color map
        float2 lmuv : TEXCOORD1; // for accessing the light map
        float3 normalDir : TEXCOORD2; // for fake specular
      };

      // This is the vertext shader, doing nothing special at all.
      // Most notably it is calculating the surface normal, because that
      // is needed for the fake specular lighting in the fragment shader.
      v2f vert(appdata v)
      {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.txuv = TRANSFORM_TEX(v.texcoord.xy, _MainTex); // using _MainTex_ST
        o.lmuv = TRANSFORM_TEX(v.texcoord1.xy, _Lightmap); // using _Lightmap_ST

        // Calculating the normal of the vertex for the fragment shader
        float4x4 modelMatrixInverse = unity_WorldToObject;
        o.normalDir = normalize(mul(float4(v.normal, 0.0), modelMatrixInverse).xyz);

        return o;
      }

      uniform float _SpecularAtt;
      uniform float _SpecularAmt;

      // Fragment Shader
      half4 frag(v2f i) : COLOR
      {
        // Reading color directly from the diffuse texture, using first UV channel
        half4 col = tex2D(_MainTex, i.txuv.xy);
        // Reading specular (on/off) value from spec map texture
        half4 specVal = tex2D(_Specmap, i.txuv.xy);
        // Reading lightmap value from the lightmap texture
        half4 lm = tex2D(_Lightmap, i.lmuv.xy);

        // Fake specular light angle calculation with a hard-coded light direction
        half3 th = normalize(half3(0, 1, -0.25));
        float spec = max(0, dot(i.normalDir, th));

        // Adjusting by overall specular amount and glossyness (material parameters)
        spec = _SpecularAmt * pow(spec, 40.0 * _SpecularAtt);
        // We're just using red value of the specular texture, like a grayscale map,
        // although technically spec could be colored.
        // Example: float3 specCol = specVal * spec;
        spec = spec * specVal.r;

        // Calculating the final color of the pixel by bringing it all together
        col.rgb = min(half4(1,1,1,1), col.rgb * DecodeLightmap(lm) + col.rgb * spec);
        return col;
      }
      ENDCG
    }
  }
  Fallback "Diffuse"
}

다음은 위의 셰이더를 이용한 머티리얼 설정 이미지입니다.

모바일용 Unity 광원 베이크된 프리팹 머티리얼 설정 MetalPop Games

(커스텀 셰이더를 활용한 머티리얼 설정)

설정 및 정적 배칭

Galactic Colonies의 경우 프리팹이 설정되어 있는 네 개의 다른 씬이 있습니다. 이 게임에는 열대, 얼음, 사막 등 다양한 바이옴이 있으며 그에 맞게 씬을 나눕니다. 게임마다 요구사항이 다르며 그에 따른 씬의 개수의 차이가 있을 수 있습니다.

하나의 씬에 사용되는 모든 프리팹은 하나의 라이트맵을 공유합니다. 이는 프리팹이 공유하는 하나의 머티리얼에 추가된 하나의 텍스처를 의미합니다.

이를 통해 한 번의 드로우 콜로 모든 모델을 정적 상태로 렌더링하고 세계의 대부분을 배치 렌더링할 수 있었습니다.

모바일용 Unity 광원 베이크된 프리팹 베이크 레벨 MetalPop Games

(Unity 에디터의 베이크 단계)

모든 타일/건물이 구축된 광원 베이킹 씬에는 위치 추정된 하이라이트를 생성하기 위한 추가 광원이 있습니다. 설정 씬은 나중에 모두 베이킹되기 때문에 원하는 만큼의 조명을 배치할 수 있습니다.

베이킹 과정은 모든 필수 단계를 처리하는 커스텀 UI 다이얼로그에서 다뤄집니다. 이는 다음 사항을 확인합니다.

  • 모든 메시에 올바른 머티리얼 할당
  • 프로세스 도중 베이크할 필요 없는 모든 항목을 숨김
  • 메시를 조합/베이크
  • UV를 복사하고 프리팹을 생성
  • 모든 항목의 이름을 올바르게 지정하고 버전 관리 시스템에서 필수 파일을 확인

모바일용 Unity 광원 베이크된 프리팹 커스텀 인스펙터 MetalPop Games

(간편한 워크플로를 위한 커스텀 인스펙터)

올바른 이름이 지정된 프리팹은 메시로부터 생성하여 게임 코드가 이를 직접적으로 로드 및 활용할 수 있도록 합니다. 메타 파일 또한 이 과정 중에서 변경되어 프리팹의 메시에 대한 레퍼런스가 손실되지 않도록 합니다.

이 워크플로를 통해 원하는 데로 건물을 수정하고 조명을 설정한 후 스크립트를 이용하여 나머지 과정을 처리할 수 있습니다.

다시 메인 씬으로 돌아와 게임을 실행하면 원활하게 작동됩니다. 별도의 수동 작업 및 업데이트가 필요 없습니다.

가짜 조명 및 동적 비트

조명이 100% 사전 베이킹된 씬의 큰 단점 중 하나는 동적인 오브젝트 또는 움직임을 추가하기 어렵다는 것입니다. 물체가 그림자를 드리울 경우 실시간 조명 및 섀도우 계산이 필요하며, 이런 경우는 피하는 것이 좋습니다. 하지만 움직이는 오브젝트가 없다면 3D 환경이 정적이고 생동감 없게 보일 것입니다.

Unity는 뛰어난 비주얼과 빠른 렌더링을 우선순위로 두기 위해 제한 사항을 일부분 수용했습니다. 생동감 있고 움직이는 우주 집단 및 도시의 느낌을 생성하기 위해서 많은 오브젝트가 실제로 움직일 필요는 없습니다. 그리고 대부분의 경우 섀도우가 필요하지 않거나 섀도우가 누락되어도 큰 영향이 없었습니다.

Galactic Colonies의 경우, 먼저 모든 도시 건축 블록을 두 개의 프리팹으로 나누었습니다. 대부분의 버텍스와 메시의 복잡한 조각을 담고 있는 정적인 부분과 최소한의 버텍스만을 담고 있는 동적인 부분이 있습니다.

프리팹의 동적인 부분은 정적인 부분 위에 배치되는 애니메이션된 조각입니다. 이는 조명 베이킹되지 않으며 가짜 조명 셰이더를 통해 오브젝트가 동적으로 라이팅된 느낌을 흉내냅니다.

Shader "Custom/FakeLighting"
{
  Properties
  {
    _Color ("Color", Color) = (1,1,1,1)
    _Brightness ("Brightness", Range(0,1)) = 0.4
    _MainTex ("Albedo (RGB)", 2D) = "white" {}
  }

  SubShader
  {
    Tags { "RenderType" = "Opaque" }
    LOD 200
    Pass
    {

      CGPROGRAM

      // Define name of vertex shader
      #pragma vertex vert
      // Define name of fragment shader
      #pragma fragment frag

      // Include some common helper functions, such as UnityObjectToClipPos
      #include "UnityCG.cginc"

      float4 _Color;
      float _Brightness;

      // Color Diffuse Map
      sampler2D _MainTex;
      // Tiling/Offset for _MainTex, used by TRANSFORM_TEX in vertex shader
      float4 _MainTex_ST;

      // This is the vertex shader input: position, UV0, UV1, normal
      struct appdata
      {
        float4 vertex   : POSITION;
        float2 texcoord : TEXCOORD0;
        float3 normal: NORMAL;
      };

      // This is the data passed from the vertex to fragment shader
      struct v2f
      {
        float4 pos  : SV_POSITION;
        float2 txuv : TEXCOORD0;
        float3 normalDir : TEXCOORD2;
      };

      // This is the vertex shader
      v2f vert(appdata v)
      {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.txuv = TRANSFORM_TEX(v.texcoord.xy,_MainTex);

        // Calculating normal so it can be used for fake lighting
        // in the fragment shader
        float4x4 modelMatrixInverse = unity_WorldToObject;
        o.normalDir = normalize(mul(float4(v.normal, 0.0), modelMatrixInverse).xyz);

        return o;
      }

      // This is the fragment shader
      half4 frag(v2f i) : COLOR
      {
        // Reading color from diffuse texture
        half4 col = tex2D(_MainTex, i.txuv.xy);

        // Using hard-coded light direction for fake lighting
        half3 th = normalize(half3(0.25, 1, -0.25));
        // Using hard-coded light direction for fake specular
        // This matches the value inside the LightmappedPrefabWithSpec shader
        half3 sth = normalize(half3(0, 1, -0.25));

        // Fake lighting
        float lightVal = max(0, dot (i.normalDir, th));
        float lightScale = 0.75;
        lightVal = lightVal * lightScale;

        // Fake spec
        float spec = max(0, dot(i.normalDir, sth));
        float specScale = 0.65;
        float specAtt = 0.65;
        spec = specScale * pow (spec, 40.0 * specAtt);

        // Add in a general brightness (similar to ambient/gamma) and then
        // calculate the final color of the pixel
        col.rgb = min(half4(1,1,1,1), col.rgb * _Brightness +
                      col.rgb * lightVal * _Color + col.rgb * spec);
        return col;
      }

      ENDCG
    }
  }
  FallBack "Diffuse"
}

오브젝트에는 그림자가 없거나 동적인 조각의 일부분으로 가짜 그림자를 생성하였습니다. 표면은 대부분 납작하기 때문에 큰 문제가 되지 않았습니다.

모바일용 Unity 광원 베이크된 프리팹 Galactic Colonies MetalPop Games

(추가 동적 오브젝트를 사용하는 건물)

동적인 조각에는 그림자가 없지만 자세히 보지 않는 이상 거의 표시가 나지 않습니다. 동적 프리팹의 조명 역시 완전히 가짜로 실시간 조명이 포함되지 않았습니다.

저희가 사용한 첫 번째 지름길은 광원(태양)의 위치를 가짜 라이팅 셰이더에 하드코딩하는 것이었습니다. 이를 통해 셰이더가 세계에서 검색하고 동적으로 채워야 할 변수가 하나 줄어듭니다.

동적인 값보다 고정된 값으로 작업하는 것이 항상 더욱 빠릅니다. 이를 통해 기본적인 조명과 메시의 밝은 면 및 어두운 면을 확보할 수 있었습니다.

스페큘러/광택

물체에 반짝임을 추가하기 위해 동적 및 정적 오브젝트에 가짜 스페큘러/광택 계산을 추가했습니다. 정반사(Specular Reflection)는 금속의 느낌을 표현하기도 하지만 또한 표면의 곡률을 전달하는 데 도움이 됩니다.

스페큘러 하이라이트는 반사의 한 가지 형태이기 때문에 올바르게 계산하기 위해서는 카메라 각도와 그에 맞는 광원의 정보가 필요합니다. 카메라가 이동하거나 회전하면 스페큘러가 변화합니다. 이를 계산하는 셰이더는 씬 안의 카메라 위치와 모든 광원에 대한 액세스를 필요로 합니다.

하지만 본 게임의 경우 스페큘러에 사용하고자 하는 광원은 단 하나입니다. 바로 태양입니다. 이 게임에서 태양은 움직이지 않으며 일종의 방향광으로 볼 수 있습니다. 단 하나의 조명을 사용하고 이에 대한 고정된 위치값과 방향의 각도를 추정하면 셰이더를 훨씬 간소화할 수 있습니다.

더 나은 것은, Galactic Colonies의 카메라는 대부분의 도시 건설 게임처럼 씬을 위에서 내려다봅니다. 카메라를 살짝 기울이거나 확대 및 축소할 수 있지만 Y축을 기준으로 회전할 수는 없습니다.

전반적으로 늘 환경을 위에서 내려다봅니다. 값싼 스페큘러 모습을 모방하기 위해서 카메라가 완전히 고정되었고 카메라와 빛 사이의 각도 역시 고정되었다고 가정하였습니다.

이 방법으로 또 다시 셰이더에 고정된 값을 하드코딩하여 값싼 스페큘러/광택 효과를 구현할 수 있었습니다.

모바일용 Unity 광원 베이크된 프리팹 Galactic Colonies MetalPop Games

(가짜 스페큘러/광택 효과)

물론, 스페큘러에 고정된 각도를 사용하는 것은 사실 정확한 것은 아니지만 카메라 각도가 크게 바뀌지 않는 이상 차이점을 알아차리기는 거의 불가능합니다. 플레이어에게 씬은 여전히 알맞게 보일 것이며, 이것이 실시간 라이팅의 목적입니다.

실시간 비디오 게임에서 환경의 조명을 설정할 때 실제로 물리적으로 올바르게 시뮬레이션하는 것보다 시각적으로 알맞게 보이는 것이 중요합니다.

대부분의 메시가 하나의 머티리얼을 공유하고 대부분의 디테일은 라이트맵 및 버텍스가 제공하기 때문에 스페큘러 텍스처 맵을 추가하여 셰이더에게 언제 어디에 스페큘러 값을 얼마나 적용할지 지시하도록 했습니다. 텍스처는 기본 UV 채널을 통해 액세스되기 때문에 추가적인 좌표 세트를 필요로 하지 않습니다. 뿐만 아니라 많은 디테일이 있지 않기 때문에 해상도가 매우 낮으며 공간을 거의 차지하지 않습니다.

버텍스 수가 적은 일부 소규모 동적 조각에 대해서는 Unity의 자동화된 동적 배칭을 활용하여 렌더링을 가속화할 수 있었습니다.

광원 베이킹된 프리팹 중첩

모든 베이킹된 섀도우는 새로운 문제를 일으킬 수 있습니다. 특히 비교적 모듈식인 건물을 다룰 때 문제가 생길 수 있습니다. 일례로 플레이어가 지을 수 있는 창고가 있었는데, 여기에 저장된 물건의 유형이 실제 건물에 표시되는 경우가 있었습니다.

이는 광원 베이킹된 오브젝트 위에 또 하나의 광원 베이킹된 오브젝트를 배치했기 때문에 문제가 되었습니다. 광원 베이킹이 중첩된 것입니다.

이 문제는 또 다른 트릭을 통해 해결했습니다.

  • 오브젝트가 추가되는 표면은 플랫해야 하며 기본 건물과 일치하는 특정 회색 컬러를 사용해야 함
  • 이러한 단점을 보완하기 위해 더 작은 플랫한 표면에서 오브젝트를 베이크하고 약간의 오프셋을 적용하여 이를 해당 영역 위에 배치할 수 있었음
  • 광원, 하이라이트, 컬러가 적용된 글로우, 그림자 모두 해당 타일에 베이크됨

모바일용 Unity 광원 베이크된 프리팹 Galactic Colonies MetalPop Games

(베이킹된 오브젝트는 파란 빛이 나며 그림자를 드리움)

일부 제한 사항이 있는 값싸고 효율적인 광원 베이킹

이 방식으로 프리팹을 구축 및 베이킹하면 매우 낮은 드로우 콜 수를 유지하면서 수백 개의 건물이 있는 거대한 맵을 사용할 수 있습니다. 전체 게임 세계가 하나의 머티리얼로 렌더링되며 현 시점에서는 UI가 게임 세계보다 더 많은 드로우 콜을 사용합니다. Unity가 렌더링하는 머티리얼의 종류가 적을수록 게임 성능에 좋습니다.

이는 세계에 파티클, 날씨 효과 및 다른 보기 좋은 요소를 추가할 수 있는 공간을 확보해 줍니다.

이 방법으로 오래된 기기를 사용하는 플레이어도 안정적으로 60fps를 유지하면서 수백 개의 건물로 이루어진 도시를 구축할 수 있습니다.

모바일용 Unity 광원 베이크된 프리팹 Galactic Colonies MetalPop Games

(광원 베이킹된 프리팹을 활용한 플레이어에 의해 구축된 도시)

게임에 알맞는 기능 활용

이 게임은 분주한 도시가 있는 큰 행성을 렌더링하기 위해서 맞춤형 솔루션을 사용합니다. 한정된 카메라 각도와 씬 내에 단일 광원을 활용하여 셰이더의 복잡도를 크게 감소시켰습니다. 또한 동적 오브젝트를 라이팅하는 것과 고정된 각도의 스페큘러를 계산하는 면에서 비용을 줄일 수 있었습니다. 동적 오브젝트의 개수를 줄이고 나머지 오브젝트는 정적으로 배칭했습니다.

그리고 이 게임의 로우폴리 및 색상만으로 이루어진 비주얼 스타일로 인해 대부분의 오브젝트 사이에 머티리얼을 공유할 수 있어 정적 및 동적 배칭이 가능하기 때문에 작업이 훨씬 수월합니다.

아마도 여기에서 설명된 솔루션을 여러분의 게임에 그대로 적용할 수 없을 겁니다. 이는 당연한 일입니다. 이는 자신만의 게임을 최적화하는 최선의 방법을 찾기 위한 시작점일 뿐입니다. 게임에서 수용할 수 있는 제한 사항을 파악하고 이를 활용하여 게임의 속도를 높이세요.

리소스 더 보기
확인

유니티에서는 웹 사이트의 모든 기능을 최대로 이용할 수 있도록 쿠키를 사용합니다. 자세한 정보는 쿠키 정책 페이지를 참조하세요.