호빗 체험

모바일 WebGL로 중간계에 생명을 불어넣다

Daniel Isaksson
Daniel Isaksson

지금까지는 웹 기반의 양방향 멀티미디어 경험을 모바일과 태블릿에 제공하기가 어려웠습니다. 주요 제약은 성능, API 가용성, 기기에서 HTML5 오디오의 제한, 원활한 인라인 동영상 재생 부족이었습니다.

올해 초 Google과 Warner Bros. 의 친구들과 함께 새로운 호빗 영화 호빗: 스마우그의 폐허를 위한 모바일 중심 웹 환경을 만드는 프로젝트를 시작했습니다. 멀티미디어 사용량이 많은 모바일 Chrome 실험실을 만드는 것은 정말 감동적이고 어려운 작업이었습니다.

이 환경은 이제 WebGL 및 웹 오디오에 액세스할 수 있는 새로운 Nexus 기기의 Android용 Chrome에 최적화되어 있습니다. 그러나 하드웨어 가속 합성 및 CSS 애니메이션 덕분에 WebGL이 아닌 기기와 브라우저에서도 대부분의 환경에 액세스할 수 있습니다.

전체 경험은 중간계의 지도와 호빗 영화의 위치 및 캐릭터를 기반으로 합니다. WebGL을 사용하면서 호빗 3부작의 풍성한 세계를 각색하고 탐험할 수 있었으며 사용자가 직접 경험을 제어할 수 있게 되었습니다.

모바일 기기에서의 WebGL 문제

첫째, '휴대기기'라는 용어는 매우 광범위합니다. 기기 사양은 매우 다양합니다. 따라서 개발자는 덜 복잡한 환경으로 더 많은 기기를 지원할지, 아니면 이 사례에서와 같이 더 현실적인 3D 세상을 표시할 수 있는 기기로 지원되는 기기를 제한할지 결정해야 합니다. '중간계를 가로지르는 여정'에서는 Nexus 기기와 5가지 인기 Android 스마트폰에 초점을 맞췄습니다.

이 실험에서는 이전 WebGL 프로젝트 중 일부에서와 마찬가지로 three.js를 사용했습니다. Google은 Nexus 10 태블릿에서 원활하게 실행되는 Trollhaw 게임의 초기 버전을 빌드하여 구현을 시작했습니다. 기기에서 몇 가지 초기 테스트를 거친 후, 저희는 저사양 노트북에 일반적으로 사용하는 것과 매우 유사한 최적화 목록을 염두에 두었습니다.

  • 로우 폴리 모델 사용
  • 저해상도 텍스처 사용
  • 도형을 병합하여 drawcall 수를 최대한 줄입니다.
  • 자재 및 조명 단순화
  • 게시물 효과 삭제 및 앤티앨리어싱 사용 중지
  • 자바스크립트 성능 최적화
  • WebGL 캔버스를 절반 크기로 렌더링하고 CSS를 사용하여 확장

이러한 최적화를 게임의 초기 버전에 적용한 후 30FPS의 안정적인 프레임 속도를 만족할 만한 수준으로 유지했습니다. 당시 우리의 목표는 프레임 속도에 부정적인 영향을 주지 않으면서 시각적 요소를 개선하는 것이었습니다. 우리는 많은 방법을 시도했습니다. 어떤 방법은 실제로 실적에 영향을 미치는 것으로 드러났지만, 어떤 방법은 기대만큼 큰 효과를 올리지 못했습니다.

로우 폴리 모델 사용

모델부터 시작하겠습니다. 로우 폴리 모델을 사용하면 다운로드 시간뿐만 아니라 장면을 초기화하는 데 걸리는 시간도 도움이 됩니다. 우리는 성능에 큰 영향을 미치지 않고 복잡성을 상당히 높일 수 있다는 것을 발견했습니다. 이 게임에서 사용하는 트롤 모델은 약 5,000개의 얼굴이고 장면은 약 40,000개의 얼굴이며 잘 작동합니다.

트롤숲의 트롤 중 한 명
Trollhaw 숲의 트롤 중 하나

환경의 다른 (아직 출시되지 않음) 위치에서 다각형을 줄이면 성능에 더 많은 영향을 미치는 것으로 확인되었습니다. 이 경우 데스크톱용으로 로드한 객체보다 휴대기기용 하위 다각형 객체를 로드했습니다. 여러 개의 3D 모델을 만들려면 추가 작업이 필요하며, 이 작업이 항상 필요한 것은 아닙니다. 실제로 얼마나 복잡한 모델인지에 따라 달라집니다.

많은 객체가 포함된 큰 장면을 작업할 때는 도형을 분할하는 방법에 전략적으로 접근하려고 했습니다. 이를 통해 덜 중요한 메시를 신속하게 켜고 끄고 모든 휴대기기에서 작동하는 설정을 찾을 수 있었습니다. 그런 다음 동적 최적화를 위해 런타임 시 자바스크립트의 도형을 병합하거나 사전 프로덕션 단계에서 병합하여 요청을 저장하도록 선택할 수 있습니다.

저해상도 텍스처 사용

휴대기기에서 로드 시간을 줄이기 위해 우리는 데스크톱에서 텍스처 크기의 절반에 불과한 다양한 텍스처를 로드하기로 했습니다. 모든 기기에서 최대 2048x2048px까지 텍스처 크기를 처리할 수 있고, 대부분의 기기에서 4096x4096px를 처리할 수 있는 것으로 나타났습니다. GPU에 업로드한 후에는 개별 텍스처의 텍스처 조회는 문제가 되지 않는 것으로 보입니다. 텍스처가 계속 업로드되고 다운로드되는 것을 방지하려면 텍스처의 전체 크기가 GPU 메모리에 맞아야 하지만, 이는 대부분의 웹 환경에서 큰 문제가 되지 않을 수 있습니다. 그러나 drawcall의 수를 줄이려면 텍스처를 가능한 한 적은 수의 스프라이트 시트로 결합하는 것이 중요합니다. 이는 휴대기기의 성능에 큰 영향을 미칩니다.

트롤숲의 트롤 중 하나의 텍스처
Trollhaw Forest의 트롤(Trollhaw) 숲속의 텍스처
(원본 크기 512x512px)

자재 및 조명 단순화

자재 선택도 실적에 큰 영향을 미칠 수 있으므로 모바일에서 현명하게 관리해야 합니다. Google은 성능을 최적화하기 위해 MeshPhongMaterial (텍셀 라이트 계산당) 대신 third.js에서 MeshLambertMaterial (꼭짓점 광원 계산당)을 사용했습니다. 기본적으로 최대한 적은 수의 광원 계산으로 간단한 셰이더를 사용하려고 했습니다.

사용하는 소재가 장면의 성능에 미치는 영향을 확인하려면 MeshBasicMaterial로 장면의 소재를 재정의하면 됩니다 . 이렇게 하면 효과적으로 비교할 수 있습니다.

scene.overrideMaterial = new THREE.MeshBasicMaterial({color:0x333333, wireframe:true});

자바스크립트 성능 최적화

모바일용 게임을 개발할 때 GPU가 항상 가장 큰 장애물은 아닙니다. CPU, 특히 물리학 및 뼈대 애니메이션에 많은 시간이 소요됩니다. 시뮬레이션에 따라 가끔 도움이 되는 한 가지 요령은 두 프레임마다 이렇게 비용이 많이 드는 계산을 실행하는 것입니다. 객체 풀링, 가비지 컬렉션 및 객체 생성에 관해서는 사용 가능한 JavaScript 최적화 기법을 사용할 수도 있습니다.

새 객체를 만드는 대신 사전 할당된 객체를 루프에서 업데이트하는 것이 게임 중에 가비지 컬렉션 '문제'를 방지하는 데 중요한 단계입니다.

예를 들어 다음과 같은 코드를 살펴보겠습니다.

var currentPos = new THREE.Vector3();

function gameLoop() {
  currentPos = new THREE.Vector3(0+offsetX,100,0);
}

이 루프의 개선된 버전을 사용하면 가비지 수집해야 하는 새 객체를 만들지 않아도 됩니다.

var originPos = new THREE.Vector3(0,100,0);
var currentPos = new THREE.Vector3();
function gameLoop() {
  currentPos.copy(originPos).x += offsetX;
  //or
  currentPos.set(originPos.x+offsetX,originPos.y,originPos.z);
}

이벤트 핸들러는 최대한 속성만 업데이트하고 requestAnimationFrame 렌더링 루프가 스테이지 업데이트를 처리하도록 해야 합니다.

또 다른 팁은 광선 전송 작업을 최적화하거나 미리 계산하는 것입니다. 예를 들어 정적 경로 이동 중에 메시에 객체를 연결해야 하는 경우 한 루프 동안 위치를 '기록'한 다음 메시에 광선을 전송하는 대신 이 데이터에서 읽을 수 있습니다. 또는 Rivendell 환경에서와 같이 레이캐스트를 사용하여 간단한 로우 폴리 보이지 않는 메시를 사용한 마우스 상호작용을 찾습니다. 하이 폴리 메시에서 충돌을 찾는 작업은 매우 느리므로 일반적으로 게임 루프에서는 피해야 합니다.

WebGL 캔버스를 절반 크기로 렌더링하고 CSS를 사용하여 확장

WebGL 캔버스의 크기는 아마도 성능 최적화를 위해 조정할 수 있는 가장 효과적인 단일 매개변수일 것입니다. 3D 장면을 그리는 데 사용하는 캔버스가 클수록 모든 프레임에 더 많은 픽셀을 그려야 합니다. 이는 당연히 성능에 영향을 미칩니다.고밀도 2560x1600 픽셀 디스플레이를 장착한 Nexus 10은 저밀도 태블릿보다 4배 더 많은 픽셀을 푸시해야 합니다. 모바일에 최적화하기 위해 캔버스의 크기를 절반 (50%)으로 설정한 다음 하드웨어 가속 CSS 3D 변환을 사용하여 원하는 크기 (100%)로 확대하는 방법을 사용합니다. 단점은 얇은 선이 문제가 될 수 있는 픽셀화된 이미지이지만 고해상도 화면에서는 그 효과가 그렇게 나쁘지 않습니다. 추가 성능을 사용할 만한 가치가 있습니다.

Nexus 10 (16FPS)에서 캔버스 크기 조정이 없고 50% (33FPS)로 조정된 동일한 장면
Nexus 10 (16FPS)에서 캔버스 크기 조정이 없고 50% (33FPS)로 조정된 동일한 장면

빌딩 블록으로서의 객체

돌 굴두르성의 거대한 미로와 끝없이 긴 리벤델의 계곡을 만들기 위해 저희는 재사용 가능한 일련의 빌딩 블록 3D 모델을 만들었습니다. 객체를 재사용하면 객체가 환경 중간이 아닌 시작 시에 인스턴스화되고 업로드되도록 할 수 있습니다.

돌 굴두르의 미로에서 사용된 3D 개체 빌딩 블록입니다.
돌 굴두르의 미로에서 사용되는 3D 객체 빌딩 블록

Rivendell에는 사용자의 여정이 진행됨에 따라 계속해서 Z-D 깊이로 재배치하는 여러 그라운드 섹션이 있습니다. 사용자가 섹션을 통과하면 먼 거리로 위치가 다시 지정됩니다.

Dol Guldur성의 경우 게임을 플레이할 때마다 미로를 재생성하고자 했습니다. 이를 위해 미로를 재생성하는 스크립트를 만들었습니다.

처음부터 전체 구조를 하나의 커다란 메시로 병합하면 장면이 매우 커지고 성능이 저하됩니다. 이 문제를 해결하기 위해, 우리는 빌딩 블록이 시야에 있는지에 따라 숨기거나 표시하기로 했습니다. 처음부터 2D 레이캐스트 스크립트를 사용하겠다는 아이디어가 있었지만 결국에는 기본 제공 3.js 절두체 컬링을 사용했습니다. 플레이어가 직면한 '위험'을 확대하기 위해 레이캐스트 스크립트를 재사용했습니다.

다음으로 처리해야 할 중요한 사항은 사용자 상호작용입니다. 데스크톱에는 마우스 및 키보드 입력이 있고 휴대기기에서는 사용자가 터치, 스와이프, 손가락 모으기, 기기 방향 등과 상호작용합니다.

모바일 웹 환경에서 터치 상호작용 사용

터치 지원을 추가하는 것은 어렵지 않습니다. 이 주제에 관해 읽어볼 만한 유용한 도움말이 있습니다. 하지만 몇 가지 사소한 문제로 인해 이러한 과정이 더 복잡해질 수 있습니다.

터치와 마우스를 모두 사용할 수 있습니다. Chromebook Pixel 및 기타 터치 지원 노트북은 마우스와 터치를 모두 지원합니다. 한 가지 흔한 실수는 기기가 터치를 사용할 수 있는지 확인한 다음 터치 이벤트 리스너만 추가하고 마우스에는 추가하지 않는 것입니다.

이벤트 리스너의 렌더링을 업데이트하지 마세요. 대신 터치 이벤트를 변수에 저장하고 requestAnimationFrame 렌더링 루프에서 이에 반응합니다. 이렇게 하면 성능이 향상되고 충돌하는 이벤트도 병합됩니다. 이벤트 리스너에서 새 객체를 만드는 대신 객체를 재사용해야 합니다.

멀티터치라는 점을 기억하세요. event.touches는 모든 터치의 배열입니다. 경우에 따라 event.targetTouches 또는 event.changedTouches를 살펴보고 관심 있는 터치에만 반응하는 것이 더 흥미로운 경우도 있습니다. 스와이프와 스와이프를 구분하기 위해 터치가 움직였는지 (스와이프) 또는 계속 움직이고 있는지 (탭) 확인하기 전에 지연 시간을 사용합니다. 손가락을 모으기 위해 두 초기 터치 사이의 거리와 시간이 지남에 따라 어떻게 변하는지 측정합니다.

3D 환경에서는 카메라가 마우스 동작과 스와이프 동작에 어떻게 반응할지 결정해야 합니다. 카메라 움직임을 추가하는 일반적인 방법 중 하나는 마우스 움직임을 따라가는 것입니다. 마우스 위치를 사용하여 직접 제어하거나 델타 이동 (위치 변경)을 사용하면 됩니다. 휴대기기에서 데스크톱 브라우저와 동일한 동작을 항상 원하는 것은 아닙니다. Google은 각 버전에 적합한 요소를 결정하기 위해 광범위한 테스트를 거쳤습니다.

작은 화면과 터치스크린을 다룰 때는 사용자의 손가락과 UI 상호작용 그래픽이 여러분이 보여주고자 하는 것과 방해가 되는 경우가 많다는 것을 알게 될 것입니다. 이는 우리가 네이티브 앱을 설계할 때는 익숙한 것이지만 웹 환경에서는 이 부분에 대해 크게 신경 쓸 필요가 없습니다. 이는 디자이너와 UX 디자이너에게 정말 어려운 문제입니다.

요약

이 프로젝트를 통해 얻은 전반적인 경험은 모바일에서 WebGL이 특히 최신 고급형 기기에서 잘 작동한다는 것입니다. 성능 측면에서는 다각형 수와 텍스처 크기가 대부분 다운로드와 초기화 시간에 영향을 미치는 것으로 보이며, WebGL 캔버스의 머티리얼, 셰이더, 크기는 모바일 성능을 최적화하기 위해 가장 중요한 부분입니다. 하지만 성능에 영향을 미치는 부분의 합이므로 개수를 최적화하기 위해 할 수 있는 모든 작업이 여기에 포함됩니다.

또한 휴대기기를 타겟팅한다는 것은 터치 상호작용에 대해 생각하는 데 익숙해져야 함을 의미합니다. 이것은 픽셀 크기뿐만 아니라 화면의 물리적 크기까지 고려해야 합니다. 어떤 경우에는 실제로 상황을 확인하기 위해 3D 카메라를 더 가까이 가져가야 했습니다.

실험을 시작했고 환상적인 여정이었어요. 즐거운 시간 되시길 바랍니다.

이용해 보시겠습니까? 나만의 중간계로 가는 여정을 떠나 보세요.