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

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

Joe Medley
Joe Medley

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

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

इस लेख में क्या नहीं बताया गया है

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

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

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

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

खिलाड़ी

XRViewerPose

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

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

XRView

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

XRWebGLLayer

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

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

WebGLFramebuffer

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

XRViewport

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

WebGLRenderingContext

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

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

XRWebGLLayer और WebGLRenderingContext के बीच का संबंध
XRWebGLLayer और WebGLRenderingContext के बीच का संबंध

गेम

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

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

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

पहले लेख में पहले और दूसरे चरण के बारे में बताया गया है. इसलिए, हम सीधे दूसरे चरण के दूसरे हिस्से से शुरू करेंगे.

दर्शक की पोज़ पाना

यह बताने की ज़रूरत नहीं है. एआर या वीआर में कुछ भी बनाने के लिए, मुझे यह जानना होगा कि दर्शक कहां है और वह कहां देख रहा है. दर्शक की पोज़िशन और ऑरिएंटेशन की जानकारी, 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 के साथ काम करने वाला वेब GL रेंडरिंग कॉन्टेक्स्ट बनाने का निर्देश दिया था. यह कॉन्टेक्स्ट मुझे canvas.getContext() को कॉल करके मिला था. सभी ड्रॉइंग, WebGL API, WebGL2 API या Three.js जैसे WebGL-आधारित फ़्रेमवर्क का इस्तेमाल करके की जाती हैं. इस कॉन्टेक्स्ट को 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 को पास ('बाइंड') करना

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 ऑब्जेक्ट को 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 एपीआई में उनके मिलते-जुलते नामों जैसे दिखते हैं. एपीआई का इस्तेमाल, संकलित की गई भाषाओं में वीआर बनाने के लिए किया जाता है. अगर आपने OpenGL का इस्तेमाल करके वीआर ऐप्लिकेशन लिखे हैं, तो यह बात आपके लिए साफ़ तौर पर समझ आ जाएगी. हालांकि, अगर आपने इस टेक्नोलॉजी का इस्तेमाल पहले कभी नहीं किया है, तो यह बात आपके लिए भ्रमित करने वाली हो सकती है.

फ़्रेमबफ़र में कुछ बनाना

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

नतीजा

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

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