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

วาดรูปใน Framebuffer

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

บทสรุป

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

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