Dance Tonite trong WebVR

Tôi rất vui khi nhóm Google Data Arts đã tiếp cận Moniker và bản thân tôi về việc làm việc cùng nhau để khám phá những khả năng mà WebVR mang đến. Tôi đã theo dõi công việc mà nhóm của họ đã làm được trong những năm qua và các dự án của họ luôn khiến tôi cảm thấy ấn tượng. Sự cộng tác của chúng tôi đã tạo nên Dance Tonite, trải nghiệm nhảy thực tế ảo không ngừng thay đổi cùng LCD Soundsystem và người hâm mộ của họ. Sau đây là cách chúng tôi thực hiện.

Khái niệm

Chúng tôi đã bắt đầu bằng việc phát triển một loạt nguyên mẫu sử dụng WebVR, một tiêu chuẩn mở cho phép người dùng truy cập vào thực tế ảo bằng cách sử dụng trình duyệt của mình để truy cập vào một trang web. Mục tiêu là giúp mọi người dễ dàng trải nghiệm thực tế ảo hơn, bất kể bạn có thiết bị nào.

Chúng tôi ghi nhớ điều này. Bất cứ thứ gì chúng tôi nghĩ ra đều phải phù hợp với mọi loại thực tế ảo, từ tai nghe thực tế ảo hoạt động với điện thoại di động như Daydream View của Google, Cardboard và Gear VR của Samsung cho đến các hệ thống quy mô phòng như HTC VIVE và Oculus Rift để phản ánh chuyển động vật lý của bạn trong môi trường ảo. Có lẽ điều quan trọng nhất là chúng tôi cảm thấy rằng dựa trên tinh thần của web, chúng tôi sẽ tạo ra thứ gì đó cũng phù hợp với tất cả những người không sở hữu thiết bị thực tế ảo.

1. Tự quay cảnh chuyển động

Vì muốn thu hút người dùng một cách sáng tạo, chúng tôi đã bắt đầu xem xét các khả năng tham gia và thể hiện bản thân bằng thực tế ảo. Chúng tôi bị ấn tượng bởi khả năng di chuyển và quan sát xung quanh tinh tế trong thực tế ảo cũng như độ chân thực ở đó. Việc này giúp chúng tôi nghĩ ra ý tưởng. Thay vì để người dùng xem hoặc tạo một thứ gì đó, hãy ghi lại chuyển động của họ?

Một người tự ghi lại cảnh mình trên Dance Tonite. Màn hình phía sau họ
  cho thấy những gì họ đang thấy trong tai nghe

Chúng tôi đã tạo ra một nguyên mẫu trong đó chúng tôi ghi lại vị trí của kính thực tế ảo và bộ điều khiển trong khi nhảy. Chúng tôi đã thay thế các vị trí được ghi lại bằng các hình dạng trừu tượng và ngạc nhiên trước kết quả. Kết quả rất con người và chứa rất nhiều tính cách! Chúng tôi nhanh chóng nhận ra rằng có thể sử dụng WebVR để chụp ảnh chuyển động giá rẻ tại nhà.

Với WebVR, nhà phát triển có thể truy cập vào vị trí và hướng đầu của người dùng thông qua đối tượng VRPose. Giá trị này được phần cứng thực tế ảo cập nhật mọi khung hình để mã của bạn có thể kết xuất các khung hình mới từ đúng góc nhìn. Thông qua API GamePad với WebVR, chúng ta cũng có thể truy cập vào vị trí/hướng của bộ điều khiển người dùng thông qua đối tượng GamepadPose. Chúng tôi chỉ lưu trữ tất cả các giá trị vị trí và hướng này trong mọi khung hình, từ đó tạo một "bản ghi" chuyển động của người dùng.

2. Trang phục và chủ nghĩa tối giản

Với thiết bị thực tế ảo (VR) quy mô phòng ngày nay, chúng ta có thể theo dõi 3 điểm trên cơ thể của người dùng: đầu và 2 tay. Trong Dance Tonite, chúng tôi muốn tập trung vào tính nhân văn trong chuyển động của 3 điểm này trong không gian. Để đạt được điều này, chúng tôi đã cố gắng tối đa hoá tính thẩm mỹ nhằm tập trung vào chuyển động. Chúng tôi thích ý tưởng giúp bộ não của mọi người làm việc.

Video này minh hoạ công trình của nhà tâm lý học người Thuỵ Điển Gunnar Johansson là một trong những ví dụ mà chúng tôi đề cập đến khi cân nhắc việc lược bớt mọi thứ càng đơn giản càng tốt. Hình ảnh này cho thấy các chấm trắng nổi có thể được nhận dạng ngay lập tức dưới dạng cơ thể khi nhìn thấy chuyển động.

Về mặt trực quan, chúng tôi được lấy cảm hứng từ những căn phòng màu sắc và trang phục hình học trong video ghi lại bộ sưu tập Ba lê Bộ ba của Oskar Schlemmer vào năm 1970.

Trong khi lý do Schlemmer chọn trang phục hình học trừu tượng là để giới hạn chuyển động của vũ công chỉ dùng con rối và con rối, nhưng chúng tôi có mục tiêu ngược lại là Dance Tonite.

Cuối cùng, chúng tôi đã căn cứ vào lựa chọn hình dạng dựa trên lượng thông tin các hình dạng đó được truyền tải khi xoay. Một quả cầu trông như nhau bất kể xoay như thế nào, nhưng một hình nón thực sự trỏ theo hướng nhìn và trông khác với mặt trước so với mặt sau.

3. Bàn đạp tròn để chuyển động

Chúng tôi muốn cho khán giả xem nhiều người được ghi hình đang nhảy múa và di chuyển cùng nhau. Sẽ không khả thi để cung cấp tính năng phát trực tiếp vì số lượng thiết bị thực tế ảo không đủ lớn. Nhưng chúng tôi vẫn muốn có nhiều nhóm người thể hiện phản ứng với nhau thông qua chuyển động. Chúng tôi chuyển sang phần trình diễn định kỳ của Norman McClaren trong tác phẩm video "Canon" ra mắt năm 1964 của ông.

Hiệu suất của McClaren có một loạt các chuyển động được vũ đạo cao bắt đầu tương tác với nhau sau mỗi vòng lặp. Giống như một vòng lặp trong âm nhạc, nơi các nhạc sĩ ngẫu hứng bằng cách xếp lớp nhiều đoạn nhạc sống, chúng tôi muốn xem liệu mình có thể tạo ra một môi trường nơi người dùng có thể tuỳ ý ứng biến các phiên bản biểu diễn đơn giản hơn hay không.

4. Các phòng thông nhau

Các phòng thông nhau

Giống như nhiều loại nhạc khác, các bản nhạc của Hệ thống âm thanh LCD được thiết kế bằng cách sử dụng các biện pháp đo thời gian chính xác. Bản nhạc của họ, Tonite, xuất hiện trong dự án của chúng tôi, có các thông tin đo lường chính xác là 8 giây. Chúng tôi muốn người dùng thực hiện hiệu suất cho mỗi vòng lặp 8 giây trong bản nhạc. Mặc dù nhịp điệu của các biện pháp này không thay đổi, nhưng nội dung âm nhạc của chúng thì không. Khi bài hát phát, có những khoảnh khắc sử dụng các nhạc cụ và giọng hát khác nhau mà người biểu diễn có thể phản ứng theo nhiều cách. Mỗi chỉ số trong số này được biểu thị dưới dạng một phòng, trong đó người dùng có thể tạo ra hiệu suất phù hợp.

Tối ưu hoá hiệu suất: không làm giảm khung hình

Việc tạo ra trải nghiệm thực tế ảo đa nền tảng chạy trên một cơ sở mã duy nhất với hiệu suất tối ưu cho từng thiết bị hoặc nền tảng không phải là một công việc đơn giản.

Khi ở chế độ thực tế ảo, một trong những trải nghiệm gây khó chịu nhất của bạn là do tốc độ khung hình không bắt kịp chuyển động của bạn. Nếu bạn quay đầu nhưng hình ảnh mà mắt bạn nhìn thấy không khớp với chuyển động mà tai trong cảm thấy, thì tình trạng này sẽ gây ra tình trạng đau bụng tức thì. Vì lý do này, chúng tôi cần tránh mọi độ trễ tốc độ khung hình lớn. Dưới đây là một số biện pháp tối ưu hoá mà chúng tôi đã triển khai.

1. Hình học vùng đệm thực thể

Vì toàn bộ dự án của chúng tôi chỉ sử dụng một số ít đối tượng 3d, nên chúng tôi đã tăng được hiệu suất đáng kể nhờ sử dụng Instanced Buffer Geometry. Về cơ bản, tính năng này cho phép bạn tải đối tượng lên GPU một lần và vẽ bao nhiêu "thực thể" của đối tượng đó tuỳ thích trong một lệnh gọi vẽ duy nhất. Trong Dance Tonite, chúng ta chỉ có 3 đối tượng khác nhau (một hình nón, một hình trụ và một căn phòng có lỗ), nhưng có thể có hàng trăm bản sao của các đối tượng đó. Instance Buffer Geometry là một phần của ThreeJS, nhưng chúng tôi đã sử dụng fork thử nghiệm và đang tiến hành của Dusan Bosnjak để triển khai THREE.InstanceMesh, giúp làm việc với Instanced Buffer Geometry dễ dàng hơn nhiều.

2. Tránh thiết bị thu gom rác

Cũng như nhiều ngôn ngữ tập lệnh khác, JavaScript tự động giải phóng bộ nhớ bằng cách tìm ra đối tượng được phân bổ không còn được sử dụng nữa. Quá trình này được gọi là thu gom rác.

Nhà phát triển không có quyền kiểm soát thời điểm việc này xảy ra. Trình thu gom rác có thể xuất hiện trước cửa bất cứ lúc nào và bắt đầu dọn sạch rác, dẫn đến tình trạng rớt khung hình khi chúng tốn thời gian.

Giải pháp cho vấn đề này là tạo ra ít rác nhất có thể bằng cách tái chế các đối tượng. Thay vì tạo một đối tượng vectơ mới cho mỗi phép tính, chúng tôi đã đánh dấu các đối tượng vectơ để sử dụng lại. Vì chúng tôi giữ lại bằng cách di chuyển tham chiếu đến chúng ra ngoài phạm vi của mình, nên chúng không được đánh dấu để xóa.

Ví dụ: đây là mã để chuyển đổi ma trận vị trí của đầu và tay người dùng thành mảng giá trị vị trí/xoay mà chúng ta lưu trữ từng khung. Bằng cách sử dụng lại SERIALIZE_POSITION, SERIALIZE_ROTATIONSERIALIZE_SCALE, chúng ta sẽ tránh được việc phân bổ bộ nhớ và thu gom rác sẽ xảy ra nếu tạo đối tượng mới mỗi khi hàm này được gọi.

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. Chuyển động và phát liên tục theo tuần tự

Để ghi lại chuyển động của người dùng trong VR, chúng tôi cần chuyển đổi tuần tự vị trí và xoay tai nghe cũng như bộ điều khiển của họ, đồng thời tải dữ liệu này lên máy chủ của chúng tôi. Chúng tôi bắt đầu thu thập các ma trận biến đổi đầy đủ cho từng khung hình. Cách tính này hoạt động hiệu quả, nhưng với 16 số nhân với 3 vị trí mỗi số ở tốc độ 90 khung hình/giây, điều này dẫn đến các tệp rất lớn và do đó phải đợi lâu trong khi tải lên và tải dữ liệu xuống. Bằng cách chỉ trích xuất dữ liệu vị trí và xoay từ các ma trận biến đổi, chúng tôi đã có thể giảm các giá trị này từ 16 xuống 7.

Vì khách truy cập trên web thường nhấp vào một đường liên kết mà không biết chính xác nội dung mong đợi, nên chúng tôi cần hiển thị nội dung hình ảnh nhanh chóng, nếu không họ sẽ rời đi trong vòng vài giây.

Vì lý do này, chúng tôi muốn đảm bảo dự án của mình có thể bắt đầu phát càng sớm càng tốt. Ban đầu, chúng tôi sử dụng JSON làm định dạng để tải dữ liệu chuyển động. Vấn đề là chúng ta phải tải toàn bộ tệp JSON rồi mới có thể phân tích cú pháp tệp đó. Không tiến bộ lắm.

Để một dự án như Dance Tonite hiển thị ở tốc độ khung hình cao nhất có thể, trình duyệt chỉ dành một khoảng thời gian nhỏ cho mỗi khung hình để tính toán JavaScript. Nếu bạn mất quá nhiều thời gian, các ảnh động sẽ bắt đầu bị giật. Ban đầu, chúng tôi gặp phải vấn đề gián đoạn khi các tệp JSON khổng lồ này đã được trình duyệt giải mã.

Chúng tôi đã tìm thấy một định dạng dữ liệu truyền trực tuyến tiện lợi có tên là NDJSON hoặc JSON được phân tách bằng Newline. Mẹo ở đây là tạo một tệp có một chuỗi JSON hợp lệ, mỗi chuỗi trên một dòng riêng. Việc này cho phép bạn phân tích cú pháp tệp trong khi tải, cho phép chúng tôi hiển thị các hiệu suất trước khi tải đầy đủ.

Dưới đây là giao diện của một phần của một trong các bản ghi của chúng tôi:

{"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, ... ]
...

Việc sử dụng NDJSON cho phép chúng ta giữ lại biểu diễn dữ liệu của các khung riêng lẻ của hiệu suất dưới dạng chuỗi. Chúng tôi có thể đợi cho đến khi đạt đến thời gian cần thiết, trước khi giải mã chúng thành dữ liệu vị trí, từ đó mở rộng quá trình xử lý cần thiết theo thời gian.

4. Chuyển động nội suy

Vì chúng tôi hy vọng sẽ hiển thị từ 30 đến 60 hiệu suất chạy cùng lúc, nên chúng tôi cần giảm tốc độ dữ liệu xuống hơn nữa so với trước đây. Nhóm Data Arts (Nghệ thuật dữ liệu) đã xử lý vấn đề tương tự trong dự án Virtual Art Session (Phiên nghệ thuật ảo), trong đó họ phát lại bản ghi hình các nghệ sĩ vẽ tranh trong thực tế ảo bằng Tilt Brush. Họ đã giải quyết vấn đề này bằng cách tạo các phiên bản trung gian của dữ liệu người dùng có tốc độ khung hình thấp hơn và nội suy giữa các khung hình trong khi phát lại. Chúng tôi rất ngạc nhiên khi nhận thấy khó có thể nhận ra sự khác biệt giữa bản ghi nội suy chạy ở 15 FPS so với bản ghi 90 FPS ban đầu.

Để tự xem, bạn có thể buộc Dance Tonite phát lại dữ liệu ở nhiều tốc độ bằng cách sử dụng chuỗi truy vấn ?dataRate=. Bạn có thể sử dụng tính năng này để so sánh chuyển động đã ghi ở tốc độ 90 khung hình/giây, 45 khung hình/giây hoặc 15 khung hình/giây.

Đối với vị trí, chúng tôi thực hiện nội suy tuyến tính giữa khung hình chính trước đó và khung hình tiếp theo, dựa trên khoảng cách giữa các khung hình chính (tỷ lệ):

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
    );

Đối với hướng, chúng tôi thực hiện một phép nội suy tuyến tính hình cầu (slerp) giữa các khung chính. Hướng được lưu trữ dưới dạng Quaternions.

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

5. Đồng bộ hoá chuyển động với nhạc

Để biết khung hình nào của ảnh động đã ghi để phát, chúng ta cần biết thời gian hiện tại của bản nhạc là từng mili giây. Hoá ra mặc dù phần tử HTML Audio là lựa chọn hoàn hảo để tải dần và phát lại âm thanh, nhưng thuộc tính thời gian mà phần tử này cung cấp không thay đổi khi đồng bộ hoá với vòng lặp khung của trình duyệt. Luôn có một chút sai lệch. Đôi khi, một phần nhỏ của mili giây là quá sớm, đôi khi là một phần quá muộn.

Điều này dẫn đến tình trạng gián đoạn trong các bản ghi âm vũ đạo tuyệt đẹp của chúng tôi. Điều này chúng tôi muốn tránh bằng mọi giá. Để khắc phục vấn đề này, chúng tôi đã triển khai bộ tính giờ riêng trong JavaScript. Bằng cách này, chúng ta có thể chắc chắn rằng khoảng thời gian thay đổi giữa các khung hình chính xác là khoảng thời gian đã trôi qua kể từ khung hình cuối cùng. Bất cứ khi nào bộ tính giờ của chúng tôi mất hơn 10 mili giây không đồng bộ với nhạc, chúng tôi sẽ đồng bộ hoá lại.

6. Mưa dông và sương mù

Mỗi câu chuyện đều cần có một kết thúc hay và chúng tôi muốn làm một điều gì đó bất ngờ cho người dùng trong trải nghiệm của chúng tôi. Khi rời khỏi căn phòng cuối cùng, bạn sẽ bước vào một không gian yên tĩnh với hình nón và hình trụ. Bạn tự hỏi: “Đây có phải là kết thúc không?”, bạn tự hỏi. Khi bạn tiến xa hơn vào sân khấu, đột nhiên âm thanh của âm nhạc khiến các nhóm hình nón và hình trụ khác nhau tạo thành các vũ công. Bạn sẽ thấy mình đang ở giữa một bữa tiệc lớn! Rồi khi nhạc dừng đột ngột, mọi thứ rơi xuống đất.

Mặc dù mang lại cảm giác tuyệt vời cho người xem, nhưng nó cũng góp phần giải quyết một số rào cản về hiệu suất. Các thiết bị thực tế ảo (VR) trên quy mô phòng và thiết bị chơi trò chơi cao cấp của chúng đã hoạt động hoàn hảo với 40 hiệu suất bổ sung cần thiết cho phần kết thúc mới của chúng tôi. Tuy nhiên, tốc độ khung hình trên một số thiết bị di động nhất định đã giảm một nửa.

Để chống lại điều này, chúng tôi đã sử dụng sương mù. Sau một khoảng cách nhất định, mọi thứ từ từ sẽ chuyển sang màu đen. Vì không cần tính toán hoặc vẽ những nội dung không hiển thị, nên chúng tôi sẽ loại bỏ hiệu suất trong các phòng không nhìn thấy. Điều này cho phép chúng tôi tiết kiệm công việc cho cả CPU và GPU. Nhưng làm thế nào để quyết định khoảng cách phù hợp?

Một số thiết bị có thể xử lý mọi thao tác bạn ném vào chúng, trong khi một số thiết bị khác thì hạn chế hơn. Chúng tôi chọn triển khai thang trượt. Bằng cách liên tục đo lường số lượng khung hình/giây, chúng ta có thể điều chỉnh khoảng cách của lớp sương mù cho phù hợp. Miễn là tốc độ khung hình chạy mượt mà, chúng tôi sẽ cố gắng tăng cường hoạt động kết xuất bằng cách đẩy sương mù vào. Nếu tốc độ khung hình không chạy đủ mượt, chúng ta sẽ đưa sương mù đến gần hơn để bỏ qua hiệu suất kết xuất trong bóng tối.

// 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;
}

Một cái gì đó cho mọi người: xây dựng VR cho web

Việc thiết kế và phát triển trải nghiệm đa nền tảng, bất đối xứng đồng nghĩa với việc tính đến nhu cầu của mỗi người dùng dựa trên thiết bị của họ. Với mỗi quyết định thiết kế, chúng tôi cần xem điều đó có thể ảnh hưởng đến những người dùng khác như thế nào. Làm thế nào để bạn đảm bảo những gì bạn nhìn thấy trong thực tế ảo cũng thú vị như khi không có thực tế ảo và ngược lại?

1. Quả cầu màu vàng

Vì vậy, người dùng thực tế ảo trên quy mô phòng của chúng tôi sẽ biểu diễn, nhưng người dùng các thiết bị thực tế ảo di động (như Cardboard, Daydream View hoặc Samsung Gear) sẽ trải nghiệm dự án này như thế nào? Để làm được điều này, chúng tôi đã giới thiệu một yếu tố mới cho môi trường của mình: quả cầu màu vàng.

Quả cầu màu vàng
Quả cầu màu vàng

Khi xem dự án ở chế độ thực tế ảo (VR), bạn đang làm như vậy từ quan điểm của quả cầu màu vàng. Khi bạn di chuyển từ phòng này sang phòng khác, các vũ công sẽ phản ứng với sự hiện diện của bạn. Chúng cử chỉ với bạn, nhảy múa xung quanh bạn, thực hiện các chuyển động vui nhộn sau lưng và di chuyển nhanh ra xa để không chạm vào bạn. Quả cầu màu vàng luôn là tâm điểm của sự chú ý.

Lý do là trong khi ghi lại một màn trình diễn, quả cầu màu vàng sẽ di chuyển qua phần trung tâm của căn phòng đồng bộ với nhạc và vòng tròn trở lại. Vị trí của quả cầu giúp người biểu diễn biết họ đang ở đâu trong thời gian và bao lâu họ còn lại trong vòng lặp. Nó giúp họ tập trung một cách tự nhiên để xây dựng hiệu suất.

2. Góc nhìn khác

Chúng tôi không muốn bỏ lỡ những người dùng không có thực tế ảo (VR), đặc biệt là khi họ có thể là đối tượng lớn nhất của chúng tôi. Thay vì tạo ra trải nghiệm thực tế ảo giả, chúng tôi muốn mang đến cho các thiết bị dựa trên màn hình trải nghiệm rất riêng. Chúng tôi có ý tưởng thể hiện hiệu suất từ trên xuống từ góc độ đẳng cự. Quan điểm này có lịch sử lâu đời trong trò chơi máy tính. Trò chơi này lần đầu tiên được sử dụng trong Zaxxon, một trò chơi bắn súng ngoài không gian từ năm 1982. Trong khi người dùng thực tế ảo (VR) trải nghiệm rất nhiều, thì phối cảnh đẳng cự sẽ mang đến một góc nhìn tựa như thần. Chúng tôi chọn mở rộng các mô hình một chút, mang lại tính thẩm mỹ của nhà búp bê.

3. Bóng: giả mạo cho đến khi bạn thực hiện

Chúng tôi nhận thấy rằng một số người dùng gặp khó khăn khi xem chiều sâu trong quan điểm đẳng áp của chúng tôi. Tôi khá chắc chắn vì lý do này, Zaxxon cũng là một trong những trò chơi máy tính đầu tiên trong lịch sử chiếu bóng động bên dưới các vật thể bay của nó.

Bóng

Hoá ra việc tạo bóng trong 3D rất khó. Đặc biệt là đối với các thiết bị hạn chế như điện thoại di động. Ban đầu chúng tôi phải đưa ra một quyết định khó khăn là đưa chúng ra khỏi phương trình, nhưng sau khi nhờ tác giả của Three.js và những tin tặc minh hoạ có kinh nghiệm là Mr doob tư vấn, anh ấy đã nảy ra ý tưởng mới là... giả mạo chúng.

Thay vì phải tính toán cách mỗi vật thể nổi che khuất ánh sáng và do đó đổ bóng ở nhiều hình dạng, chúng ta vẽ cùng một hình ảnh hoạ tiết được làm mờ hình tròn bên dưới mỗi vật thể đó. Vì hình ảnh của chúng tôi không bắt chước thực tế ngay từ đầu, nên chúng tôi nhận thấy có thể loại bỏ nó khá dễ dàng chỉ với một vài lần điều chỉnh. Khi các vật thể đến gần mặt đất hơn, chúng ta sẽ thấy hoạ tiết trở nên tối hơn và nhỏ hơn. Khi chúng di chuyển lên, chúng tôi sẽ làm cho các hoạ tiết trở nên trong suốt và lớn hơn.

Để tạo các lớp này, chúng tôi đã sử dụng hoạ tiết này với hiệu ứng chuyển màu từ trắng đến đen nhẹ nhàng (không có độ trong suốt alpha). Chúng ta đặt chất liệu là trong suốt và sử dụng pha trộn trừu tượng. Điều này giúp các hình ảnh này kết hợp đẹp mắt khi chồng chéo lên nhau:

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. Hoạt động ở đó

Bằng cách nhấp vào đầu của nghệ sĩ biểu diễn, khách truy cập không có thực tế ảo (VR) có thể xem mọi thứ từ góc nhìn của vũ công. Từ góc độ này, rất nhiều chi tiết nhỏ trở nên rõ ràng. Cố gắng giữ màn trình diễn theo từng bước, các vũ công nhanh chóng liếc nhìn nhau. Khi quả cầu đi vào phòng, bạn sẽ thấy chúng lo lắng nhìn về hướng. Là người xem, bạn không thể tác động đến những chuyển động này, nhưng điều này giúp truyền tải cảm giác chìm đắm một cách đáng kinh ngạc. Xin nhắc lại rằng chúng tôi ưu tiên làm điều này hơn là trình bày cho người dùng một phiên bản thực tế ảo giả có thể điều khiển bằng chuột.

5. Chia sẻ bản ghi âm

Chúng tôi biết bạn có thể tự hào như thế nào khi thực hiện một bản ghi được biên đạo công phu gồm 20 lớp nghệ sĩ biểu diễn phản ứng với nhau. Chúng tôi biết rằng người dùng của mình có thể sẽ muốn hiển thị ứng dụng cho bạn bè của họ. Tuy nhiên, chỉ một hình ảnh tĩnh của kỳ công này sẽ không đủ thông tin để truyền tải. Thay vào đó, chúng tôi muốn cho phép người dùng chia sẻ video về hiệu suất của họ. Thực ra thì tại sao không phải là ảnh GIF? Ảnh động của chúng ta được tô bóng phẳng, rất phù hợp với bảng màu giới hạn của định dạng.

Chia sẻ bản ghi âm

Chúng tôi đã chuyển sang sử dụng GIF.js, một thư viện JavaScript cho phép bạn mã hoá ảnh gif động từ bên trong trình duyệt. Trình xử lý này giảm tải việc mã hoá khung cho trình thực thi web (có thể chạy trong nền dưới dạng các quy trình riêng biệt), nhờ đó có thể tận dụng việc nhiều bộ xử lý hoạt động song song.

Thật đáng tiếc, với số lượng khung hình chúng tôi cần cho ảnh động, quá trình mã hoá vẫn quá chậm. Ảnh GIF có thể tạo các tệp nhỏ bằng cách sử dụng một bảng màu giới hạn. Chúng tôi nhận thấy rằng hệ thống dành hầu hết thời gian để tìm màu gần nhất cho từng pixel. Chúng tôi có thể tối ưu hoá quá trình này gấp 10 lần bằng cách xâm nhập trong một đoạn cắt ngắn: nếu màu của pixel giống với màu cuối cùng, hãy sử dụng cùng một màu trong bảng màu như trước đây.

Bây giờ, chúng tôi đã mã hoá nhanh nhưng các tệp GIF thu được có kích thước quá lớn. Định dạng GIF cho phép bạn chỉ ra cách hiển thị từng khung hình ở đầu khung hình cuối cùng bằng cách xác định phương thức loại bỏ khung hình đó. Để tải các tệp nhỏ hơn, thay vì cập nhật từng pixel trong mỗi khung hình, chúng tôi chỉ cập nhật các pixel đã thay đổi. Mặc dù một lần nữa, quá trình mã hoá lại chậm lại, nhưng thao tác này đã làm giảm kích thước tệp của chúng tôi rất nhiều.

6. Nền tảng vững chắc: Google Cloud và Firebase

Phần phụ trợ của trang web "nội dung do người dùng tạo" thường có thể phức tạp và dễ vỡ, nhưng chúng tôi đã tạo ra một hệ thống đơn giản và mạnh mẽ nhờ Google Cloud và Firebase. Khi người biểu diễn tải một điệu nhảy mới lên hệ thống, các điệu nhảy đó sẽ được xác thực ẩn danh bằng tính năng Xác thực Firebase. Họ được cấp quyền tải bản ghi của mình lên một không gian tạm thời bằng Cloud Storage cho Firebase. Khi quá trình tải lên hoàn tất, máy khách sẽ gọi trình kích hoạt HTTP Hàm đám mây cho Firebase bằng mã thông báo Firebase. Lệnh này kích hoạt quy trình máy chủ xác thực việc gửi, tạo bản ghi cơ sở dữ liệu và di chuyển bản ghi vào một thư mục công khai trên Google Cloud Storage.

Nền đất cứng

Tất cả nội dung công khai của chúng tôi được lưu trữ trong một loạt tệp phẳng trong Bộ chứa Cloud Storage. Điều này đồng nghĩa với việc dữ liệu của chúng tôi có thể truy cập được trên khắp thế giới một cách nhanh chóng và chúng tôi không cần lo lắng về việc tải lưu lượng truy cập cao ảnh hưởng đến tính sẵn có của dữ liệu dưới bất kỳ hình thức nào.

Chúng tôi đã sử dụng điểm cuối Cơ sở dữ liệu theo thời gian thực Firebase và điểm cuối Chức năng đám mây để xây dựng một công cụ kiểm duyệt/tuyển chọn đơn giản cho phép chúng tôi xem mỗi lần gửi mới trong thực tế ảo và xuất bản danh sách phát mới từ bất kỳ thiết bị nào.

7. Trình chạy dịch vụ

Trình chạy dịch vụ là một cải tiến khá mới giúp quản lý việc lưu các tài sản của trang web vào bộ nhớ đệm. Trong trường hợp của chúng tôi, trình chạy dịch vụ sẽ tải nội dung của chúng tôi nhanh như chớp để khách truy cập cũ và thậm chí cho phép trang web hoạt động ngoại tuyến. Đây là những tính năng quan trọng vì nhiều khách truy cập của chúng tôi sử dụng các kết nối di động có chất lượng khác nhau.

Bạn có thể dễ dàng thêm trình chạy dịch vụ vào dự án nhờ vào một trình bổ trợ gói web tiện dụng giúp xử lý hầu hết các công việc khó. Trong cấu hình bên dưới, chúng tôi tạo một trình chạy dịch vụ sẽ tự động lưu tất cả các tệp tĩnh của chúng tôi vào bộ nhớ đệm. Thao tác này sẽ lấy tệp danh sách phát mới nhất từ mạng (nếu có), vì danh sách phát sẽ luôn cập nhật. Tất cả các tệp json ghi lại sẽ lấy từ bộ nhớ đệm nếu có, vì các tệp này sẽ không bao giờ thay đổi.

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',
        },
        },
    }],
    })
);

Hiện tại, trình bổ trợ này không xử lý các tài sản nghe nhìn được tải dần như tệp nhạc. Vì vậy, chúng tôi đã khắc phục bằng cách đặt tiêu đề Cache-Control trong Cloud Storage trên các tệp này thành public, max-age=31536000 để trình duyệt lưu tệp vào bộ nhớ đệm trong tối đa một năm.

Kết luận

Chúng tôi rất háo hức muốn xem những người biểu diễn sẽ thêm vào trải nghiệm này như thế nào và sử dụng nó làm công cụ để thể hiện sáng tạo bằng chuyển động. Chúng tôi đã phát hành toàn bộ mã nguồn mở mà bạn có thể tìm thấy tại https://github.com/puckey/dance-tonite. Ở những ngày đầu của công nghệ thực tế ảo (VR) và đặc biệt là WebVR, chúng tôi rất mong được thấy những hướng đi mới mẻ và đầy bất ngờ của phương tiện mới này. Bật nhảy.