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

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

Joe Medley
Joe Medley

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

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

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

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

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

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

ผู้เล่น

XRViewerPose

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

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

XRView

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

XRWebGLLayer

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

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

WebGLFramebuffer

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

XRViewport

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

WebGLRenderingContext

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

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

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

เกม

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

ขั้นตอนพื้นฐานสำหรับเฟรมแบบวนซ้ำมีดังนี้

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

เนื่องจากเราได้อธิบายขั้นตอนที่ 1 และ 2ก ในบทความก่อนหน้าแล้ว เราจะเริ่มต้นที่ขั้นตอน 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") การเก็บ WebGLFramebuffer

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 แล้ว ก็ถึงเวลารับ Viewport XRViewerPose มีอาร์เรย์ของอินเทอร์เฟซ XRView ซึ่งแต่ละรายการแสดงถึงจอแสดงผลหรือส่วนหนึ่งของจอแสดงผล โดยจะมีข้อมูลที่จำเป็นในการเรนเดอร์เนื้อหาที่วางตำแหน่งอย่างถูกต้องสำหรับอุปกรณ์และผู้ชม เช่น มุมมอง การเลื่อนตา และพร็อพเพอร์ตี้อื่นๆ เกี่ยวกับแสง เนื่องจากฉันวาดภาพสำหรับดวงตา 2 ข้าง ฉันจึงมีมุมมอง 2 มุมมอง ซึ่งฉันจะวนดูและวาดภาพแยกกันสำหรับแต่ละมุมมอง

เมื่อติดตั้งใช้งานสำหรับ Augmented Reality บนโทรศัพท์ ฉันจะมีมุมมองเดียว แต่จะใช้ลูป แม้ว่าการวนดูมุมมองเดียวอาจดูเหมือนไม่มีจุดหมาย แต่การดำเนินการนี้จะช่วยให้คุณมีเส้นทางการแสดงผลเดียวสำหรับประสบการณ์ที่สมจริงที่หลากหลาย นี่คือความแตกต่างที่สำคัญระหว่าง 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 หมายถึงสิ่งที่สังเกตได้บนหน้าจอ แต่หากต้องการวาดในมุมมองนั้น ฉันต้องใช้พิกัดและมิติข้อมูลเฉพาะสำหรับอุปกรณ์ของฉัน เช่นเดียวกับเฟรมบัฟเฟอร์ เราจะขอข้อมูลจาก 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

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

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

วาดสิ่งที่อยู่ในเฟรมบัฟเฟอร์

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

บทสรุป

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

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