2016년 10월 20일

인텔 내장 그래픽 칩셋에서도 60 FPS를 내는 언리얼 엔진 게임

저자: Jay Mattis

여러분 안녕하십니까! 저는 로스 앤젤레스에 위치한 2인 개발사, 하이 호스 엔터테인먼트(High Horse Entertainment) 소속의 Jay라고 합니다. 저희는 최신 그래픽과 컨트롤 방식, 그리고 활발한 온라인 경쟁 플레이 모두를 강조한 아케이드 게임을 만들어보고자 하이 호스를 설립하였습니다. 그렇게 처음으로 만든 프로젝트인 디스크 잼(Disc Jam)은 승리하기 위해 타이밍 감각과 반사신경을 총동원해야 하는 아케이드 액션 스포츠 게임입니다. 이 게임을 직접 확인해보고 싶으시다면, www.discjamgame.com 홈페이지에서 현재 프리-알파 테스트 버전의 스팀 키를 무료로 받아보실 수 있습니다!

디스크 잼의 게임플레이 스타일은 매우 유동적이고 반응성이 높기 때문에, 이런 플레이를 만들어내기 위해서는 반드시 초당 60프레임을 유지해야 했습니다. 따라서 이번 프로젝트의 개발에서도 퍼포먼스에 가장 중점을 두어야 했으며, 결과적으로 이번에 언리얼 엔진 4를 활용하면서 목표 퍼포먼스를 충족하는 것에 대해 많은 것을 배울 수 있었습니다. 아래 내용에서는 인텔의 내장 그래픽 칩셋으로 작업을 한 경험과, 최소 사양을 올리지 않고서도 목표 퍼포먼스를 충족해낸 것에 대해 논해보았습니다.

목표 사양을 내장형 그래픽 칩셋으로 잡은 이유

조립형 PC는 콘솔과 달리 기준이 되는 하드웨어 사양이 없기 때문에, 맞추기가 굉장히 까다롭습니다. 많은 사람들이 PC를 활용해 게임을 즐기며 개중에는 그래픽카드를 따로 구입해 사용하는 사람들도 있지만, PC 소유자 중 상당한 비율의 사용자들이 내장형 그래픽 칩셋에 의존해 게임을 즐깁니다. 이런 시장의 전체 규모가 얼마나 되는지 정확히 파악하기는 어려웠지만, 유니티(Unity) 측의 기술을 통해 하드웨어 사용 현황을 조사한 결과 현재 40%가량의 PC가 인텔 사의 그래픽 칩셋을 사용하고 있는 것으로 나타났습니다. 이는 다른 경쟁사들과 비교해보아도 가장 높은 수치를 보여주는 것입니다. 수많은 PC 게임들이 높은 수준의 최소사양을 요구하고 있지만, 디스크 잼만큼은 최소사양을 최대한 낮게 잡아야 하는 중요한 이유가 2가지 있었습니다.

멀티플레이

디스크 잼같은 멀티플레이어 게임은 같이 멀티플레이를 하는 사람이 얼마나 되느냐에 게임의 생명이 걸려있습니다. 만약 아무도 멀티플레이를 하지 않으면 아무도 멀티에서 매치를 찾을 수 없을 거고, 그럼 기본 플레이어들의 숫자도 계속 줄어들다 결국 완전히 사라져버리고 말 겁니다. 그렇기 때문에 일정 플레이어 숫자를 유지하기 위해서는 최대한 넓은 폭의 사양을 지원하는 것이 중요했습니다.

퍼포먼스

디스크 잼은 초당 60프레임(FPS)의 환경에서 플레이하도록 디자인된 게임입니다. 만약 이런 프레임을 충족하지 못하는 환경에서 게임을 플레이할 경우, 개발 과정에서 의도했던 게임 경험을 완전히 누리지 못하게 될 것입니다. 이는 디스크 잼이 주로 온라인 플레이에 집중해 만들었단 점을 생각해보면, 같은 팀원은 물론 적 팀원의 게임 플레이에도 치명적인 영향을 끼치게 될 것입니다.

언리얼 엔진 4의 확장성과 퍼포먼스

개발 접근 방향을 결정하는 단계에서, 먼저 언리얼 엔진 4의 퍼포먼스는 목표로 삼고 있는 하드웨어 수준에 '이미 딱 맞는다는 점'을 눈여겨보았습니다. 이 테스트에는 바이너리 릴리즈 버전의 언리얼 엔진 4.12.5에서 FPS 게임을 예시로 사용했습니다. 모든 테스트는 인텔(Intel)® Core™ i7-4720 HQ 프로세서와 인텔® HD Graphics 4600 GPU 사양의 노트북에서 이루어졌습니다.


사진 1. 에픽 퀄리티 세팅(Epic Quality Settings): 초당 최대 20프레임


사진 2. 낮음 퀄리티 세팅(Low Quality Settings): 초당 최대 40프레임

만약 목표 퍼포먼스를 초당 30 프레임으로 잡고 게임을 만들었다면 이 테스트는 좋은 소식이었을 것입니다. 하지만 아쉽게도, 디스크 잼은 가장 낮은 사양에서도 초당 60프레임을 유지하는 것을 목표로 삼고 있었습니다. 테스트 예제로 삼았던 FPS 게임보다 더욱 최적화시키기도 어려웠을 뿐더러, 아무래도 언리얼 엔진 4의 데스크탑 PC용 렌더러로 좋은 퍼포먼스를 보여주기 위해서는 디스크 잼이 목표로 삼은 하드웨어보다 더 높은 사양이 필요할 것 같았습니다. 다행히 창의력과 수고를 좀 들일 각오만 되어 있다면, 언리얼 엔진 4는 대체할 수 있는 방법이 많이 있습니다.

언리얼 엔진 4의 모바일 프리뷰 렌더러

언리얼 엔진은 최신 데스크탑 사양의 PC와 콘솔용 게임 뿐만 아니라, 최신 모바일 게임도 만듭니다! 또한 시장에 다양하게 출시되어 있는 모바일 기기들을 모두 호환하여 지원하기 위해, 게임의 렌더링 기능도 다양한 방식을 가지고 있습니다. 가장 최신 기능의 경우 OpenGL ES 3.1과 Android Extension Pack (AEP)용으로 디자인되어 있어, 이 기능에 관심을 가지고 접근하기로 했습니다. 테스트 결과 이 렌더링 기능은 인텔 내장형 그래픽 칩셋에서 최고의 가성비를 보여주었습니다.

여기서 중요한 점은 언리얼 엔진 4에 모바일 프리뷰(Mobile Preview)라는 기능이 있단 것입니다. 이 기능은 게임을 굳이 모바일 기기로 옮겨 구동할 필요 없이, 엔진에서 미리보기를 제공하여 이터레이션 작업에 소요되는 시간을 줄이도록 만든 기능입니다. 이를 통해 데스크탑 환경에서 언리얼 특유의 만능 고사양 렌더러 대신 모바일 렌더링 방식으로 게임을 렌더링 할 수 있어, 굉장히 효율적인 작업을 할 수 있었습니다. 이 기능을 사용해 얻은 결과는 다음과 같습니다:


사진 3. OpenGL ES 3.1 + AEP 모바일 미리보기: 초당 최대 100프레임

모바일 프리뷰 기능으로 테스트해본 결과,  최저 사양의 설정에서 데스크탑 렌더러보다 최대 2.5 배의 퍼포먼스를 낼 수 있었습니다. 이제 내장 그래픽 칩셋 사양으로 720p 화질에 초당 60프레임이라는 목표를 달성할 수 있게 된 것입니다! 물론 데스크탑 렌더러의 스크린샷과 모바일 렌더러의 스크린샷 사이에 그래픽의 차이점이 있다는 것이 보이실 겁니다. 이는 라이팅과 섀도 등 모바일 렌더러에 사양상의 한계가 있기 때문입니다. 에픽게임즈의 모바일 플랫폼용 라이팅모바일 디바이스용 퍼포먼스 지침서 공식 문서에서 더 많은 정보를 얻으실 수 있습니다.

다중 라이팅 릭

위와 같은 문제를 해결하고 게임의 무대가 되는 코트에 안정적인 라이트를 제공하기 위해, 디스크 잼에는 다중 라이팅 릭을 사용했습니다. 기존의 렌더러로 렌더링을 하면서 릭 하나를 쓰고, 모바일 프리뷰로 렌더링을 하면서 다른 릭을 또 하나 쓰는 방식이죠. 모든 게임은 제각기 필요한 라이팅이 다르지만, 디스크 잼은 두 버전에 사실상 같은 라이트들을 사용합니다. 유일한 차이점은 드리우는 그림자의 모빌리티 뿐입니다. 하이 엔드 버전에서는 스테이셔너리 라이트를 주 라이트로 사용했기 때문입니다. 또한 모바일 프리뷰에서 스태틱 스포트라이트를 사용해 모든 라이팅을 미리 구워두었고, 이를 통해 최대한의 퍼포먼스를 이끌어낼 수 있었습니다..

UnrealEngine%2Fblog%2Fgoogle-daydream-sdk-1-0-released-supported-in-4-13-1-copy%2Fdisk-jam-high-end-lighting-610x343-78b0ec2082bf1c8f6364beb8f94a0b26542e9dc5
사진 4. 디스크 잼의 하이엔드 렌더러와 라이팅


사진 5. 디스크 잼의 로우엔드 렌더러와 라이팅

언리얼 엔진 4에서 다중 라이트 릭을 사용하려고 할 때 제일 먼저 맞닥뜨리는 것은, 바로 베이크드 라이팅이 라이트가 아니라 맵의 지오메트리 상에 주로 저장되어 있다는 사실입니다. 안타깝지만 이는 곧 다른 맵에 지오메트리를 모두 복제하여, 별도의 라이트를 다시 구워야 한다는 뜻입니다.

디스크 잼에서는 퍼시스턴트 레벨을 설정하여 여기에 라이팅에 영향을 받지 않는 액터를 모두 배치했습니다. 이 액터들은 맵의 하이엔드 버전부터 로우엔드 버전은 물론, 종류도 스폰 지점부터 콜리전 볼륨에 이르기까지 다양했습니다. 그 다음 하이엔드 맵과 로우엔드 맵에 같은 지오메트리를 배치하고, 라이팅에만 차별성을 두었습니다. 레벨이 로딩되자, 적절한 버전을 스트림했습니다:


사진 6. Disc Jam’s Persistent Level Blueprint

위에서 사용된 “Is in Mobile Preview” 노드는 다음과 같이 정의한 커스텀 함수입니다:

bool UDiscJamBlueprintFunctionLibrary::IsInMobilePreview()
{
	return GMaxRHIFeatureLevel <= ERHIFeatureLevel::ES3_1;
}

패키징과 개발

주의: 이 문단에서는 윈도우 운영체제에서의 개발과 패키징을 다루고 있습니다. 하지만 개발 단계 자체는 다른 운영체제와 비교해보아도 비슷할 것입니다.

게임의 패키징을 끝내고 “-FeatureLevelES31” 인수를 붙여 실행을 하자마자, 필수 셰이더가 패키지에 포함되지 않았단 사실이 확실하게 드러났습니다. Project Settings(프로젝트 세팅) → Platforms(플랫폼) → Windows(윈도우) → Targeted RHIs(타겟 RHI), 아래를 보면, 어떤 셰이더 변수를 패키지할 것인지 선택하는 체크박스들을 볼 수 있습니다. 아쉽게도 OpenGL ES 3.1 셰이더는 그 선택지중에 없었지만, 코드 2줄만 간단하게 바꾸는 것만으로 이 셰이더를 추가할 수 있었습니다.

GenericWindowsTargetPlatform.h에서는 GetAllPossibleShaderFormats 함수가 반드시 OpenGL ES 3.1 셰이더를 포함하도록 수정해야 합니다:

virtual void GetAllPossibleShaderFormats( TArray<FName>& OutFormats ) const override
{
	// no shaders needed for dedicated server target
	if (!IS_DEDICATED_SERVER)
	{
		static FName NAME_PCD3D_SM5(TEXT("PCD3D_SM5"));
		static FName NAME_PCD3D_SM4( TEXT( "PCD3D_SM4" ) );
		static FName NAME_PCD3D_ES3_1( TEXT( "PCD3D_ES31" ) );
		static FName NAME_GLSL_150(TEXT("GLSL_150"));
		static FName NAME_GLSL_430(TEXT("GLSL_430"));

		OutFormats.AddUnique(NAME_PCD3D_SM5);
		OutFormats.AddUnique(NAME_PCD3D_SM4);
		OutFormats.AddUnique(NAME_PCD3D_ES3_1);
		OutFormats.AddUnique(NAME_GLSL_150);
		OutFormats.AddUnique(NAME_GLSL_430);
	}
}

그리고 WindowsTargetSettingsDetails.cpp에는 GetFriendlyNameFromRHIName 함수를 수정해, UI에 같은 팀원의 이름이 출력되도록 했습니다. 수정 내용은 다음과 같습니다:

FText GetFriendlyNameFromRHIName(const FString& InRHIName)
{
	FText FriendlyRHIName = LOCTEXT("UnknownRHI", "UnknownRHI");
	if (InRHIName == TEXT("PCD3D_SM5"))
	{
		FriendlyRHIName = LOCTEXT("DirectX11", "DirectX 11 (SM5)");
	}
	else if (InRHIName == TEXT("PCD3D_SM4"))
	{
		FriendlyRHIName = LOCTEXT("DirectX10", "DirectX 10 (SM4)");
	}
	else if (InRHIName == TEXT("PCD3D_ES31"))
	{
		FriendlyRHIName = LOCTEXT("DirectXES31", "DirectX Mobile Emulation (ES3.1)");
	}
	else if (InRHIName == TEXT("GLSL_150"))
	{
		FriendlyRHIName = LOCTEXT("OpenGL3", "OpenGL 3 (SM4)");
	}
	else if (InRHIName == TEXT("GLSL_430"))
	{
		FriendlyRHIName = LOCTEXT("OpenGL4", "OpenGL 4 (SM5, Experimental)");
	}
	else if (InRHIName == TEXT("SF_VKES31"))
	{
		FriendlyRHIName = LOCTEXT("Vulkan ES31", "Vulkan Mobile (ES3.1, Experimental)");
	}
	else if (InRHIName == TEXT("SF_VULKAN_SM4"))
	{
		FriendlyRHIName = LOCTEXT("VulkanSM4", "Vulkan (SM4)");
	}
	else if (InRHIName == TEXT("SF_VULKAN_SM5"))
	{
		FriendlyRHIName = LOCTEXT("VulkanSM5", "Vulkan (SM5)");
	}

	return FriendlyRHIName;
}

이렇게 수정한 다음 엔진에서 다시 컴파일을 거치자, 이제 남은 작업은 윈도우 플랫폼 환경 아래의 체크박스에 체크를 하는 것 뿐이었습니다:


사진 7. 새로 추가한 ‘DirectX Mobile Emulation (ES3.1)’이 목록에 나타남

보너스: 인텔 그래픽 칩셋에서 모바일 프리뷰를 자동으로 활성화 하는 법

디스크 잼은 게임을 시작할 때, 스팀(Steam) 실행 옵션을 통해 플레이어에게 어떤 렌더러를 사용할 것인지 선택지를 제공합니다. 저사양 렌더러를 선택하고 싶다면 그냥 간단하게 “-FeatureLevelES31"  인수를 붙여 게임을 실행하면 됩니다.


사진 8. 디스크 잼의  스팀 실행 옵션

하지만 인텔의 그래픽 칩셋의 경우, 게임 기본 설정을 모바일 프리뷰 렌더러로 설정해두었습니다. 이 역시 간단한 코드 수정을 통해 처리한 것입니다. WindowsD3D11Device.cpp에서 함수 FD3D11DynamicRHI::InitD3DDevice() 로 비디오 어댑터를 초기화 하도록 합니다. 해당 함수는 이하 대략 100줄에 걸쳐 플레이어가 인텔의 그래픽 칩셋을 사용하는지 확인하여, 실행환경에 적합한 그래픽 메모리를 설정합니다. 이 블록에서는 렌더러를 다음과 같이 설정했습니다:

if ( IsRHIDeviceIntel() )
{
	// It's all system memory.
	FD3D11GlobalStats::GTotalGraphicsMemory = FD3D11GlobalStats::GDedicatedVideoMemory;
	FD3D11GlobalStats::GTotalGraphicsMemory += FD3D11GlobalStats::GDedicatedSystemMemory;
	FD3D11GlobalStats::GTotalGraphicsMemory += ConsideredSharedSystemMemory;

	GMaxRHIFeatureLevel = ERHIFeatureLevel::ES3_1;
	GMaxRHIShaderPlatform = SP_PCD3D_ES3_1;
}

저의 이야기는 여기서 끝마치도록 하겠습니다!

만약 이 글이 도움이 되었다면 @HighHorseGames 트위터 계정에 한 줄 평을 남겨주세요. 디스크 잼 및 그 개발 관련 소식을 계속해서 받아보시려면 http://www.discjamgame.com 블로그를 확인해 보시기 바랍니다.

이 곳 Intel Developer Zone 블로그에서 원문도 읽어보실 수 있습니다.