Thực tế ảo xuất hiện trên web

Một vài kiến thức cơ bản để chuẩn bị cho bạn trải nghiệm sống động: thực tế ảo, thực tế tăng cường và mọi thứ ở giữa.

Joe Medley
Joe Medley

Trải nghiệm sống động đã có mặt trên web trong Chrome 79. WebXR Device API mang đến trải nghiệm thực tế ảo, trong khi tính năng hỗ trợ thực tế tăng cường có trong Chrome 81. Trong khi bản cập nhật cho GamePad API mở rộng việc sử dụng nâng cao các chế độ điều khiển cho VR. Các trình duyệt khác sẽ sớm hỗ trợ những thông số kỹ thuật này, bao gồm Firefox Reality, Oculus Browser, Edge và trình duyệt Helio của Magic Leap, cùng nhiều trình duyệt khác.

Bài viết này bắt đầu một loạt bài viết về web sống động. Phần này đề cập đến việc thiết lập một ứng dụng WebXR cơ bản cũng như cách vào và thoát khỏi phiên XR. Các bài viết sau này sẽ đề cập đến vòng lặp khung hình (trụ cột của trải nghiệm WebXR), các thông số cụ thể của thực tế tăng cường và WebXR Hit Test API, một phương tiện để phát hiện các bề mặt trong phiên AR. Trừ phi có quy định khác, mọi thứ tôi đề cập trong bài viết này và các bài viết tiếp theo đều áp dụng như nhau cho cả AR và VR.

Web sống động là gì?

Mặc dù chúng ta sử dụng 2 thuật ngữ để mô tả trải nghiệm sống động – thực tế tăng cường và thực tế ảo – nhưng nhiều người nghĩ về chúng trên một phổ từ thực tế hoàn chỉnh đến hoàn toàn ảo, với các mức độ sống động ở giữa. Chữ "X" trong XR nhằm phản ánh suy nghĩ đó bằng cách là một loại biến đại số đại diện cho bất kỳ thứ gì trong phổ trải nghiệm sống động.

Biểu đồ minh hoạ phổ trải nghiệm thị giác từ thực tế hoàn chỉnh đến hoàn toàn sống động.
Phổ trải nghiệm sống động

Ví dụ về trải nghiệm sống động bao gồm:

  • Trò chơi
  • Video 360°
  • Video 2D (hoặc 3D) truyền thống được trình bày trong môi trường sống động
  • Mua nhà
  • Xem sản phẩm trong nhà trước khi mua
  • Nghệ thuật sống động
  • Một điều gì đó thú vị mà chưa ai nghĩ đến

Khái niệm và cách sử dụng

Tôi sẽ giải thích một vài kiến thức cơ bản về cách sử dụng WebXR Device API. Nếu bạn cần thông tin chi tiết hơn những gì tôi đã cung cấp, hãy xem các mẫu WebXR của Nhóm công tác về web sống động hoặc tài liệu đối chiếu ngày càng tăng của MDN. Nếu đã quen thuộc với các phiên bản đầu của WebXR Device API, bạn nên xem qua tất cả tài liệu này. Đã có những thay đổi.

Mã trong bài viết này dựa trên mẫu cơ bản của Nhóm công tác về web sống động (bản minh hoạ, nguồn), nhưng được chỉnh sửa để cho rõ ràng và đơn giản.

Một phần của việc tạo đặc tả WebXR là bổ sung các biện pháp bảo mật và quyền riêng tư để bảo vệ người dùng. Do đó, việc triển khai phải tuân thủ một số yêu cầu nhất định. Trang web hoặc ứng dụng phải đang hoạt động và được chọn thì mới có thể yêu cầu bất kỳ thông tin nhạy cảm nào từ người xem. Các trang web hoặc ứng dụng phải được phân phát qua HTTPS. Bản thân API được thiết kế để bảo vệ thông tin thu được từ các cảm biến và máy ảnh, những thông tin cần thiết để hoạt động.

Yêu cầu một phiên

Để vào phiên XR, người dùng phải thực hiện một cử chỉ. Để thực hiện việc đó, hãy sử dụng tính năng phát hiện để kiểm thử XRSystem (thông qua navigator.xr) và thực hiện lệnh gọi đến XRSystem.isSessionSupported(). Xin lưu ý rằng trong Chrome phiên bản 79 và 80, đối tượng XRSystem được gọi là XR.

Trong ví dụ bên dưới, tôi đã chỉ ra rằng tôi muốn có một phiên thực tế ảo với loại phiên 'immersive-vr'. Các loại phiên khác'immersive-ar''inline'. Phiên nội tuyến dùng để trình bày nội dung trong HTML và chủ yếu được dùng cho nội dung giới thiệu. Mẫu Phiên AR sống động minh hoạ điều này. Tôi sẽ giải thích điều đó trong một bài viết sau.

Sau khi biết rằng các phiên thực tế ảo được hỗ trợ, tôi sẽ bật một nút cho phép tôi thu thập cử chỉ của người dùng.

if (navigator.xr) {
  const supported = await navigator.xr.isSessionSupported('immersive-vr');
  if (supported) {
    xrButton.addEventListener('click', onButtonClicked);
    xrButton.textContent = 'Enter VR';
    xrButton.enabled = supported; // supported is Boolean
  }
}

Sau khi bật nút, tôi sẽ đợi sự kiện nhấp chuột rồi yêu cầu một phiên.

let xrSession = null;
function onButtonClicked() {
  if (!xrSession) {
    navigator.xr.requestSession('immersive-vr')
    .then((session) => {
      xrSession = session;
      xrButton.textContent = 'Exit XR';
      onSessionStarted(xrSession);
    });
  } else {
    xrSession.end();
  }
}

Hãy chú ý đến hệ phân cấp đối tượng trong mã này. Hệ phân cấp này di chuyển từ navigator sang xr đến một thực thể XRSession. Trong các phiên bản đầu của API, một tập lệnh phải yêu cầu một thiết bị trước khi yêu cầu một phiên. Giờ đây, thiết bị được thu thập một cách ngầm ẩn.

Vào một phiên

Sau khi nhận được một phiên, tôi cần bắt đầu và vào phiên đó. Nhưng trước tiên, tôi cần thiết lập một vài thứ. Một phiên cần có trình xử lý sự kiện onend để ứng dụng hoặc trang web có thể được đặt lại khi người dùng thoát.

Tôi cũng cần một phần tử <canvas> để vẽ cảnh của mình. Phần tử này phải là WebGLRenderingContext hoặc WebGL2RenderingContexttương thích với XR. Tất cả hoạt động vẽ đều được thực hiện bằng cách sử dụng các phần tử này hoặc một khung dựa trên WebGL như Three.js.

Giờ đây, tôi đã có một nơi để vẽ, tôi cần một nguồn nội dung để vẽ trên đó. Để thực hiện việc đó, tôi tạo một thực thể của XRWebGLLayer. Tôi liên kết thực thể này với canvas bằng cách gọi XRSession.updateRenderState().

Sau khi vào một phiên, tôi cần một cách để xác định vị trí của mọi thứ trong thực tế ảo. Tôi sẽ cần một không gian tham chiếu. Không gian tham chiếu 'local-floor' là không gian mà nguồn gốc nằm gần người xem và trục y là 0 ở cấp sàn và không được mong đợi di chuyển. Có nhiều loại không gian tham chiếu khác, nhưng đó là một chủ đề phức tạp hơn tôi có thể đề cập ở đây. Tôi lưu không gian tham chiếu vào một biến vì tôi sẽ cần biến đó khi vẽ vào màn hình.

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  let canvas = document.createElement('canvas');
  webGLRenContext = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(xrSession, webGLRenContext)
  });

  xrSession.requestReferenceSpace('local-floor')
  .then((refSpace) => {
    xrRefSpace = refSpace;
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

Sau khi nhận được không gian tham chiếu, tôi sẽ gọi XRSession.requestAnimationFrame(). Đây là điểm bắt đầu của việc trình bày nội dung ảo, được thực hiện trong vòng lặp khung hình.

Chạy vòng lặp khung hình

Vòng lặp khung hình là một vòng lặp vô hạn do tác nhân người dùng kiểm soát, trong đó nội dung được vẽ lặp đi lặp lại vào màn hình. Nội dung được vẽ trong các khối rời rạc gọi là khung hình. Sự kế tiếp của các khung hình tạo ra ảo ảnh chuyển động. Đối với các ứng dụng VR, số khung hình/giây có thể từ 60 đến 144. AR cho Android chạy ở tốc độ 30 khung hình/giây. Mã của bạn không được giả định bất kỳ tốc độ khung hình cụ thể nào.

Quy trình cơ bản cho vòng lặp khung hình là:

  1. Gọi XRSession.requestAnimationFrame(). Để phản hồi, tác nhân người dùng sẽ gọi XRFrameRequestCallback do bạn xác định.
  2. Bên trong hàm callback:
    1. Gọi lại XRSession.requestAnimationFrame().
    2. Lấy tư thế của người xem.
    3. Truyền ("liên kết") WebGLFramebuffer từ XRWebGLLayer đến WebGLRenderingContext.
    4. Lặp lại từng đối tượng XRView, truy xuất XRViewport của đối tượng đó từ XRWebGLLayer và truyền đối tượng đó đến WebGLRenderingContext.
    5. Vẽ nội dung nào đó vào bộ đệm khung.

Phần còn lại của bài viết này mô tả bước 1 và một phần của bước 2, thiết lập và gọi XRFrameRequestCallback. Các mục còn lại của bước 2 được đề cập trong phần II.

XRFrameRequestCallback

XRFrameRequestCallback do bạn xác định. Hàm này nhận 2 tham số: DOMHighResTimeStamp và thực thể XRFrame. Đối tượng XRFrame cung cấp thông tin cần thiết để kết xuất một khung hình duy nhất vào màn hình. Đối số DOMHighResTimeStamp dùng cho mục đích sử dụng trong tương lai.

Trước khi làm bất cứ điều gì khác, tôi sẽ yêu cầu khung hình ảnh động tiếp theo. Như đã nêu trước đó, thời gian của khung hình được xác định bởi tác nhân người dùng dựa trên phần cứng cơ bản. Việc yêu cầu khung hình tiếp theo trước tiên đảm bảo rằng vòng lặp khung hình tiếp tục nếu có lỗi xảy ra trong quá trình gọi lại.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  // Render a frame.
}

Tại thời điểm này, đã đến lúc vẽ nội dung nào đó cho người xem. Đó là nội dung thảo luận cho phần II. Trước khi chuyển sang phần đó, hãy để tôi chỉ cho bạn cách kết thúc một phiên.

Kết thúc phiên

Một phiên sống động có thể kết thúc vì nhiều lý do, bao gồm cả việc kết thúc bằng mã của riêng bạn thông qua lệnh gọi đến XRSession.end(). Các nguyên nhân khác bao gồm việc tai nghe bị ngắt kết nối hoặc một ứng dụng khác kiểm soát tai nghe đó. Đây là lý do tại sao một ứng dụng hoạt động tốt nên theo dõi sự kiện end. Khi sự kiện này xảy ra, hãy loại bỏ phiên và các đối tượng kết xuất liên quan. Không thể tiếp tục phiên sống động đã kết thúc. Để vào lại trải nghiệm sống động, ứng dụng của tôi cần bắt đầu một phiên mới.

Hãy nhớ lại từ phần Vào một phiên rằng trong quá trình thiết lập, tôi đã thêm trình xử lý sự kiện onend.

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);
  // More setup…
}

Bên trong trình xử lý sự kiện, hãy khôi phục trạng thái của ứng dụng trước khi người dùng vào một phiên.

function onSessionEnded(event) {
  xrSession = null;
  xrButton.textContent = 'Enter VR';
}

Kết luận

Tôi chưa giải thích mọi thứ bạn cần để viết ứng dụng Web XR hoặc AR. Hy vọng rằng tôi đã cung cấp cho bạn đủ thông tin để tự mình hiểu được mã và đủ để bắt đầu thử nghiệm. Trong bài viết tiếp theo, tôi sẽ giải thích về vòng lặp khung hình, nơi nội dung được vẽ vào màn hình.