모바일 WebGL로 중간계 구현하기
지금까지는 양방향 웹 기반 멀티미디어 환경을 모바일과 태블릿에 제공하는 것이 쉽지 않았습니다. 주요 제약사항은 성능, API 가용성, 기기의 HTML5 오디오 제한, 원활한 인라인 동영상 재생의 부족이었습니다.
올해 초 Google과 Warner Bros. 의 친구들과 함께 새로운 호빗 영화 호빗: 스마우그의 황폐화를 위한 모바일 중심 웹 환경을 만드는 프로젝트를 시작했습니다. 멀티미디어를 많이 사용하는 모바일 Chrome 실험을 구축하는 것은 매우 고무적이면서도 어려운 작업이었습니다.
이 환경은 이제 WebGL 및 Web Audio에 액세스할 수 있는 새로운 Nexus 기기의 Android용 Chrome에 최적화되어 있습니다. 그러나 하드웨어 가속 컴포지션과 CSS 애니메이션 덕분에 WebGL이 아닌 기기와 브라우저에서도 환경의 상당 부분에 액세스할 수 있습니다.
전체 환경은 중간계 지도와 호빗 영화의 위치 및 캐릭터를 기반으로 합니다. WebGL을 사용하면 호빗 삼부작의 풍부한 세계를 극적으로 묘사하고 탐색할 수 있으며 사용자가 환경을 제어할 수 있습니다.
휴대기기의 WebGL과 관련된 문제
첫째, '휴대기기'라는 용어는 매우 광범위합니다. 기기의 사양은 매우 다양합니다. 따라서 개발자는 덜 복잡한 환경으로 더 많은 기기를 지원할지 아니면 이 사례에서와 같이 더 사실적인 3D 세상을 표시할 수 있는 기기로 지원 기기를 제한할지 결정해야 합니다. 'Journey through Middle-earth'에서는 Nexus 기기와 인기 있는 Android 스마트폰 5대 중 하나를 사용했습니다.
실험에서는 이전의 일부 WebGL 프로젝트에서와 마찬가지로 three.js를 사용했습니다. Nexus 10 태블릿에서 잘 실행되는 Trollshaw 게임의 초기 버전을 빌드하여 구현을 시작했습니다. 기기에서 몇 가지 초기 테스트를 진행한 후 저사양 노트북에 일반적으로 사용하는 것과 유사한 최적화 목록을 작성했습니다.
- 로우 폴리 모델 사용
- 저해상도 텍스처 사용
- 도형을 병합하여 최대한 많은 드로우 콜 수를 줄입니다.
- 재료 및 조명 간소화
- 후처리 효과 삭제 및 안티앨리어싱 사용 중지
- JavaScript 성능 최적화
- WebGL 캔버스를 절반 크기로 렌더링하고 CSS로 크기를 조정합니다.
이러한 최적화를 게임의 첫 번째 대략적인 버전에 적용한 후 만족스러운 30FPS의 안정적인 프레임 속도를 얻었습니다. 당시 Google의 목표는 프레임 속도에 부정적인 영향을 미치지 않으면서 시각적 효과를 개선하는 것이었습니다. 다양한 방법을 시도해 보았습니다. 그중 일부는 실적에 실제로 영향을 미쳤고, 일부는 기대만큼 큰 효과를 내지 못했습니다.
로우 폴리 모델 사용
모델부터 시작하겠습니다. 로우 폴리 모델을 사용하면 다운로드 시간과 장면을 초기화하는 데 걸리는 시간을 줄일 수 있습니다. 성능에 큰 영향을 주지 않으면서 복잡성을 상당히 높일 수 있다는 사실을 발견했습니다. 이 게임에서 사용하는 트롤 모델은 약 5,000개의 얼굴이고 장면은 약 40,000개의 얼굴이며 이 정도면 괜찮습니다.

환경의 다른 위치 (아직 출시되지 않음)에서는 다각형을 줄이면 성능에 더 큰 영향을 미치는 것으로 나타났습니다. 이 경우 데스크톱용으로 로드한 객체보다 휴대기기용으로 로드한 객체의 다각형 수가 적었습니다. 다양한 3D 모델 세트를 만들려면 추가 작업이 필요하며 항상 필요한 것은 아닙니다. 모델의 복잡도에 따라 다릅니다.
객체가 많은 대규모 장면을 작업할 때는 도형을 분할하는 방법을 전략적으로 고려했습니다. 이렇게 하면 덜 중요한 메시지를 빠르게 켜거나 끌 수 있으므로 모든 휴대기기에 적합한 설정을 찾을 수 있습니다. 그런 다음 동적 최적화를 위해 런타임 시 JavaScript에서 도형을 병합하거나 요청을 저장하기 위해 사전 프로덕션에서 병합할 수 있습니다.
저해상도 텍스처 사용
모바일 기기의 로드 시간을 줄이기 위해 데스크톱의 텍스처 크기의 절반인 다른 텍스처를 로드하기로 했습니다. 모든 기기는 최대 2048x2048픽셀의 텍스처 크기를 처리할 수 있으며 대부분은 4096x4096픽셀을 처리할 수 있습니다. 개별 텍스처가 GPU에 업로드된 후에는 텍스처 조회가 문제가 되지 않는 것 같습니다. 텍스처가 지속적으로 업로드 및 다운로드되지 않도록 하려면 텍스처의 총 크기가 GPU 메모리에 맞아야 하지만, 이는 대부분의 웹 환경에서는 큰 문제가 아닙니다. 하지만 텍스처를 최대한 적은 수의 스프라이트 시트로 결합하는 것이 중요합니다. 이는 drawcall 수를 줄이기 위한 조치로, 모바일 기기의 성능에 큰 영향을 미칩니다.

(원래 크기 512x512픽셀)
재료 및 조명 간소화
재료 선택도 성능에 큰 영향을 미칠 수 있으므로 모바일에서는 현명하게 관리해야 합니다. three.js에서 MeshPhongMaterial
(텍셀당 조명 계산) 대신 MeshLambertMaterial
(정점당 조명 계산)을 사용하는 것이 성능을 최적화하는 데 사용된 한 가지 방법입니다. 기본적으로 조명 계산을 최대한 적게 사용하여 간단한 셰이더를 사용해 보았습니다.
사용하는 재료가 장면의 성능에 어떤 영향을 미치는지 확인하려면 MeshBasicMaterial
로 장면의 재료를 재정의하면 됩니다 . 이렇게 하면 비교가 쉬워집니다.
scene.overrideMaterial = new THREE.MeshBasicMaterial({color:0x333333, wireframe:true});
JavaScript 성능 최적화
모바일용 게임을 빌드할 때 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%)로 조정하는 트릭을 사용합니다. 단점은 얇은 선이 문제가 될 수 있는 모자이크 현상이 발생한다는 점이지만 고해상도 화면에서는 그다지 나쁘지 않습니다. 성능이 향상되는 만큼 충분히 가치가 있습니다.

구성 요소로서의 객체
돌굴두르 성의 거대한 미로와 끝없는 리븐델 계곡을 만들 수 있도록 재사용할 수 있는 빌딩 블록 3D 모델 세트를 제작했습니다. 객체를 재사용하면 환경 중간에 객체가 인스턴스화되고 업로드되지 않고 환경 시작 시 인스턴스화되고 업로드되도록 할 수 있습니다.

Rivendell에는 사용자 여정이 진행됨에 따라 Z-depth에서 지속적으로 재위치를 지정하는 여러 지상 섹션이 있습니다. 사용자가 섹션을 지나면 섹션이 먼 거리에 다시 배치됩니다.
Dol Guldur 성의 경우 매 게임마다 미로가 재생성되기를 원했습니다. 이를 위해 미로를 재생성하는 스크립트를 만들었습니다.
처음부터 전체 구조를 하나의 큰 메시에 병합하면 매우 큰 장면이 생성되고 성능이 저하됩니다. 이 문제를 해결하기 위해 뷰에 있는지 여부에 따라 빌딩 블록을 숨기거나 표시하기로 했습니다. 처음부터 2D 레이캐스터 스크립트를 사용하려 했지만 결국 내장된 three.js 프러스텀 컬링을 사용했습니다. 레이캐스터 스크립트를 재사용하여 플레이어가 직면한 '위험'을 확대했습니다.
다음으로 처리해야 할 중요한 사항은 사용자 상호작용입니다. 데스크톱에서는 마우스와 키보드 입력이 사용됩니다. 휴대기기에서는 사용자가 터치, 스와이프, 핀치, 기기 방향 등으로 상호작용합니다.
모바일 웹 환경에서 터치 상호작용 사용
터치 지원을 추가하는 것은 어렵지 않습니다. 이 주제에 관한 유용한 도움말이 있습니다. 하지만 조금만 더 복잡해질 수 있는 작은 문제가 있습니다.
터치와 마우스를 둘 다 사용할 수 있습니다. Chromebook Pixel 및 기타 터치 지원 노트북은 마우스와 터치를 모두 지원합니다. 일반적인 실수는 기기가 터치 지원 여부를 확인한 후 터치 이벤트 리스너만 추가하고 마우스 이벤트 리스너는 추가하지 않는 것입니다.
이벤트 리스너에서 렌더링을 업데이트하지 마세요. 대신 터치 이벤트를 변수에 저장하고 requestAnimationFrame 렌더링 루프에서 이에 반응합니다. 이렇게 하면 성능이 개선되고 충돌하는 이벤트가 병합됩니다. 이벤트 리스너에서 새 객체를 만드는 대신 객체를 재사용해야 합니다.
멀티 터치입니다. event.touches는 모든 터치의 배열입니다. 경우에 따라 event.targetTouches 또는 event.changedTouches를 대신 보고 관심 있는 터치에만 반응하는 것이 더 흥미로울 수 있습니다. 탭과 스와이프를 구분하기 위해 터치가 이동했는지 (스와이프) 또는 움직이지 않았는지 (탭) 확인하기 전에 지연을 사용합니다. 핀치 동작을 가져오려면 두 개의 초기 터치 사이의 거리와 시간이 지남에 따라 어떻게 달라지는지 측정합니다.
3D 환경에서는 마우스 작업과 스와이프 작업에 카메라가 어떻게 반응할지 결정해야 합니다. 카메라 움직임을 추가하는 일반적인 방법 중 하나는 마우스 움직임을 따르는 것입니다. 마우스 위치를 사용한 직접 제어 또는 델타 이동 (위치 변경)을 사용하여 수행할 수 있습니다. 휴대기기에서 데스크톱 브라우저와 동일한 동작을 항상 원하는 것은 아닙니다. 각 버전에 적합한 버전을 결정하기 위해 광범위한 테스트를 진행했습니다.
작은 화면과 터치 스크린을 다룰 때는 사용자의 손가락과 UI 상호작용 그래픽이 표시하려는 내용을 가리는 경우가 많습니다. 이는 네이티브 앱을 설계할 때는 익숙하지만 웹 환경에서는 이전에 고려하지 않았던 사항입니다. 이는 디자이너와 UX 디자이너에게 진정한 도전입니다.
요약
이 프로젝트에서 얻은 전반적인 경험은 모바일의 WebGL이 특히 최신 고급 기기에서 매우 잘 작동한다는 것입니다. 성능과 관련하여 다각형 수와 텍스처 크기는 다운로드 및 초기화 시간에 주로 영향을 미치는 것으로 보이며, 소재, 셰이더, WebGL 캔버스의 크기는 모바일 성능에 맞게 최적화하는 데 가장 중요한 부분입니다. 하지만 실적에 영향을 미치는 요소의 합계이므로 집계를 최적화하기 위해 할 수 있는 모든 조치를 취해야 합니다.
휴대기기를 타겟팅한다는 것은 터치 상호작용에 대해 생각하는 데 익숙해져야 한다는 것을 의미하며, 픽셀 크기뿐만 아니라 화면의 실제 크기도 고려해야 합니다. 어떤 경우에는 3D 카메라를 더 가까이 옮겨야 실제로 무슨 일이 일어나고 있는지 확인할 수 있었습니다.
실험이 시작되었으며 멋진 여정을 함께했습니다. 즐겁게 이용해 주시기 바랍니다.
이용해 보시겠습니까? 나만의 중간계 여행을 떠나 보세요.