Unity 검색

DLSS를 활용한 나라카: 블레이드포인트의 월드 제작

2021년 10월 25일 게임 | 12 분 소요
Standing before a battle
Standing before a battle
공유

Is this article helpful for you?

Thank you for your feedback!

24 Entertainment가 인기 대작 나라카: 블레이드포인트를 NVIDIA의 DLSS(딥 러닝 슈퍼 샘플링) 기술을 사용하여 성능 저하 없이 4K 그래픽스로 구현한 방법을 알아보세요.

2021년 8월, 전 세계에 동시 출시된 배틀 로얄 타이틀 나라카: 블레이드포인트가 큰 반향을 일으켰습니다. 고대 중국의 판타지에서 영감을 받은 근사한 인게임 아트워크가 특히 전 세계 플레이어의 마음을 사로잡았습니다.

NetEase의 독립 자회사인 24 Entertainment는 포트폴리오를 지속적으로 확장하고 있으며, 첫 번째 데스크톱 게임으로 선보인 나라카: 블레이드포인트로 이름을 알린 뒤 빠르게 세계적인 주목을 받았습니다. 출시 일주일만에 Steam의 Top 10 차트에 입성했으며, 레인보우 식스 시즈, 스플릿게이트 등의 인기 타이틀보다도 많은 사용자를 유입했습니다.

Naraka Bladepoint

이 액션 어드벤처 배틀 로얄 게임은 매우 생생하게 렌더링된 화려한 산봉우리와 울창한 숲, 드넓은 고대 도시를 배경으로 펼쳐집니다. 24 Entertainment는 아름다운 환경을 구현하면서 동시에 60인 멀티플레이어를 지원하는 데 필요한 성능과 프레임 속도를 유지하기 위해 NVIDIA 및 유니티와 긴밀히 협력했습니다. 

24 Entertainment는 NVIDIA와의 파트너십을 통해 딥 러닝 슈퍼 샘플링(DLSS)을 조기에 이용할 수 있었습니다. DLSS는 높은 프레임 속도와 해상도로 실시간 월드를 실행하도록 설계된 최신 렌더링 기술입니다. DLSS는 인공 지능을 활용하여 그래픽스 성능과 전반적인 아트 품질을 보강합니다.

나라카: 블레이드포인트는 강력한 성능을 유지하기 위해 저해상도로 렌더링하며, 픽셀 셰이딩 계산과 같은 작업의 필요성을 제거합니다. 내부적으로 DLSS는 신경망을 이용하여 고해상도 이미지를 생성하고 게이머가 경험하는 아트의 디테일을 보존합니다. 이렇게 하면 고품질의 결과물을 생성할 수 있으며, 인공 지능의 도움으로 누락된 정보를 채울 수 있으므로 게임이 두 배 가까이 빠르게 렌더링됩니다. 멀티플레이어 격투 게임에서 이러한 역량은 매우 중요한 역할을 합니다.

이 콘텐츠는 Targeting Cookies 카테고리를 수락해야만 동영상을 시청할 수 있도록 허용하는 타사 제공업체에서 호스팅합니다. 이러한 제공업체의 비디오를 보려면 쿠키 환경 설정에서 Targeting Cookies 카테고리를 수락하시기 바랍니다.

24 Entertainment는 DLSS를 통해 프레임 속도 저하 없이 고해상도 결과물과 선명한 디테일을 확보했습니다. 결과물을 보면 네이티브 4K와 DLSS 4K를 구분하는 것이 거의 불가능해졌습니다.

24 Entertainment는 DLSS를 통해 프레임 속도 저하 없이 고해상도 결과물과 선명한 디테일을 확보했으며, 네이티브 4K와 DLSS 4K를 구분하는 것이 거의 불가능해졌습니다.

AI는 이전 프레임을 처리하도록 훈련되며, 이는 안티앨리어싱과 같은 요소에 유용합니다. 그리고 일관된 모델을 사용하여 특정 게임을 위해 신경망을 재훈련할 필요가 없도록 합니다.

나라카: 블레이드포인트에 통합된 DLSS

나라카: 블레이드포인트 개발은 C#으로 스크립팅된 다양한 렌더링 아키텍처를 사용할 수 있는 Unity의 스크립터블 렌더 파이프라인(SRP)을 기반으로 커스텀 렌더 파이프라인을 구현하면서 몇 년 전에 시작되었습니다.

24 Entertainment는 NVIDIA의 Developer Relations 전문 지원 팀 및 유니티의 Core Support 팀과 협력하여 Unity에서 DLSS를 처음 사용했습니다.

나라카: 블레이드 포인트의 그래픽스 팀은 실시간 플랫폼 환경에서 DLSS가 어떻게 작동하는지 심도 있게 파악할 수 있도록 구현 세부 정보와 팁, 당면 과제를 공개했습니다.

업샘플링

DLSS를 구현하려면 제일 먼저 저해상도의 입력 이미지를 업샘플링해야 합니다.

Implementing DLSS integrations
24 Entertainment에서 해결해야 했던 과제 예시

24 Entertainment는 최종 프레임 품질에 미치는 영향을 줄이기 위해 블룸, 톤 매핑, 특수 효과 조명과 같은 포스트 프로세싱 효과를 적용하기에 앞서 이 프로세스를 진행했습니다. 모든 포스트 프로세싱 효과는 샘플링을 마친 고해상도 이미지에 적용되었습니다.

파이프라인은 다음과 같이 실행되었습니다.

1단계: 품질 모드를 선택한 후, NVIDIA 함수 getOptimalsettings를 사용하여 입력 크기와 권장 선명도를 구했습니다. 품질 모드에 따라 스케일링 범위가 달라집니다.

2단계: NVIDIA CreateFeature 인터페이스를 통해 각 카메라에 대한 DLSS 기능을 준비하고, 이를 사용하여 선택된 품질 모드, 타겟 렌더링 출력 크기, 추가 샤프닝(sharpening) 파라미터를 설정했습니다. 추가 샤프닝 작업을 통해 DLSS는 보다 세밀한 출력을 만듭니다.

3단계: 포스트 프로세싱 전에 DLSS 추론을 위한 실행 함수를 호출했으며, 이때 다음 입력이 필요합니다. 

commandBuffer.ApplyDLSS(m_DLSSArguments);

입력 준비

DLSS와 호환되는 입력을 확보하기 위해 파이프라인이 약간 수정되었습니다.

저해상도 입력은 대규모 렌더링 오브젝트를 요구할 수 밖에 없습니다. 따라서 뷰포트를 gbuffer로 설정해야만 올바른 포워드 패스가 가능합니다. 렌더 타겟은 적절하게 스케일링된 크기로 만들어야 합니다.

pixelRect = new Rect(0.0f, 0.0f,      Mathf.CeilToInt(renderingData.cameraData.pixelWidth * viewportScale),      Mathf.CeilToInt(renderingData.cameraData.pixelHeight * viewportScale));  commandBuffer.SetViewport(pixelRect); // RenderScale Supported

렌더링이 완료되면, DLSS 인수를 소스의 크기, 대상의 크기, 입력 컬러 렌더링 타겟, 뎁스 타겟을 포함한 저해상도 이미지 관련 파라미터로 채워야 합니다.

 int scaledWidth = UpSamplingTools.Instance.GetRTScaleInt(cameraData.pixelWidth);  int scaledHeight = UpSamplingTools.Instance.GetRTScaleInt(cameraData.pixelHeight);

 // Set the argument  m_DLSSArguments.SrcRect.width = scaledWidth;  m_DLSSArguments.SrcRect.height = scaledHeight;

 m_DLSSArguments.DestRect.width = cameraData.pixelWidth;  m_DLSSArguments.DestRect.height = cameraData.pixelHeight;

 m_DLSSArguments.InputColor = sourceHandle.rt;  m_DLSSArguments.InputDepth = depthHandle.rt;

Jitter Offset

다음으로 Jitter Offset 입력을 살펴보겠습니다. Jitter Offset은 프레임 전반에 걸쳐 더 많은 샘플을 축적하는 작업과 관련이 있습니다.

삼각형이 덮은 영역만을 기준으로 픽셀이 셰이딩되는 경우, 프리미티브의 래스터화가 매우 이산적으로 이루어집니다. 모양이 들쭉날쭉하고 부자연스러우며 가장자리도 매끄럽지 않습니다.

하지만 해상도를 높이면 보다 세밀하고 자연스러운 결과를 얻을 수 있습니다. 그러나 이산 샘플이 충분하지 않으면 연속 신호를 재구성하기가 매우 어렵다는 사실도 알아 두어야 합니다. 이는 앨리어싱을 유발하기도 합니다.

Jitter offset inputs

4K 결과물을 얻으려면 간단히 해상도를 높여 앨리어싱을 완화할 수 있습니다. 만약 원하는 해상도가 8K라면, 렌더링이 4배 느려지고, 8K 텍스처 대역폭(메모리 사용량) 문제가 발생할 수 있습니다.

앨리어싱을 해결하는 또 다른 방법은 GPU 하드웨어에서 지원하는 MSAA(멀티샘플 안티앨리어싱)입니다. MSAA는 픽셀의 중앙 샘플만 확인하는 것이 아니라, 다양한 하위 픽셀 위치에서 여러 샘플을 확인합니다. 삼각형 프래그먼트 컬러를 조정하여 픽셀에서 프리미티브로 덮인 샘플 수를 기반으로 가장자리를 부드럽게 만들 수 있습니다.

여러 프레임에 걸쳐 샘플을 축적하는 또 다른 방법은 TAA(Temporal Anti-Aliasing)입니다. 이 방법을 사용하면 샘플링 위치를 변경하기 위해 각 프레임에 다른 지터(jitter)가 추가됩니다. 그리고 모션 벡터를 사용하여 프레임 간 컬러 결과를 블렌드합니다.

현재 프레임에서 각 픽셀의 이전 컬러를 식별할 수 있다면, 해당 정보를 이후에도 사용할 수 있습니다.

일반적으로 지터는 픽셀에서 샘플링 위치가 약간 조정되는 것을 의미하며, 언더샘플링 문제를 한 번에 해결하기 보다는 여러 프레임에 걸쳐 샘플이 축적되도록 합니다.

24 Entertainment에서는 이러한 이유로 DLSS를 선택했습니다. DLSS를 사용함으로써 전체적인 렌더링 해상도를 낮춰 문제를 해결했을 뿐만 아니라, 가장자리가 충분히 부드럽게 표현된 고품질 결과물까지 확보할 수 있었습니다.

여기서 권장 샘플 패턴을 구성하는 Halton 수열은 무작위로 보이지만 공간을 보다 균일하게 덮는 저불일치 수열입니다.

실제 Jitter Offset 적용은 다소 직관적으로 이루어집니다. 다음 단계에 따라 효과적으로 수행해 보세요.

1단계: 다양한 설정에 따라 특정 카메라의 Halton 수열에서 샘플을 생성합니다. 출력 지터는 -0.5 ~ 0.5 범위 내에 있어야 합니다.

 Vector2 temporalJitter = m_HalotonSampler.Get(m_TemporalJitterIndex, samplesCount);

2단계: 지터를 Vector4에 저장합니다. 그리고 지터에 2를 곱한 뒤 스케일 해상도로 나누고, 해당 지터를 픽셀 단위의 스크린 공간으로 변환합니다. 이를 zw 컴포넌트에 저장합니다.

두 값은 나중에 투사 행렬을 수정하고 렌더링 결과에 전역적 영향을 미치는 데 사용합니다.

  m_TemporalJitter = new Vector4(temporalJitter.x, temporalJitter.y,            temporalJitter.x * 2.0f / UpSamplingTools.GetRTScalePixels(cameraData.pixelWidth),            temporalJitter.y * 2.0f / UpSamplingTools.GetRTScalePixels(cameraData.pixelHeight) );

3단계: View Projection 행렬을 전역 프로퍼티 UNITY_MATRIX_VP로 설정합니다. 이렇게 하면 버텍스 셰이더가 스크린에서 World 위치를 변환하기 위해 동일한 함수를 호출하므로 셰이더가 수정 없이 원활하게 작동합니다.

 var projectionMatrix = cameraData.camera.nonJitteredProjectionMatrix; projectionMatrix.m02 += m_TemporalJitter.z; projectionMatrix.m12 += m_TemporalJitter.w;

projectionMatrix = GL.GetGPUProjectionMatrix(projectionMatrix, true); var jitteredVP = projectionMatrix * cameraData.viewMatrix;

Shader calculation

모션 벡터

Jitter Offset 입력이 성공적으로 해결되면 이제 모션 벡터 툴을 사용하여 모션 벡터를 생성할 수 있습니다.

카메라와 동적 오브젝트의 위치가 바뀌면 스크린 이미지도 바뀔 수 있습니다. 따라서 원하는 출력이 여러 프레임에 걸쳐 샘플을 축적하는 것이라면 이전 스크린 위치를 식별해야 합니다.

다이어그램에 보이는 것처럼, 카메라의 위치가 q에서 p로 바뀌면 World 공간의 같은 지점이 완전히 다른 스크린 좌표로 투사될 수 있습니다. 두 값을 감산한 결과가 모션 벡터이며, 스크린에서 현재 프레임에 대한 이전 프레임의 위치를 나타냅니다.

Motion Vectors
In game motion vectors

다음 단계에 따라 모션 벡터를 계산할 수 있습니다.

1단계: 파이프라인의 뎁스 전용 패스에서 동적 오브젝트의 모션 벡터를 계산합니다. 뎁스 전용 패스는 동적으로 움직이는 오브젝트의 뎁스를 드로우하는 데 사용되어 뎁스 테스팅을 돕습니다.

Dynamic objects & Camera

2단계: 카메라의 이전 프레임 View Projection 행렬을 사용하여, 카메라 움직임에 따라 빈 픽셀을 채우고, World 위치를 변환하여 이전 스크린 위치를 구합니다.

3단계: DLSS 인수에 모션 벡터와 관련된 프로퍼티를 채웁니다.

모션 벡터 스케일링을 사용하면 원하는 결과에 맞는 다양한 정확도에 따라 모션 벡터를 생성할 수 있습니다. 이 예시에서 24 Entertainment는 스크린 공간에 모션 벡터를 생성하므로 이를 음수 너비 및 높이 스케일로 설정했으며, DLSS에서는 픽셀 단위 설정이 필요했습니다.

구현된 파이프라인이 감산 시 현재 프레임 위치와 이전 프레임 위치를 바꾸기 때문에 여기서는 음수 부호가 사용되었습니다.

 m_DLSSArguments.MotionVectorScale = new Vector2(-scaledWidth, -scaledHeight); m_DLSSArguments.InputMotionVectors = motionHandle.rt;

나라카: 블레이드포인트의 렌더링 루프 통합 개요

24 Entertainment의 렌더링 파이프라인은 디퍼드 프레임워크와 포워드 프레임워크가 조합된 형태입니다. 메모리를 절약하기 위해 모든 렌더 타겟이 스케일링 없이 할당됩니다.

DLSS 관리자는 RTHandle 시스템을 통해 스케일링된 렌더 타겟이 카메라가 생성될 때 한 번만 할당되도록 하여 카메라마다 재할당되는 것을 방지합니다.

rendering loop integration overview

시간적인 효과를 얻으려면 모션 벡터를 뎁스 전용 패스에 생성합니다(모션 벡터 계산은 동적 오브젝트에만 필요). 다음으로 전체 화면 패스를 사용하여 카메라 모션 벡터를 생성합니다.

DLSS 관리자는 포스트 프로세싱 초기에 스케일링된 렌더 타겟을 할당하기 위해 RTHandle 시스템을 지원합니다. 

여기서 DLSS 평가 단계는 지터를 제외한 DLSS 인수가 요구하는 모든 정보를 얻을 수 있습니다.

시간적 멀티샘플링을 사용하려면 지터를 추가하세요.

24 Entertainment의 나라카: 블레이드포인트의 파이프라인에는 Halton 패턴의 단계 인덱스, 지터가 있는 행렬과 없는 행렬을 비롯하여 카메라의 정보를 캐시하는 시스템이 있습니다. 개발팀은 모션 벡터 패스를 제외한 모든 래스터화 단계에서 View Projection 및 Jitter 행렬을 활용했습니다. 모션 벡터 패스에서는 지터가 없는 행렬을 사용했습니다.

24 Entertainment에서 공개하는 DLSS 최대 활용 팁

밉맵은 사전 계산된 이미지 시퀀스로 각 이미지는 이전 해상도의 절반으로 표현됩니다. 밉맵을 사용하여 텍스처를 효율적으로 축소한 다음, 기존 텍스처에서 스크린 픽셀에 기여하는 모든 텍셀을 샘플링합니다.

이 렌더링 예시는 카메라에서 멀리 떨어진 오브젝트가 밉맵에서 보다 선명한 결과물을 얻기 위해 저해상도 텍스처를 샘플링하는 방법을 보여 줍니다.

텍스처를 샘플링할 때에는 반드시 Mip Map Bias를 추가하세요. DLSS는 체커보드 렌더링과 같은 기타 유사한 알고리즘과 마찬가지로 업샘플링 방식이므로, 저해상도 뷰포트 렌더링에서 텍스처 해상도를 높여야 합니다. 이렇게 하면 고해상도를 재구성해도 텍스처가 흐리게 표시되지 않습니다.

No Mipmap vs Mipmapping

Mip Map Bias는 다음과 같이 계산할 수 있습니다.

MipLevelBias = log2(RenderResolution.x / DisplayResolution.x)

출력이 음수이면 더 높은 해상도의 이미지로 수준을 다시 편향할 수 있습니다. Mip Map Bias를 설정해야만 저해상도 텍스처를 샘플링할 때 DLSS의 이미지 품질이 동일하게 유지됩니다.

카메라 사용을 위한 기능 캐싱

카메라를 사용하려면 DLSS 기능을 캐시해야 합니다. 

카메라 변경 사항 중 크기 및 품질 모드 변경 사항을 처리하려면 완전히 새로운 기능을 만들어야 합니다. 경우에 따라 크기와 품질 모드가 동일한 여러 카메라가 동시에 렌더링될 수 있으며, 각 기능은 이전 프레임의 정보를 활용하므로 다른 카메라에서 사용될 수 없습니다. 

나라카: 블레이드포인트 개발팀은 각 카메라의 해시 기술자(descriptor)를 딕셔너리에 기능을 캐시하는 키로 사용하여 모든 기능이 카메라 하나에서만 사용되도록 했습니다.

commandBuffer.ApplyDLSS(m_DLSSArguments, cameraDescriptor);

마지막으로 DLSS는 다른 안티앨리어싱 방법과 함께 사용하지 않도록 합니다. 여러 방법을 혼용하면 예기치 못한 결함이 발생할 수 있습니다.

Unity 2021.2의 DLSS

NVIDIA와 긴밀하게 구축한 기술 파트너십 덕분에 유니티는 DLSS를 비롯하여 위에서 설명한 모든 기능을 Unity와 HDRP에 통합했으며, 앞으로 해당 기능을 더 발전시킬 계획입니다.

이렇게 통합된 기능은 Unity 2021.2에서 확인할 수 있으며 Unity 핵심 기능으로 유지됩니다. NVIDIA의 기술은 현재 다음과 같이 Unity에 통합되어 있습니다.

DLSS 코어 통합

내부 코어에 DLSS 모듈에 적합한 C# 스크립팅 API 레이어를 만들었습니다. 이 레이어는 플랫폼 호환성, 플랫폼에서 DLSS 전용 코드를 제외할 수 있도록 하는 엔진 내 #defines 지원과 기술 자료도 제공합니다. DLSS를 완성하는 SRP용 공식 API로, 스크립터블 렌더 파이프라인에 DLSS를 통합하는 시작점 역할을 합니다.

HDRP에서의 그래픽스 통합

유니티는 DLSS를 위에서 설명한 스크립트 API를 사용하는 HDRP의 다이내믹 해상도 시스템(DRS) 기능으로 구현했습니다.

나라카: 블레이드포인트에서 사용된 것과 동일한 TAA 지터링 알고리즘을 기반으로, 포스트 프로세싱 전에 해상도 업샘플링을 적용하고, 최대 해상도로 포스트 프로세싱을 렌더링합니다.

한 가지 개선된 사항은 DLSS가 다이내믹 해상도 시스템을 기반으로 구현되어, 해상도를 실시간으로 빠르게 전환할 수 있다는 점입니다. 이 시스템은 유니티의 RTHandle 시스템과 완전히 통합되었으며, DLSS와 같은 하드웨어 다이내믹 해상도 시스템(텍스처 앨리어싱 기반)과 소프트웨어 다이내믹 해상도 시스템(뷰포트 기반)을 지원합니다. 앞서 이야기된 Mip Map Bias 기능은 유니티에서 개발했지만, 다른 DRS 필터 및 기법과도 사용할 수 있습니다.

파티클의 고스트 현상을 억제하기 위해 다이내믹 해상도 시스템이 모든 HDRP 기능과 호환되도록 특별 패스를 추가했습니다.

또한 DLSS 버저닝과 프레임 내 상태에 관한 모든 정보가 있는 종합 디버그 패널도 추가했습니다.

아울러 VR과 실시간 레이트레이싱에 더하여 DX11, DX12, Vulkan에 대한 지원을 추가했습니다.

Unity 2021.2에서 DLSS 시작하기

프로젝트에서 DLSS를 활성화하는 방법은 다음과 같습니다.

  1. NVIDIA 패키지를 설치하고 Dynamic Resolution과 DLSS를 활성화합니다.
Get started with DLSS in Unity 2021.2

2. 프로젝트에 필요한 카메라에서 DLSS를 활성화합니다.

Enable DLSS in the cameras required for your project

자세한 내용은 DLSS 스크립터블 지원 및 HDRP에서 DLSS 지원에 대한 기술 자료를 참조하세요.

피드백 공유

공개 로드맵을 참조하여 지속적으로 성능과 품질, 그리고 플랫폼 도달률을 개선하고 있는 유니티의 계획을 확인하세요.

질문이나 제안이 있다면 이 포럼에서 공유해 주세요.

2021년 10월 25일 게임 | 12 분 소요

Is this article helpful for you?

Thank you for your feedback!