Virtual Reality มาสู่เว็บ ตอนที่ 2

ข้อมูลทั้งหมดเกี่ยวกับการวนซ้ำของเฟรม

Joe Medley
Joe Medley

เมื่อเร็วๆ นี้ฉันได้เผยแพร่ Virtual Reality บนเว็บ ซึ่งเป็นบทความที่นำเสนอแนวคิดพื้นฐานเบื้องหลัง WebXR Device API และยังได้บอกวิธีการขอ เข้า และจบเซสชัน XR ด้วย

บทความนี้อธิบายการวนซ้ำของเฟรม ซึ่งเป็นลูปแบบไม่สิ้นสุดที่มีการควบคุมของ User Agent ซึ่งระบบจะแสดงเนื้อหาไปยังหน้าจอซ้ำๆ เนื้อหาจะถูกดึงออกเป็นบล็อกต่างๆ แยกกัน ซึ่งเรียกว่า "เฟรม" เฟรมที่ต่อเนื่องกันจะสร้าง ภาพลวงตาของการเคลื่อนไหว

สิ่งที่ไม่ใช่บทความนี้

WebGL และ WebGL2 เป็นวิธีเดียวในการแสดงผลเนื้อหาระหว่างการวนซ้ำเฟรมในแอป WebXR โชคดีที่หลายๆ เฟรมเวิร์กมอบเลเยอร์ Abstraction เพิ่มเติมจาก WebGL และ WebGL2 เฟรมเวิร์กดังกล่าว ได้แก่ three.js, babylonjs และ PlayCanvas ส่วน A-Frame และ React 360 ออกแบบมาเพื่อการโต้ตอบกับ WebXR

บทความนี้ไม่ใช่ทั้ง WebGL และบทแนะนำเกี่ยวกับเฟรมเวิร์ก โดยจะอธิบายพื้นฐานของการวนซ้ำเฟรมโดยใช้ตัวอย่างเซสชัน VR ที่สมจริงของ Immersive Web Working Group (การสาธิต แหล่งที่มา) หากคุณต้องการเจาะลึกเกี่ยวกับ WebGL หรือเฟรมเวิร์กอย่างใดอย่างหนึ่ง อินเทอร์เน็ตมีรายชื่อบทความจำนวนมากขึ้นเรื่อยๆ

ผู้เล่นและเกม

ฉันพยายามทำความเข้าใจการวนซ้ำของเฟรมอยู่เรื่อย ๆ โดยมีออบเจ็กต์จำนวนมากทำงานอยู่ และบางรายการมีชื่อตามพร็อพเพอร์ตี้การอ้างอิงในออบเจ็กต์อื่นๆ เท่านั้น เพื่อช่วยให้ผมอธิบายเกี่ยวกับ ออบเจ็กต์ที่ผมเรียกว่า "ผู้เล่น" จากนั้นฉันจะอธิบายถึงการโต้ตอบ ระหว่างกัน ซึ่งเราเรียกว่า "เกม"

ผู้เล่น

XRViewerPose

โพสท่าคือตำแหน่งและการวางแนวของบางสิ่งในพื้นที่ 3 มิติ ทั้งผู้ชมและอุปกรณ์อินพุตต่างก็มีท่าทาง แต่เป็นท่าทางของผู้ชมที่เรากังวลตรงนี้ ทั้งตำแหน่งอุปกรณ์รับชมและอุปกรณ์อินพุตจะมีแอตทริบิวต์ transform ที่อธิบายตำแหน่งเวกเตอร์และการวางแนวเป็นควอเทิร์นที่สัมพันธ์กับต้นทาง จะมีการระบุต้นทางตามประเภทพื้นที่อ้างอิงที่ขอเมื่อเรียกใช้ XRSession.requestReferenceSpace()

พื้นที่อ้างอิงจะใช้เวลาสักครู่ในการอธิบาย ฉันพูดถึงเรื่องนี้แบบเจาะลึกใน Augmented Reality ตัวอย่างที่ฉันใช้เป็นพื้นฐานสำหรับบทความนี้ใช้พื้นที่อ้างอิง 'local' ซึ่งหมายความว่าต้นทางจะอยู่ที่ตำแหน่งของผู้ชม ณ เวลาที่สร้างเซสชันโดยไม่มีการกำหนดราคาพื้นที่ชัดเจน และตำแหน่งที่แน่นอนอาจแตกต่างกันไปตามแพลตฟอร์ม

XRView

มุมมองจะสอดคล้องกับกล้องที่ดูฉากเสมือนจริง มุมมองยังมีแอตทริบิวต์ transform ที่อธิบายตำแหน่งเวกเตอร์และการวางแนว ตัวแปรเหล่านี้เป็นคู่เวกเตอร์/ควอเทอร์เนียนและเป็นเมทริกซ์ที่เทียบเท่ากัน คุณสามารถใช้การนำเสนอแบบใดแบบหนึ่งขึ้นอยู่กับว่าแบบใดเหมาะกับโค้ดของคุณที่สุด แต่ละมุมมองจะสอดคล้องกับจอแสดงผลหรือบางส่วนของจอแสดงผลที่อุปกรณ์ใช้ในการนำเสนอภาพให้แก่ผู้ชม ออบเจ็กต์ XRView รายการจะแสดงผลในอาร์เรย์จากออบเจ็กต์ XRViewerPose จำนวนการดูในอาร์เรย์จะแตกต่างกันไป บนอุปกรณ์เคลื่อนที่ ฉาก AR จะมีมุมมอง 1 มุมมอง ซึ่งอาจบังหน้าจออุปกรณ์หรือไม่ก็ได้ ชุดหูฟังมักจะเห็นได้ 2 มุมมอง คือสำหรับตาแต่ละข้าง

XRWebGLLayer

เลเยอร์จะให้แหล่งที่มาของรูปภาพบิตแมปและคำอธิบายวิธีแสดงผลรูปภาพเหล่านั้นในอุปกรณ์ คำอธิบายนี้ไม่ครอบคลุมถึงสิ่งที่ โปรแกรมเล่นนี้ทำ ฉันถือว่านี่เป็นคนกลางระหว่างอุปกรณ์กับ WebGLRenderingContext โดย MDN จะใช้มุมมองเดียวกันอย่างมาก โดยระบุว่า "ให้การเชื่อมโยง" ระหว่างทั้ง 2 ประเภท ด้วยเหตุนี้ จึงเป็นการให้สิทธิ์เข้าถึงแก่ผู้เล่นคนอื่นๆ

โดยทั่วไปออบเจ็กต์ WebGL จัดเก็บข้อมูลสถานะสำหรับการแสดงผลกราฟิก 2 มิติและ 3 มิติ

WebGLFramebuffer

Framebuffer ระบุข้อมูลรูปภาพไปยัง WebGLRenderingContext หลังจากเรียกข้อมูลจาก XRWebGLLayer แล้ว คุณเพียงแค่ส่งต่อไปยัง WebGLRenderingContext ปัจจุบัน นอกเหนือจากการเรียกใช้ bindFramebuffer() (เพิ่มเติมในเรื่องนี้ในภายหลัง) คุณจะเข้าถึงออบเจ็กต์นี้โดยตรงไม่ได้เลย แต่จะต้องส่งผ่าน XRWebGLLayer ไปยัง WebGLRenderingContext เท่านั้น

XRViewport

วิวพอร์ตจะระบุพิกัดและมิติข้อมูลของบริเวณสี่เหลี่ยมผืนผ้าในWebGLFramebuffer

WebGLRenderingContext

บริบทการแสดงผลคือจุดเข้าใช้งานแบบเป็นโปรแกรมสำหรับผืนผ้าใบ (พื้นที่ที่เรากำลังวาดลงบนพื้นที่) ซึ่งต้องมีทั้ง WebGLFramebuffer และ XRViewport

โปรดสังเกตความสัมพันธ์ระหว่าง XRWebGLLayer กับ WebGLRenderingContext ข้อความหนึ่งตรงกับอุปกรณ์ของผู้ชม ส่วนอีกแบบหนึ่งสอดคล้องกับหน้าเว็บ WebGLFramebuffer และ XRViewport จะเปลี่ยนจากรายการแรกไปยังรายการหลัง

ความสัมพันธ์ระหว่าง XRWebGLLayer และ WebGLRenderingContext
ความสัมพันธ์ระหว่าง XRWebGLLayer กับ WebGLRenderingContext

เกม

เมื่อรู้แล้วว่าผู้เล่นเป็นใคร เรามาเล่นเกมที่เขาเล่นกัน เป็นเกมที่จะเริ่มใหม่ในทุกๆ เฟรม อย่าลืมว่าเฟรมเป็นส่วนหนึ่งของลูปเฟรมที่เกิดขึ้นในอัตราที่ขึ้นอยู่กับฮาร์ดแวร์พื้นฐาน ในแอปพลิเคชัน VR เฟรม ต่อวินาทีอาจเป็นได้ตั้งแต่ 60 ถึง 144 AR สำหรับ Android ทำงานที่ 30 เฟรมต่อวินาที โค้ดของคุณไม่ควรมีอัตราเฟรมเฉพาะ

ขั้นตอนพื้นฐานสำหรับการวนซ้ำของเฟรมจะมีลักษณะดังนี้

  1. โทรมาที่ XRSession.requestAnimationFrame() ในการตอบกลับ User Agent จะเรียกใช้ XRFrameRequestCallback ตามที่คุณกำหนด
  2. ในฟังก์ชันเรียกกลับ ให้ทำดังนี้
    1. โทรหา XRSession.requestAnimationFrame() อีกครั้ง
    2. โพสต์ท่าทางของผู้ชม
    3. ผ่าน ("bind") WebGLFramebuffer จาก XRWebGLLayer ไปยัง WebGLRenderingContext
    4. วนซ้ำออบเจ็กต์ XRView แต่ละรายการ โดยดึงข้อมูล XRViewport จาก XRWebGLLayer และส่งไปยัง WebGLRenderingContext
    5. วาดสิ่งที่อยู่ในเฟรมบัฟเฟอร์

เนื่องจากขั้นตอนที่ 1 และ 2a อธิบายไว้ในบทความก่อนหน้านี้แล้ว ฉันจะเริ่มที่ขั้นตอนที่ 2ข

โพสท่าให้ผู้ชม

ก็ไม่ต้องบอกใคร ถ้าจะวาดภาพอะไรใน AR หรือ VR ผมต้องรู้เลยว่า ผู้ชมอยู่ที่ไหนและกำลังมองอะไร ตำแหน่งและการวางแนวของผู้ชมมาจากออบเจ็กต์ XRViewerPose ผมได้ท่าทางของผู้ดูโดยการเรียก XRFrame.getViewerPose() ในเฟรมภาพเคลื่อนไหวปัจจุบัน ผมส่งต่อพื้นที่อ้างอิงที่ได้มาเมื่อตั้งค่าเซสชัน ค่าที่ออบเจ็กต์นี้แสดงผลจะสัมพัทธ์กับพื้นที่อ้างอิงที่ฉันขอเสมอเมื่อฉันป้อนเซสชันปัจจุบัน คุณอาจจำได้ว่า ฉันต้องส่งผ่านพื้นที่อ้างอิงปัจจุบันเมื่อขอโพสท่า

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
  if (xrViewerPose) {
    // Render based on the pose.
  }
}

มีผู้ชม 1 ท่าที่แสดงตำแหน่งโดยรวมของผู้ใช้ ซึ่งหมายถึงศีรษะของผู้ชมหรือกล้องโทรศัพท์ในกรณีที่ใส่สมาร์ทโฟน ท่าทางจะบอกแอปพลิเคชันของคุณว่าผู้ดูอยู่ที่ไหน การแสดงผลรูปภาพจริงใช้ออบเจ็กต์ XRView ซึ่งฉันจะเข้าถึงในอีกสักครู่

ก่อนจะไปต่อ ฉันจะทดสอบว่าระบบส่งคืนท่าทางของผู้ชมในกรณีที่ระบบไม่ได้ติดตามหรือบล็อกท่าทางด้วยเหตุผลด้านความเป็นส่วนตัว การติดตามคือความสามารถของอุปกรณ์ XR ในการรู้ว่าตัวเองอยู่ที่ไหนและ/หรืออุปกรณ์อินพุตนั้นสัมพันธ์กับสภาพแวดล้อม การติดตามอาจสูญหายได้หลายวิธีและจะแตกต่างกันไปขึ้นอยู่กับวิธีที่ใช้สำหรับการติดตาม ตัวอย่างเช่น หากมีการใช้กล้องบนชุดหูฟังหรือโทรศัพท์เพื่อติดตามอุปกรณ์ อาจไม่สามารถระบุได้ว่าอุปกรณ์อยู่ที่ใดในสถานการณ์ที่มีแสงน้อยหรือไม่มีแสง หรือกล้องบังอยู่หรือไม่

ตัวอย่างของการบล็อกท่าทางเพื่อเหตุผลด้านความเป็นส่วนตัวคือ หากชุดหูฟังแสดงกล่องโต้ตอบด้านความปลอดภัย เช่น ข้อความแจ้งสิทธิ์ เบราว์เซอร์อาจหยุดแสดงท่าทางแก่แอปพลิเคชันในขณะที่ดำเนินการอยู่ แต่เราได้เรียกใช้ XRSession.requestAnimationFrame() แล้ว ถ้าระบบกู้คืนได้ การวนซ้ำเฟรมจะดำเนินต่อไป มิเช่นนั้น User Agent จะสิ้นสุดเซสชันและเรียกใช้ตัวแฮนเดิลเหตุการณ์ end

เปลี่ยนเส้นทางสั้นๆ

ขั้นตอนถัดไปต้องใช้ออบเจ็กต์ที่สร้างขึ้นในระหว่างการตั้งค่าเซสชัน อย่าลืมว่าฉันได้สร้าง Canvas และสั่งให้สร้างบริบทการแสดงผล Web GL ที่ใช้ร่วมกับ XR ได้ ซึ่งผมได้รับจากการเรียกใช้ canvas.getContext() การวาดทั้งหมดจะเสร็จสิ้นโดยใช้ WebGL API, WebGL2 API หรือเฟรมเวิร์กที่ใช้ WebGL เช่น Three.js ระบบจะส่งบริบทนี้ไปยังออบเจ็กต์เซสชันผ่าน updateRenderState() รวมถึงอินสแตนซ์ใหม่ของ XRWebGLLayer

let canvas = document.createElement('canvas');
// The rendering context must be based on WebGL or WebGL2
let webGLRenContext = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(xrSession, webGLRenContext)
  });

ส่ง ("bind") WebGL Framebuffer

XRWebGLLayer มี Framebuffer สําหรับ WebGLRenderingContext ที่มีไว้สำหรับ WebXR โดยเฉพาะและเพื่อแทนที่Framebuffer เริ่มต้นของการแสดงผล วิธีนี้เรียกว่า "การเชื่อมโยง" ในภาษาของ WebGL

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
  if (xrViewerPose) {
    let glLayer = xrSession.renderState.baseLayer;
    webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer);
    // Iterate over the views
  }
}

ทำซ้ำบนออบเจ็กต์ XRView แต่ละรายการ

หลังจากได้ท่าทางและเชื่อมโยง Framebuffer แล้ว ก็ถึงเวลารวบรวมวิวพอร์ต XRViewerPose มีอาร์เรย์ของอินเทอร์เฟซ XRView แต่ละอินเทอร์เฟซซึ่งแสดงถึงจอแสดงผลหรือบางส่วนของจอแสดงผล การ์ดเหล่านี้มีข้อมูลที่จำเป็นต่อการแสดงผลเนื้อหาที่วางในตำแหน่งที่ถูกต้องของอุปกรณ์และผู้ชม เช่น ขอบเขตการมองเห็น ออฟเซ็ตสายตา และคุณสมบัติทางออปติคัลอื่นๆ เนื่องจากผมวาดตาสองข้าง จะเห็น 2 มุมมอง ซึ่งผมจะวนซ้ำและวาดภาพแยกกันสำหรับมุมมองแต่ละด้าน

เมื่อนำมาใช้กับ Augmented Reality ทางโทรศัพท์ ผมจะมียอดดูแค่ 1 ครั้ง แต่ก็ยังคงใช้การวนซ้ำอยู่ แม้ว่าการทำซ้ำใน 1 มุมมองอาจดูไม่มีประโยชน์ แต่การทำเช่นนี้จะช่วยให้คุณมีเส้นทางการแสดงผลเส้นทางเดียวสำหรับประสบการณ์ที่สมจริงในหลายระดับ นี่เป็นข้อแตกต่างที่สำคัญระหว่าง WebXR กับ ระบบที่สมจริงอื่นๆ

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
  if (xrViewerPose) {
    let glLayer = xrSession.renderState.baseLayer;
    webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer);
    for (let xrView of xrViewerPose.views) {
      // Pass viewports to the context
    }
  }
}

ส่งออบเจ็กต์ XRViewport ไปยัง WebGLRenderingContext

วัตถุ XRView หมายถึงสิ่งที่สังเกตได้บนหน้าจอ แต่การจะวาดรูปสำหรับมุมมองนั้น ผมต้องมีพิกัดและขนาดที่เจาะจงสำหรับอุปกรณ์ของผม เช่นเดียวกับการใช้ Framebuffers ฉันจะส่งคำขอจาก XRWebGLLayer และส่งไปยัง WebGLRenderingContext

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
  if (xrViewerPose) {
    let glLayer = xrSession.renderState.baseLayer;
    webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer);
    for (let xrView of xrViewerPose.views) {
      let viewport = glLayer.getViewport(xrView);
      webGLRenContext.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
      // Draw something to the framebuffer
    }
  }
}

webGLRenContext

ในการเขียนบทความนี้ ฉันมีการโต้เถียงกับเพื่อนร่วมงาน 2-3 คนเกี่ยวกับการตั้งชื่อออบเจ็กต์ webGLRenContext สคริปต์ตัวอย่างและโค้ด WebXR ส่วนใหญ่เรียกใช้ตัวแปรนี้ gl ตอนที่พยายามทำความเข้าใจตัวอย่างเพลง ฉันมักลืมว่า gl หมายถึงอะไร ฉันตั้งชื่อว่า webGLRenContext เพื่อเตือนคุณ ขณะทราบว่านี่คืออินสแตนซ์ของ WebGLRenderingContext

เหตุผลคือการใช้ gl ช่วยให้ชื่อเมธอดดูคล้ายกับคู่ของเมธอดใน OpenGL ES 2.0 API ซึ่งใช้สำหรับการสร้าง VR ในภาษาที่คอมไพล์แล้ว ข้อเท็จจริงนี้เห็นได้ชัดมากหากคุณเขียนแอป VR โดยใช้ OpenGL แต่ก็สับสนหากคุณยังใหม่กับเทคโนโลยีนี้อย่างเต็มที่

วาดบางสิ่งลงในเฟรมบัฟเฟอร์

ถ้าคุณรู้สึกว่าตัวเองทะเยอทะยาน คุณสามารถใช้ WebGL ได้โดยตรง แต่เราไม่แนะนำให้ทำ การใช้เฟรมเวิร์กที่ระบุไว้ที่ด้านบนจะง่ายกว่ามาก

บทสรุป

อย่างไรก็ตาม การอัปเดตหรือบทความ WebXR ยังไม่สิ้นสุดเพียงเท่านี้ ดูข้อมูลอ้างอิงสำหรับอินเทอร์เฟซและสมาชิกทั้งหมดของ WebXR ได้ที่ MDN สำหรับการปรับปรุงอินเทอร์เฟซในเร็วๆ นี้ ให้ทำตามแต่ละฟีเจอร์ในสถานะของ Chrome

รูปภาพโดย JESHOOTS.COM ใน Unsplash