वर्चुअल रिएलिटी अब वेब पर उपलब्ध, दूसरा पार्ट

फ़्रेम लूप के बारे में पूरी जानकारी

Joe Medley
Joe Medley

हाल ही में, मैंने वेब पर वर्चुअल रिएलिटी की सुविधा पब्लिश की है. यह एक लेख है, जिसमें WebXR Device API के बुनियादी सिद्धांतों के बारे में बताया गया है. मैंने XR सेशन का अनुरोध करने, उसमें शामिल होने, और उसे खत्म करने के निर्देश भी दिए थे.

इस लेख में फ़्रेम लूप के बारे में बताया गया है. यह एक उपयोगकर्ता-एजेंट कंट्रोल करने वाला इनफ़ाइनाइट लूप है. इसमें कॉन्टेंट को बार-बार स्क्रीन पर दिखाया जाता है. कॉन्टेंट अलग-अलग ब्लॉक में तैयार किया जाता है, जिसे फ़्रेम कहा जाता है. फ़्रेम का क्रम, चलने का भ्रम पैदा करता है.

यह लेख क्या नहीं है

WebXR ऐप्लिकेशन में फ़्रेम लूप के दौरान, कॉन्टेंट को रेंडर करने का सिर्फ़ एक तरीका है WebGL और WebGL2. अच्छी बात यह है कि कई फ़्रेमवर्क, WebGL और WebGL2 के ऊपर ऐब्स्ट्रैक्शन की एक लेयर उपलब्ध कराते हैं. इस तरह के फ़्रेमवर्क में three.js, babylonjs, और PlayCanvas शामिल हैं. वहीं, A-Frame और React 360 को WebXR से इंटरैक्ट करने के लिए डिज़ाइन किया गया है.

यह लेख न तो WebGL है और न ही कोई फ़्रेमवर्क ट्यूटोरियल है. इसमें इमर्सिव वेब वर्किंग ग्रुप के इमर्सिव वीआर सेशन सैंपल का इस्तेमाल करके फ़्रेम लूप की बुनियादी बातें बताई गई हैं. (डेमो, सोर्स). अगर आपको WebGL या किसी एक फ़्रेमवर्क के बारे में ज़्यादा जानना है, तो इंटरनेट लेखों की बढ़ती हुई सूची उपलब्ध कराता है.

खिलाड़ी और गेम

फ़्रेम लूप को समझने की कोशिश करते समय, मैं बारीकियों में खोई रहती थी. इस प्रोजेक्ट में बहुत सारे ऑब्जेक्ट हैं और उनमें से कुछ के नाम, सिर्फ़ अन्य ऑब्जेक्ट की रेफ़रंस प्रॉपर्टी के आधार पर दिए गए हैं. इसे आसान बनाने में आपकी मदद करने के लिए, मैं उन ऑब्जेक्ट के बारे में बताऊँगी जिन्हें मैंने 'प्लेयर' कहा है. फिर मैं बताऊँगी कि वे कैसे इंटरैक्ट करते हैं, जिन्हें मैं 'गेम' कह रहा हूं.

खिलाड़ी

XRViewerPose

पोज़, 3D स्पेस में किसी चीज़ की पोज़िशन और ओरिएंटेशन को कहते हैं. दर्शक और इनपुट डिवाइस, दोनों का पोज़ होता है, लेकिन हमें दर्शकों का पोज़ के बारे में यहां चिंता है. व्यूअर और इनपुट डिवाइस पोज़, दोनों में एक transform एट्रिब्यूट होता है. यह एट्रिब्यूट, इसकी पोज़िशन को वेक्टर के तौर पर और ऑरिजिन के मुकाबले क्वाटर्नियन के तौर पर बताता है. XRSession.requestReferenceSpace() को कॉल करते समय ऑरिजिन, अनुरोध किए गए रेफ़रंस स्पेस टाइप के आधार पर तय किया जाता है.

रेफ़रंस स्पेस को समझने में थोड़ा समय लगता है. मैंने उन्हें ऑगमेंटेड रिएलिटी (एआर) के बारे में विस्तार से बताया है. इस लेख के लिए आधार के तौर पर, मैंने जिस सैंपल का इस्तेमाल किया है उसमें 'local' रेफ़रंस स्पेस का इस्तेमाल किया गया है. इसका मतलब है कि सेशन बनाते समय ऑरिजिन, सेशन बनाते समय दर्शक की पोज़िशन पर है, लेकिन प्लैटफ़ॉर्म के हिसाब से इसकी सटीक पोज़िशन अलग-अलग हो सकती है.

XRView

व्यू, वर्चुअल सीन को देखने वाले कैमरे से मेल खाता है. एक व्यू में भी एक transform एट्रिब्यूट होता है, जो इसकी पोज़िशन, वेक्टर के तौर पर और इसके ओरिएंटेशन के बारे में बताता है. ये वेक्टर/क्वाटर्नियन पेयर और समतुल्य मैट्रिक्स, दोनों के तौर पर दिए जाते हैं. इनमें से किसी भी तरीके को इस आधार पर इस्तेमाल किया जा सकता है कि आपके कोड के लिए कौनसा सबसे सही है. हर व्यू, डिवाइस के इस्तेमाल किए जाने वाले डिसप्ले या डिसप्ले के किसी हिस्से से जुड़ा होता है. इससे दर्शक को इमेज दिखाने के लिए मदद मिलती है. XRView ऑब्जेक्ट, अरे में XRViewerPose ऑब्जेक्ट से दिखाए जाते हैं. कलेक्शन में मौजूद व्यू की संख्या अलग-अलग हो सकती है. मोबाइल डिवाइस पर एआर सीन में एक व्यू होता है, जो डिवाइस की स्क्रीन को कवर कर भी सकता है और नहीं भी. आम तौर पर, हेडसेट के दो व्यू होते हैं, हर आंख के लिए एक व्यू.

XRWebGLLayer

लेयर, बिटमैप इमेज का सोर्स उपलब्ध कराती हैं. साथ ही, यह जानकारी भी देती हैं कि उन इमेज को डिवाइस में कैसे रेंडर किया जाएगा. यह विवरण इस प्लेयर के क्या काम करता है, इस बारे में पूरी जानकारी नहीं देता. मुझे लगा कि यह एक डिवाइस और WebGLRenderingContext के बीच एक बिचौलिए की तरह है. एमडीएन, दोनों के बीच 'जुड़ने' को कहता है. साथ ही, इससे दूसरे खिलाड़ियों को ऐक्सेस दिया जाता है.

आम तौर पर, WebGL ऑब्जेक्ट 2D और 3D ग्राफ़िक रेंडर करने के लिए, स्थिति की जानकारी सेव करता है.

WebGLFramebuffer

फ़्रेमबफ़र, WebGLRenderingContext को इमेज डेटा देता है. XRWebGLLayer से डेटा पाने के बाद, आपको उसे मौजूदा WebGLRenderingContext में भेजना होगा. bindFramebuffer() को कॉल करने के अलावा (इसके बारे में ज़्यादा जानकारी) आपके पास इस ऑब्जेक्ट को सीधे तौर पर ऐक्सेस करने का विकल्प नहीं होगा. आपको इसे सिर्फ़ XRWebGLLayer से WebGLRenderingContext में भेजना होगा.

XRViewport

व्यूपोर्ट, WebGLFramebuffer में किसी आयताकार क्षेत्र के निर्देशांक और डाइमेंशन उपलब्ध कराता है.

WebGLRenderingContext

रेंडरिंग कॉन्टेक्स्ट, कैनवस के लिए प्रोग्राम का इस्तेमाल करने का ऐक्सेस पॉइंट है (वह स्पेस जिसे हम ड्रॉ कर रहे हैं). ऐसा करने के लिए, आपको WebGLFramebuffer और XRViewport, दोनों की ज़रूरत होगी.

XRWebGLLayer और WebGLRenderingContext के बीच के संबंध पर ध्यान दें. एक तरीका दर्शक के डिवाइस से जुड़ा है और दूसरा वेब पेज. WebGLFramebuffer और XRViewport को पिछली वाली फ़ाइल से पास किया गया है.

XRWebGLlayer और WebGL रेंडरिंगContext के बीच का संबंध
XRWebGLLayer और WebGLRenderingContext के बीच का संबंध

गेम

अब जब हम जानते हैं कि खिलाड़ी कौन हैं, तो आइए अब देखते हैं कि वे कौनसा गेम खेलते हैं. यह एक ऐसा गेम है जो हर फ़्रेम के साथ शुरू होता है. याद रखें कि फ़्रेम उस फ़्रेम लूप का हिस्सा होते हैं जो वीडियो में मौजूद हार्डवेयर के हिसाब से तय दर से होता है. वीआर ऐप्लिकेशन के लिए, फ़्रेम प्रति सेकंड 60 से 144 तक हो सकता है. Android का एआर 30 फ़्रेम प्रति सेकंड पर चलता है. आपके कोड को किसी खास फ़्रेम रेट को नहीं मानना चाहिए.

फ़्रेम लूप की बुनियादी प्रक्रिया इस तरह दिखती है:

  1. XRSession.requestAnimationFrame() पर कॉल करें. जवाब में, उपयोगकर्ता एजेंट आपके तय किए गए XRFrameRequestCallback को शुरू करता है.
  2. आपके कॉलबैक फ़ंक्शन में:
    1. XRSession.requestAnimationFrame() को फिर से कॉल करें.
    2. दर्शक का पोज़ देखें.
    3. WebGLFramebuffer को XRWebGLLayer से WebGLRenderingContext में पास ('बाइंड') करें.
    4. हर XRView ऑब्जेक्ट पर दोहराएं, इसके XRViewport को XRWebGLLayer से वापस लाएं और WebGLRenderingContext को पास करें.
    5. फ़्रेमबफ़र में कुछ बनाएं.

पिछले लेख में चरण 1 और 2a के बारे में बताया गया है, इसलिए मैं चरण 2b से शुरू करूंगी.

दर्शकों का पोज़ देखें

बिना कुछ कहे भी ऐसा किया जा सकता है. एआर या वीआर में कुछ भी ड्रॉ करने के लिए, मुझे यह जानना होगा कि दर्शक कहां है और वह कहां देख रहा है. दर्शक की पोज़िशन और ओरिएंटेशन की जानकारी, 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.
  }
}

इसमें दर्शक का एक पोज़ होता है जो उपयोगकर्ता की पूरी पोज़िशन दिखाता है. इसका मतलब है कि स्मार्टफ़ोन के मामले में दर्शक का सिर या फ़ोन का कैमरा. पोज़ से आपके ऐप्लिकेशन को यह पता चलता है कि दर्शक कहां है. असल इमेज रेंडरिंग में XRView ऑब्जेक्ट इस्तेमाल होते हैं, जिनके बारे में हम थोड़ी देर में बात करेंगे.

आगे बढ़ने से पहले, मैं यह जांच करता हूं कि क्या दर्शक का पोज़ वापस दिखा दिया गया है. ऐसा इसलिए है, क्योंकि निजता की वजह से सिस्टम, पोज़ को ट्रैक नहीं कर पाता है या पोज़ को ब्लॉक कर देता है. ट्रैकिंग की मदद से, XR डिवाइस यह जान पाता है कि आस-पास के माहौल के हिसाब से आपका डिवाइस कहां और/या उसके इनपुट डिवाइस हैं. ट्रैकिंग कई तरीकों से खो सकती है और यह ट्रैकिंग के लिए इस्तेमाल किए जाने वाले तरीके के आधार पर अलग-अलग होती है. उदाहरण के लिए, अगर हेडसेट या फ़ोन के कैमरों का इस्तेमाल डिवाइस को ट्रैक करने के लिए किया जाता है, तो इसकी वजह से शायद कैमरे को यह पता न चले कि उस जगह पर रोशनी कम है या नहीं है या कैमरे ढके हैं.

निजता के लिए पोज़ को ब्लॉक करने का एक उदाहरण यह है कि अगर हेडसेट अनुमति प्रॉम्प्ट जैसा सुरक्षा डायलॉग दिखा रहा हो, तो ऐसा होने के दौरान ब्राउज़र ऐप्लिकेशन के लिए पोज़ देना बंद कर सकता है. हालांकि, मैंने XRSession.requestAnimationFrame() को पहले ही कॉल कर लिया है, ताकि अगर सिस्टम रिकवर हो सके, तो फ़्रेम लूप जारी रहेगा. अगर ऐसा नहीं है, तो उपयोगकर्ता एजेंट सेशन को खत्म करेगा और end इवेंट हैंडलर को कॉल करेगा.

एक छोटा चक्कर

अगले चरण के लिए, सेशन सेट-अप के दौरान बनाए गए ऑब्जेक्ट ज़रूरी हैं. याद रखें कि मैंने एक कैनवस बनाया था और उसे XR के साथ काम करने वाला Web GL रेंडरिंग कॉन्टेक्स्ट बनाने का निर्देश दिया था, जो मुझे canvas.getContext() को कॉल करके मिला. सभी ड्रॉइंग, WebGL एपीआई, WebGL2 एपीआई या 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)
  });

WebGLFramebuffer को पास ('bind') करें

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 ऑब्जेक्ट पर दोहराएं

पोज़ पाने और फ़्रेमबफ़र को बाइंड करने के बाद, अब व्यूपोर्ट पाने का समय है. XRViewerPose में XRView इंटरफ़ेस का एक कलेक्शन होता है. हर इंटरफ़ेस, डिसप्ले या डिसप्ले के किसी हिस्से को दिखाता है. इनमें ऐसी जानकारी होती है जो कॉन्टेंट को रेंडर करने के लिए ज़रूरी होती है. यह कॉन्टेंट, डिवाइस और व्यूअर के लिए सही पोज़िशन में होता है. जैसे, फ़ील्ड ऑफ़ व्यू, आई ऑफ़सेट, और अन्य ऑप्टिकल प्रॉपर्टी. मैं दो आंखों के लिए ड्रॉइंग बना रहा/रही हूं, मेरे पास दो व्यू हैं. इन्हें लूप में रखते हुए हर एक के लिए एक अलग इमेज बनाई गई है.

फ़ोन पर आधारित ऑगमेंटेड रिएलिटी (एआर) के लिए लागू करते समय, मेरे पास सिर्फ़ एक व्यू होगा, लेकिन मैं फिर भी लूप का इस्तेमाल करूंगा. हालांकि, ऐसा हो सकता है कि इस प्रोसेस को एक तरीके से दोहराना नामुमकिन लगे, लेकिन ऐसा करने से आपको एक ही रेंडरिंग पाथ बनाने में मदद मिलती है. इसमें कई शानदार अनुभव मिल सकते हैं. यह 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 ऑब्जेक्ट को WebGL रेंडरिंगContext पर पास करें

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

webGLRenकॉन्टेक्स्ट

इस लेख को लिखते समय, webGLRenContext ऑब्जेक्ट के नाम को लेकर मेरी कुछ सहयोगियों से बहस हुई थी. सैंपल स्क्रिप्ट और ज़्यादातर WebXR कोड, इस वैरिएबल को gl को आसानी से कॉल करते हैं. सैंपल को समझने के दौरान, मैं इसे भूलने की कोशिश करता था जिसके बारे में gl ने बताया था. मैंने इसे webGLRenContext नाम दिया है, ताकि आपको याद दिला सकें कि यह WebGLRenderingContext का एक इंस्टेंस है.

इसकी वजह यह है कि gl का इस्तेमाल करने पर, तरीके के नाम OpenGL ES 2.0 एपीआई में, उनके मिलते-जुलते नाम की तरह दिख सकते हैं. इसका इस्तेमाल कंपाइलेशन भाषाओं में वीआर बनाने के लिए किया जाता है. अगर आपने OpenGL का इस्तेमाल करके VR ऐप्लिकेशन लिखे हैं, तो यह बात साफ़-साफ़ ज़ाहिर नहीं होगी, लेकिन अभी यह ग़लतफ़हमी है कि आप इस तकनीक के लिए नए हैं.

फ़्रेमबफ़र में कुछ ड्रॉ करें

अगर आप बहुत सोच रहे हैं, तो सीधे WebGL का इस्तेमाल करें, लेकिन मेरा सुझाव नहीं है. सबसे ऊपर दिए गए फ़्रेमवर्क का इस्तेमाल करना ज़्यादा आसान है.

नतीजा

WebXR के अपडेट या लेख यहां खत्म नहीं होंगे. आपको एमडीएन पर WebXR के सभी इंटरफ़ेस और सदस्यों के लिए रेफ़रंस मिल सकता है. इन इंटरफ़ेस को बेहतर बनाने के लिए, Chrome की स्थिति में दी गई अलग-अलग सुविधाओं को फ़ॉलो करें.

Unsplash पर JESHOOTS.COM की फ़ोटो