WebVR의 댄스 톤

Google 데이터 아트팀이 Moniker와 저에게 연락해 WebVR이 가져온 가능성을 함께 탐색할 수 있을 때 정말 기뻤습니다. 몇 년 동안 이 팀들이 작업을 하는 것을 지켜봤는데 항상 저와 공감대를 형성했습니다. 이러한 협업으로 LCD 사운드시스템 및 팬들과 함께하는 끊임없이 변화하는 VR 댄스 경험인 Dance Tonite가 탄생했습니다. 이렇게 했습니다.

기본 개념

Google은 브라우저를 사용하여 웹사이트를 방문하여 VR에 들어갈 수 있는 개방형 표준인 WebVR을 사용하여 일련의 프로토타입을 개발하기 시작했습니다. 따라서 사용 중인 기기에 관계없이 누구나 VR 경험을 더 쉽게 즐길 수 있도록 하는 것이 목표입니다.

이를 염두에 두었습니다. Google의 Daydream View, Cardboard, 삼성의 Gear VR과 같이 휴대전화와 호환되는 VR 헤드셋에서부터 가상 환경에서 사용자의 물리적 움직임을 반영하는 HTC VIVE 및 Oculus Rift와 같은 룸스케일 시스템에 이르기까지 모든 유형의 VR에서 활용할 수 있습니다. 무엇보다도 VR 기기를 갖고 있지 않은 사람도 모두 이용할 수 있는 게임을 만들 수 있으면 좋겠다고 생각했습니다.

1. DIY 모션 캡처

창의적으로 사용자의 참여를 유도하고 싶었기 때문에 VR을 통해 참여와 자기 표현의 가능성을 모색하기 시작했습니다. VR에서 얼마나 섬세하게 움직이고 주변을 둘러볼 수 있는지, 충실도도 높은지 정말 인상적이었습니다. 이것으로 아이디어를 얻었습니다. 사용자에게 무언가를 보거나 만드는 대신 움직임을 기록하면 어떨까요?

댄스 토니트에 자신을 녹음하는 사람이 있다. 뒤쪽 화면에 헤드셋에서 보이는 내용이 표시됩니다.

우리는 춤추는 동안 VR 고글과 컨트롤러의 위치를 기록하는 프로토타입을 만들었습니다. 우리는 기록된 위치를 추상적인 모양으로 바꾸고 결과에 놀랐습니다. 그 결과는 매우 인간적인 것이었고 많은 개성을 담고 있었습니다. 우리는 WebVR을 사용하여 집에서 저렴한 비용으로 모션 캡처를 할 수 있다는 것을 금방 깨달았습니다.

WebVR을 사용하면 개발자가 VRPose 객체를 통해 사용자의 머리 위치와 방향에 액세스할 수 있습니다. 이 값은 코드가 올바른 관점에서 새 프레임을 렌더링할 수 있도록 VR 하드웨어에 의해 모든 프레임에 업데이트됩니다. 또한 WebVR이 포함된 GamePad API를 통해 GamepadPose 객체를 통해 사용자 컨트롤러의 위치/방향에 액세스할 수도 있습니다. 우리는 단순히 모든 프레임에 이러한 모든 위치 및 방향 값을 저장하여 사용자의 움직임을 '기록'합니다.

2. 미니멀리즘 및 의상

오늘날의 방 규모 VR 장비로 사용자 몸의 세 지점, 즉 머리와 두 손을 추적할 수 있습니다. Dance Tonite에서는 우주의 이 세 지점이 움직일 때 인간에 초점을 맞추고 싶었습니다. 이를 위해 움직임에 집중하기 위해 미적인 요소를 최대한 최소화했습니다. 사람들의 뇌를 발휘하는 아이디어가 좋았습니다.

이 동영상은 스웨덴 심리학자 군나르 요한슨의 연구를 보여주는 것으로, Google은 최대한 제거를 고려할 때 언급한 사례 중 하나입니다. 이 모델은 떠다니는 흰색 점이 어떻게 움직이는 물체로 인식되는지를 보여줍니다.

마가레테 헤이스팅스의 1970년 오스카 슐레머의 3부대 발레를 재연한 장면에서 우리는 색상이 있는 방과 기하학적 의상에서 영감을 얻었습니다.

슐레머가 추상적인 기하학적 의상을 선택한 이유는 무용수의 움직임을 꼭두각시와 꼭두각시의 움직임으로 제한하기 위해서였지만, 우린 댄스 토나이트의 반대 목표를 세웠습니다.

결국 Google은 회전을 통해 전달되는 정보의 양을 기반으로 도형을 선택했습니다. 구는 어떻게 회전하든 동일하게 보이지만 원뿔은 실제로 보는 방향을 가리키며 뒤쪽과 전면에서 다르게 보입니다.

3. 움직임을 위해 고리 페달

우리는 여러 사람이 춤추고 움직일 수 있도록 녹화된 대규모 그룹을 보여주려고 했습니다. VR 기기의 개수가 많지 않기 때문에 실시간 스트림은 불가능할 것입니다. 하지만 우리는 여전히 움직임을 통해 서로에게 반응할 사람들이 있길 원했습니다. 우리는 노먼 맥클라렌의 1964년 동영상 작품인 '캐논'에서 반복적인 공연을 떠올렸습니다.

McClaren의 공연에서는 매 루프마다 서로 상호작용하기 시작하는 일련의 고도로 연출된 악장을 보여줍니다. 음악가들이 여러 라이브 음악을 겹쳐서 연주하는 루프 페달과 매우 흡사하게, 우리는 사용자가 더 느슨한 버전의 공연을 자유롭게 즉흥적으로 연주할 수 있는 환경을 만들 수 있을지 알아보고자 했습니다.

4. 서로 연결된 회의실

서로 연결된 회의실

다른 많은 음악과 마찬가지로 LCD 사운드 시스템의 트랙은 정확한 타이밍에 따른 척도를 사용하여 구성됩니다. 프로젝트에 사용된 Tonite 트랙에는 정확히 8초 길이의 측정치가 포함되어 있습니다. 사용자가 트랙의 8초 루프마다 공연을 하도록 하고 싶었습니다. 이러한 소절의 리듬은 변하지 않지만 음악 콘텐츠는 변화합니다. 곡이 진행됨에 따라 연주자는 다양한 악기와 보컬에 따라 여러 가지 방식으로 반응할 수 있습니다. 이러한 각 측정값은 방으로 표현되므로 사람들이 이 공간에 맞는 공연을 할 수 있습니다.

성능 최적화: 프레임 드롭 금지

각 기기나 플랫폼에 최적화된 성능을 갖춘 단일 코드베이스에서 실행되는 다중 플랫폼 VR 환경을 만드는 것은 간단한 일이 아닙니다.

VR로 볼 때 가장 화가 나는 상황 중 하나는 프레임 속도가 움직임을 따라잡지 못하기 때문입니다. 고개를 돌리지만 눈으로 보는 시각이 속귀가 느끼는 움직임과 일치하지 않으면 즉각적인 배탈이 일어납니다. 이러한 이유로 큰 프레임 속도 지연을 방지해야 했습니다. 다음은 구현한 몇 가지 최적화입니다.

1. 인스턴스화된 버퍼 도형

전체 프로젝트에서 몇 개의 3D 객체만 사용하므로 인스턴스 버퍼 도형을 사용하여 성능을 크게 향상할 수 있었습니다. 기본적으로 객체를 GPU에 한 번 업로드하고 단일 그리기 호출에서 원하는 만큼 객체의 '인스턴스'를 그릴 수 있습니다. Dance Tonite에는 3개의 서로 다른 객체 (원뿔, 원기둥, 구멍이 있는 방)만 있지만 이러한 객체의 사본 수백 개는 있을 수 있습니다. 인스턴스 버퍼 도형은 ThreeJS의 일부이지만 THREE.InstanceMesh를 구현하는 Dusan Bosnjak의 실험용 및 진행 중인 포크를 사용했기 때문에 인스턴스 버퍼 도형을 훨씬 더 쉽게 사용할 수 있습니다.

2. 가비지 컬렉터 방지

다른 많은 스크립트 언어와 마찬가지로 JavaScript는 할당된 객체를 더 이상 사용하지 않는 것을 찾아 자동으로 메모리를 확보합니다. 이 프로세스를 가비지 컬렉션이라고 합니다.

이러한 상황이 발생하는 경우 개발자는 이를 제어할 수 없습니다. 가비지 컬렉터는 언제든지 문에 표시되어 가비지를 비우기 시작할 수 있으며, 결과적으로 소중한 시간이 소요되는 프레임이 떨어질 수 있습니다.

이 문제를 해결하는 방법은 객체를 재활용하여 가능한 한 적은 가비지를 생성하는 것입니다. 계산할 때마다 새로운 벡터 객체를 만드는 대신 스크래치 객체를 재사용할 수 있도록 표시했습니다. Google에서는 이러한 참조에 관한 참조를 지원 범위 외부로 이동하여 이를 유지하고 있으므로 삭제 대상으로 표시되지 않았습니다.

예를 들어 다음은 사용자 머리와 손의 위치 행렬을 각 프레임에 저장된 위치/회전 값의 배열로 변환하는 코드입니다. SERIALIZE_POSITION, SERIALIZE_ROTATION, SERIALIZE_SCALE를 재사용함으로써 함수가 호출될 때마다 새 객체를 만들 때 발생할 수 있는 메모리 할당 및 가비지 컬렉션을 방지합니다.

const SERIALIZE_POSITION = new THREE.Vector3();
const SERIALIZE_ROTATION = new THREE.Quaternion();
const SERIALIZE_SCALE = new THREE.Vector3();
export const serializeMatrix = (matrix) => {
    matrix.decompose(SERIALIZE_POSITION, SERIALIZE_ROTATION, SERIALIZE_SCALE);
    return SERIALIZE_POSITION.toArray()
    .concat(SERIALIZE_ROTATION.toArray())
    .map(compressNumber);
};

3. 모션 및 프로그레시브 재생 직렬화

VR에서 사용자의 움직임을 포착하려면 헤드셋과 컨트롤러의 위치와 회전을 직렬화하고 이 데이터를 Google 서버에 업로드해야 했습니다. 모든 프레임의 전체 변환 행렬을 캡처하기 시작했습니다. 성능이 우수했지만, 초당 90프레임에 숫자 16개와 3개의 위치를 곱했기 때문에 파일이 매우 커지기 때문에 데이터를 업로드하고 다운로드하는 동안 대기 시간이 길어졌습니다. 변환 행렬에서 위치 데이터와 회전 데이터만 추출하여 값을 16에서 7까지 줄일 수 있었습니다.

웹 방문자는 내용을 정확히 모르고 링크를 클릭하는 경우가 많으므로 시각적 콘텐츠를 빠르게 표시해야 하며, 그러지 않으면 몇 초 만에 이탈하게 됩니다.

이러한 이유로 가능한 한 빨리 프로젝트가 재생되도록 하고 싶었습니다. 처음에는 움직임 데이터를 로드하는 형식으로 JSON을 사용했습니다. 문제는 전체 JSON 파일을 먼저 로드해야 이를 파싱할 수 있다는 점입니다. 그다지 진보적이지는 않습니다.

Dance Tonite와 같은 프로젝트를 가능한 가장 높은 프레임 속도로 표시하기 위해 브라우저에서는 자바스크립트 계산을 위해 프레임마다 적은 시간만 허용합니다. 시간이 너무 오래 걸리면 애니메이션이 끊기기 시작합니다. 처음에는 이러한 큰 JSON 파일이 브라우저에서 디코딩될 때 끊김 현상이 발생했습니다.

NDJSON 또는 줄바꿈으로 구분된 JSON이라고 하는 편리한 스트리밍 데이터 형식을 발견했습니다. 여기서 요령은 일련의 유효한 JSON 문자열을 각각 고유한 줄에 포함하는 파일을 만드는 것입니다. 이렇게 하면 로드 중에 파일을 파싱할 수 있으므로 파일이 완전히 로드되기 전에 성능을 표시할 수 있습니다.

Google 녹음 파일의 섹션은 다음과 같이 구성됩니다.

{"fps":15,"count":1,"loopIndex":"1","hideHead":false}
[-464,17111,-6568,-235,-315,-44,9992,-3509,7823,-7074, ... ]
[-583,17146,-6574,-215,-361,-38,9991,-3743,7821,-7092, ... ]
[-693,17158,-6580,-117,-341,64,9993,-3977,7874,-7171, ... ]
[-772,17134,-6591,-93,-273,205,9994,-4125,7889,-7319, ... ]
[-814,17135,-6620,-123,-248,408,9988,-4196,7882,-7376, ... ]
[-840,17125,-6644,-173,-227,530,9982,-4174,7815,-7356, ... ]
[-868,17120,-6670,-148,-183,564,9981,-4069,7732,-7366, ... ]
...

NDJSON을 사용하면 공연의 개별 프레임에 대한 데이터 표현을 문자열로 유지할 수 있습니다. 필요한 시간에 도달할 때까지 기다렸다가 위치 데이터로 디코딩하여 시간이 지남에 따라 필요한 처리를 분산할 수 있습니다.

4. 보간 이동

동시에 30~60개의 성능을 표시하고 싶었기 때문에 데이터 속도를 기존보다 훨씬 더 낮춰야 했습니다. 데이터 아트팀은 Virtual Art Sessions 프로젝트에서 이 문제를 해결했습니다. 여기서는 아티스트가 Tilt Brush를 사용하여 VR로 그림을 그리는 녹화본을 재생했습니다. 이 문제는 프레임 속도가 낮은 사용자 데이터의 중간 버전을 만들고 재생하는 동안 프레임 간에 보간하는 방식으로 해결했습니다. 15FPS로 실행되는 보간된 기록과 원래 90FPS로 실행되는 기록 간의 차이를 거의 발견할 수 없다는 사실에 놀랐습니다.

직접 확인하려면 ?dataRate= 쿼리 문자열을 사용하여 Dance Tonite에서 다양한 속도로 데이터를 재생하도록 강제할 수 있습니다. 이를 사용하여 초당 90프레임, 초당 45프레임 또는 초당 15프레임에서 녹화된 모션을 비교할 수 있습니다.

위치의 경우 키프레임 간의 시간 범위 (비율)에 따라 이전 키프레임과 다음 키프레임 사이에 선형 보간을 수행합니다.

const { x: x1, y: y1, z: z1 } = getPosition(previous, performanceIndex, limbIndex);
const { x: x2, y: y2, z: z2 } = getPosition(next, performanceIndex, limbIndex);
interpolatedPosition = new THREE.Vector3();
interpolatedPosition.set(
    x1 + (x2 - x1) * ratio,
    y1 + (y2 - y1) * ratio,
    z1 + (z2 - z1) * ratio
    );

방향의 경우 키프레임 간에 구면 선형 보간 유형 (slerp)을 실행합니다. 방향은 쿼터니언으로 저장됩니다.

const quaternion = getQuaternion(previous, performanceIndex, limbIndex);
quaternion.slerp(
    getQuaternion(next, performanceIndex, limbIndex),
    ratio
    );

5. 움직임을 음악에 동기화

녹화된 애니메이션에서 재생할 프레임을 알기 위해서는 음악의 현재 시간을 밀리초 단위까지 알아야 합니다. HTML 오디오 요소가 점진적으로 사운드를 로드하고 재생하는 데 적합하지만 이 요소에서 제공하는 시간 속성은 브라우저의 프레임 루프와 동기화되어 변경되지 않습니다. 이는 항상 약간 벗어납니다. 어떤 경우에는 1밀리초가 너무 이르고, 어떤 밀리초도 너무 늦을 때도 있습니다.

이렇게 하면 아름다운 댄스 녹음에서 끊어지는 현상으로 이어지며, 이러한 현상은 절대로 피해야 합니다. 이 문제를 해결하기 위해 자바스크립트에 자체 타이머를 구현했습니다. 이렇게 하면 프레임 간에 변경되는 시간이 정확히 마지막 프레임 이후 경과된 시간을 확신할 수 있습니다. 타이머가 음악과 10ms 이상 동기화되지 않을 때마다 다시 동기화합니다.

6. 컬링 및 안개

모든 스토리에는 좋은 결말이 있어야 하며, YouTube는 스토리를 끝까지 읽어낸 사용자를 위해 놀라움을 선사하고자 했습니다. 마지막 방을 나가면 원뿔과 원기둥으로 이루어진 조용한 풍경 같은 곳으로 들어갑니다. '이게 끝인가요?'라고 생각하실 수 있습니다. 운동장으로 더 나아가면 음악의 톤이 갑자기 다양한 원뿔과 원기둥으로 이루어져 댄서가 됩니다. 엄청나게 파티 중이시네요! 음악이 갑자기 멈추면 모든 것이 바닥으로 떨어집니다.

이 기능은 시청자 입장에서는 좋았지만 해결해야 할 성능 문제가 일부 발생했습니다. 룸 스케일 VR 기기와 고급 게임 장비는 새로운 엔딩에 필요한 40가지의 이상한 추가 성능을 통해 완벽한 성능을 보여주었습니다. 그러나 특정 휴대기기의 프레임 속도는 절반으로 줄었습니다.

이에 대응하기 위해 안개를 도입했습니다. 일정 거리가 지나면 모든 것이 서서히 검은 색이 됩니다. 보이지 않는 것을 계산하거나 그릴 필요가 없으므로 보이지 않는 방의 성능을 추출하여 CPU와 GPU의 작업을 모두 절약할 수 있습니다. 하지만 적절한 거리를 어떻게 판단해야 할까요?

던지는 모든 기기를 처리할 수 있는 기기도 있고 더 엄격한 기기도 있습니다. 우리는 슬라이딩 배율을 구현하기로 했습니다. 초당 프레임 수를 지속적으로 측정하면 안개 거리를 적절하게 조정할 수 있습니다. 프레임 속도가 원활하게 실행되는 한 안개를 제거하여 렌더링 작업을 더 많이 수행합니다. 프레임 속도가 충분히 매끄럽게 실행되지 않으면 안개를 더 가까이 가져와 어둠의 렌더링 성능을 건너뛸 수 있습니다.

// this is called every frame
// the FPS calculation is based on stats.js by @mrdoob
tick: (interval = 3000) => {
    frames++;
    const time = (performance || Date).now();
    if (prevTime == null) prevTime = time;
    if (time > prevTime + interval) {
    fps = Math.round((frames * 1000) / (time - prevTime));
    frames = 0;
    prevTime = time;
    const lastCullDistance = settings.cullDistance;

    // if the fps is lower than 52 reduce the cull distance
    if (fps <= 52) {
        settings.cullDistance = Math.max(
        settings.minCullDistance,
        settings.cullDistance - settings.roomDepth
        );
    }
    // if the FPS is higher than 56, increase the cull distance
    else if (fps > 56) {
        settings.cullDistance = Math.min(
        settings.maxCullDistance,
        settings.cullDistance + settings.roomDepth
        );
    }
    }

    // gradually increase the cull distance to the new setting
    cullDistance = cullDistance * 0.95 + settings.cullDistance * 0.05;

    // mask the edge of the cull distance with fog
    viewer.fog.near = cullDistance - settings.roomDepth;
    viewer.fog.far = cullDistance;
}

모두를 위한 제품: 웹용 VR 빌드

다중 플랫폼 비대칭 환경을 설계하고 개발하려면 기기에 따라 각 사용자의 요구사항을 고려해야 합니다. 그리고 디자인을 결정할 때마다 다른 사용자에게 어떤 영향을 미칠지 파악해야 했습니다 어떻게 하면 VR을 보지 않을 때와 마찬가지로 흥미진진한 경험을 할 수 있을까요? 그리고 그 반대의 경우도 마찬가지입니다.

1. 노란 구

그렇다면 룸스케일 VR 사용자가 직접 공연을 펼치게 되지만 모바일 VR 기기 (예: Cardboard, Daydream View, 삼성 Gear) 사용자는 이 프로젝트를 어떻게 경험할 수 있을까요? 이를 위해 환경에 새로운 요소인 노란색 구체를 도입했습니다.

노란 구
노란 구

VR로 프로젝트를 보면 노란색 구체의 관점에서 볼 수 있습니다. 방을 이리저리 돌아다닐 때 댄서들이 존재에 반응합니다. 몸짓을 하고 주변에서 춤추고 등 뒤로 재밌는 동작을 하다가 내 부딪치지 않도록 빠르게 움직입니다. 노란색 구체는 항상 관심의 중심에 있습니다

공연을 녹화하는 동안 노란색 구슬이 음악에 맞춰 방 한가운데를 가로질러 이동하고 순환하게 되기 때문입니다. 공연자는 오브의 위치를 통해 자신의 현재 위치와 루프에 얼마나 남았는지 알 수 있습니다. 이를 통해 성능을 구축하는 데 자연스럽게 집중할 수 있습니다.

2. 다른 관점

VR을 사용하지 않는 사용자는 저희의 가장 큰 시청자층이 될 가능성이 높기 때문에 빼고 싶지 않았습니다. 우리는 가상 현실 환경을 만드는 대신 화면 기반 기기에 나만의 경험을 선사하고 싶었습니다. 우리는 등각적인 관점에서 공연을 보여주겠다는 아이디어가 떠올랐습니다. 이러한 관점은 컴퓨터 게임에서 풍부한 역사를 가지고 있습니다. 1982년에 출시된 우주 슈팅 게임인 잭슨에서 처음 사용되었습니다. VR 사용자는 두꺼운 체형에 반해 등사 원근감을 사용하면 마치 신과 같은 시야를 얻을 수 있습니다. 모델을 살짝 확장하여 인형 하우스의 아름다움을 더했습니다.

3. 그림자: 만들 때까지 가짜로 만드세요.

그 결과 일부 사용자는 등각 관점의 깊이를 보는 데 어려움을 겪고 있음을 알게 되었습니다. 이런 이유로 잭슨은 날아다니는 물체 아래에 동적 그림자를 투사한 최초의 컴퓨터 게임 중 하나이기도 합니다.

그림자

3D로 그림자를 만드는 것은 어려운 것으로 나타났습니다. 특히 휴대전화와 같이 제한된 기기의 경우 그렇습니다. 처음에 우리는 이 두 가지 방법을 포기하기 위해 어려운 결정을 내려야 했습니다. 하지만 Three.js의 작성자에게 물어보고 데모 해커 Mr doob에게 조언을 구한 후 그는 사람들을 속이겠다는 참신한 아이디어를 떠올렸습니다.

떠 있는 각 객체가 빛을 어떻게 가리므로 다양한 모양의 그림자를 드리는지 계산하지 않고 각 객체 아래에 동일한 원형 블러 텍스처 이미지를 그립니다. 애초에 영상이 현실을 흉내 내는 것이 아니기 때문에 몇 가지만 조정하면 아주 쉽게 벗어날 수 있었습니다. 객체가 지면에 가까워지면 텍스처가 어두워지고 작아집니다. 위로 이동하면 텍스처가 더 투명하고 커집니다.

이를 만들기 위해 (알파 투명도 없음) 부드러운 흰색-검은색 그라데이션으로 이 텍스처를 사용했습니다. 머티리얼을 투명으로 설정하고 감산 블렌딩을 사용합니다. 이렇게 하면 겹칠 때 잘 섞일 수 있습니다.

function createShadow() {
    const texture = new THREE.TextureLoader().load(shadowTextureUrl);
    const material = new THREE.MeshLambertMaterial({
        map: texture,
        transparent: true,
        side: THREE.BackSide,
        depthWrite: false,
        blending: THREE.SubtractiveBlending,
    });
    const geometry = new THREE.PlaneBufferGeometry(0.5, 0.5, 1, 1);
    const plane = new THREE.Mesh(geometry, material);
    return plane;
    }

4. 존재감

VR이 없는 방문객은 공연자의 머리를 클릭하여 댄서의 관점에서 사물을 볼 수 있습니다. 이 각도에서 보면 많은 디테일이 잘 드러납니다. 댄서들은 공연을 차근차근 보여주며 서로를 빨리 쳐다봅니다. 구슬이 방 안으로 들어오면 초조하게 방을 바라보는 것을 볼 수 있습니다. 시청자는 이러한 움직임에 영향을 미칠 수는 없지만 몰입감을 놀라울 정도로 잘 전달합니다. Google은 마우스로 조화롭게 제어하는 가상 VR 버전을 사용자에게 제공하는 것보다 이 방법을 선호했습니다.

5. 녹음 파일 공유

20겹의 공연자들이 서로 리액션하는 정교하게 연출된 녹음을 펼치면 얼마나 자랑스러운 일인지 잘 알고 있습니다. 사용자들이 친구들에게 보여주기를 원한다는 것을 알았습니다. 하지만 이 장면의 정지 이미지만으로는 충분한 전달이 어렵습니다 그보다는 사용자가 공연 동영상을 공유할 수 있도록 하고 싶었습니다. GIF는 왜 안 되나요? 애니메이션은 플랫 음영으로, 형식의 제한된 색상 팔레트에 적합합니다.

녹음 파일 공유

브라우저 내에서 애니메이션 GIF를 인코딩할 수 있는 자바스크립트 라이브러리인 GIF.js로 전환했습니다. 프레임 인코딩을 백그라운드에서 별도의 프로세스로 실행할 수 있는 웹 작업자로 오프로드하므로 나란히 작동하는 여러 프로세서를 활용할 수 있습니다.

안타깝게도 애니메이션에 필요한 프레임의 양으로 인해 인코딩 프로세스가 여전히 너무 느렸습니다. GIF는 제한된 색상 팔레트를 사용하여 작은 파일을 만들 수 있습니다. 대부분의 시간이 각 픽셀에 가장 가까운 색상을 찾는 데 소요된다는 것을 발견했습니다. 이 프로세스를 작은 지름길로 해킹하여 10배나 최적화할 수 있었습니다. 픽셀의 색상이 마지막 색상과 같다면 이전과 동일한 팔레트의 색상을 사용하세요.

이제 빠르게 인코딩했지만 결과 GIF 파일의 크기가 너무 큽니다. GIF 형식을 사용하면 처리 메서드를 정의하여 마지막 프레임 위에 각 프레임을 표시하는 방법을 나타낼 수 있습니다. 더 작은 파일을 가져오기 위해 프레임마다 각 픽셀을 업데이트하는 대신 변경된 픽셀만 업데이트합니다. 인코딩 프로세스 속도가 다시 느려지면서 파일 크기는 크게 줄었습니다.

6. 기초: Google Cloud와 Firebase

'사용자 제작 콘텐츠' 사이트의 백엔드는 복잡하고 취약한 경우가 많지만 Google은 Google Cloud와 Firebase를 활용하여 단순하고 강력한 시스템을 개발했습니다. 출연자가 시스템에 새로운 댄스를 업로드하면 Firebase 인증을 통해 익명으로 인증됩니다. 사용자에게 Firebase용 Cloud Storage를 사용하여 녹화 파일을 임시 공간에 업로드할 권한이 부여됩니다. 업로드가 완료되면 클라이언트 머신이 Firebase 토큰을 사용하여 Firebase용 Cloud Functions HTTP 트리거를 호출합니다. 이렇게 하면 제출의 유효성을 검사하고 데이터베이스 레코드를 만들고 기록을 Google Cloud Storage의 공개 디렉터리로 이동하는 서버 프로세스가 트리거됩니다.

단단한 바닥

모든 공개 콘텐츠는 Cloud Storage 버킷에 일련의 플랫 파일로 저장됩니다. 따라서 전 세계에서 데이터에 빠르게 액세스할 수 있으며 높은 트래픽 부하가 어떻게 데이터 가용성에 영향을 미칠지에 대해 걱정할 필요가 없습니다.

Firebase 실시간 데이터베이스 및 Cloud 함수 엔드포인트를 사용하여 VR에서 각각의 새 제출물을 시청하고 어느 기기에서나 새 재생목록을 게시할 수 있는 간단한 검토/선별 도구를 빌드했습니다.

7. 서비스 워커

서비스 워커는 최근에 출시된 혁신 기능으로 웹사이트 애셋 캐싱을 관리하는 데 도움이 됩니다. 우리의 사례에서 서비스 워커는 재방문자에게 콘텐츠를 매우 빠르게 로드하며, 심지어는 사이트를 오프라인으로도 사용할 수 있게 해 줍니다. 대부분의 방문자가 다양한 품질의 모바일 연결을 사용하므로 이러한 기능은 매우 중요합니다.

대부분의 까다로운 작업을 자동으로 처리해 주는 편리한 webpack 플러그인 덕분에 서비스 워커를 프로젝트에 쉽게 추가할 수 있었습니다. 아래 구성에서는 모든 정적 파일을 자동으로 캐시하는 서비스 워커를 생성합니다. 재생목록이 항상 업데이트되므로 가능한 경우 최신 재생목록 파일을 네트워크에서 가져옵니다. 모든 기록 JSON 파일은 변경되지 않으므로 가능한 경우 캐시에서 가져와야 합니다.

const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
config.plugins.push(
    new SWPrecacheWebpackPlugin({
    dontCacheBustUrlsMatching: /\.\w{8}\./,
    filename: 'service-worker.js',
    minify: true,
    navigateFallback: 'index.html',
    staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
    runtimeCaching: [{
        urlPattern: /playlist\.json$/,
        handler: 'networkFirst',
    }, {
        urlPattern: /\/recordings\//,
        handler: 'cacheFirst',
        options: {
        cache: {
            maxEntries: 120,
            name: 'recordings',
        },
        },
    }],
    })
);

현재 플러그인은 음악 파일과 같이 점진적으로 로드되는 미디어 애셋을 처리하지 않으므로 이러한 파일의 Cloud Storage Cache-Control 헤더를 public, max-age=31536000로 설정하여 브라우저에서 최대 1년 동안 파일을 캐시하도록 했습니다.

결론

공연자들이 이러한 환경을 어떻게 더 추가하고 모션을 사용한 창의적인 표현의 도구로 사용할 수 있을지 무척 기대됩니다. 모든 코드 오픈소스를 공개했으며 https://github.com/puckey/dance-tonite에서 확인할 수 있습니다. 이러한 VR, 특히 WebVR의 초창기에는 이 새로운 매체가 어떤 새로운 창의적이고 예상치 못한 방향으로 나아가게 될지 무척 기대됩니다. 댄스 켜 줘.