हिट टेस्ट एपीआई की मदद से, वर्चुअल आइटम को असल दुनिया के व्यू में रखा जा सकता है.
WebXR Device API को पिछले साल Chrome 79 में लॉन्च किया गया था. जैसा कि पहले बताया गया था, Chrome में इस एपीआई को लागू करने की प्रोसेस जारी है. Chrome को यह बताते हुए खुशी हो रही है कि कुछ काम पूरे हो गए हैं. Chrome 81 में, दो नई सुविधाएं जोड़ी गई हैं:
इस लेख में WebXR हिट टेस्ट एपीआई के बारे में बताया गया है. यह असल दुनिया के कैमरे के व्यू में वर्चुअल ऑब्जेक्ट डालने का एक तरीका है.
इस लेख में, हम यह मानकर चल रहे हैं कि आपको ऑगमेंटेड रिएलिटी (एआर) सेशन बनाने और फ़्रेम लूप चलाने का तरीका पहले से पता है. अगर आपको इन कॉन्सेप्ट के बारे में नहीं पता है, तो आपको इस सीरीज़ के पिछले लेख पढ़ने चाहिए.
- वर्चुअल रिएलिटी अब वेब पर भी उपलब्ध है
- वर्चुअल रिएलिटी अब वेब पर भी, दूसरा पार्ट
- वेब एआर: शायद आपको पहले से ही इसके इस्तेमाल का तरीका पता हो
इमर्सिव एआर सेशन का सैंपल
इस लेख में दिया गया कोड, इमर्सिव वेब वर्किंग ग्रुप के हिट टेस्ट सैंपल (डेमो, सोर्स) में मौजूद कोड पर आधारित है. हालांकि, यह पूरी तरह से उसके जैसा नहीं है. इस उदाहरण की मदद से, असल दुनिया की सतहों पर वर्चुअल सूरजमुखी के फूल रखे जा सकते हैं.
पहली बार ऐप्लिकेशन खोलने पर, आपको एक नीला सर्कल दिखेगा. इसके बीच में एक बिंदु होगा. डॉट, आपके डिवाइस से आस-पास के माहौल में मौजूद पॉइंट तक की एक काल्पनिक लाइन का इंटरसेक्शन है. डिवाइस को मूव करने पर, यह भी मूव होता है. इंटरसेक्शन पॉइंट मिलने पर, यह फ़र्श, टेबल टॉप, और दीवारों जैसी सतहों पर स्नैप होता हुआ दिखता है. ऐसा इसलिए होता है, क्योंकि हिट टेस्टिंग से इंटरसेक्शन पॉइंट की पोज़िशन और ओरिएंटेशन की जानकारी मिलती है. हालांकि, इससे सतहों के बारे में कोई जानकारी नहीं मिलती.
इस सर्कल को रेटिकल कहा जाता है. यह एक अस्थायी इमेज होती है, जो ऑगमेंटेड रिएलिटी (एआर) में किसी ऑब्जेक्ट को रखने में मदद करती है. स्क्रीन पर टैप करने पर, सूरजमुखी को रेटिकल की जगह और रेटिकल पॉइंट के ओरिएंटेशन के हिसाब से सतह पर रखा जाता है. इससे कोई फ़र्क़ नहीं पड़ता कि आपने स्क्रीन पर कहां टैप किया है. रेटिकल आपके डिवाइस के साथ मूव करता रहता है.
रेटिकल बनाना
आपको रेटिकल इमेज खुद बनानी होगी, क्योंकि यह ब्राउज़र या एपीआई से नहीं मिलती. इसे लोड करने और ड्रॉ करने का तरीका, फ़्रेमवर्क के हिसाब से तय होता है.
अगर WebGL या WebGL2 का इस्तेमाल करके सीधे तौर पर ड्रॉ नहीं किया जा रहा है, तो अपने फ़्रेमवर्क के दस्तावेज़ देखें. इस वजह से, हम इस बारे में ज़्यादा जानकारी नहीं देंगे कि सैंपल में रेटिकल को कैसे ड्रॉ किया जाता है. यहां मैंने इसकी एक लाइन सिर्फ़ एक वजह से दिखाई है: ताकि बाद के कोड सैंपल में, आपको पता चल सके कि reticle वैरिएबल का इस्तेमाल करते समय, मैं किस बारे में बात कर रहा हूं.
let reticle = new Gltf2Node({url: 'media/gltf/reticle/reticle.gltf'});
सेशन का अनुरोध करना
सेशन का अनुरोध करते समय, आपको requiredFeatures कलेक्शन में 'hit-test' का अनुरोध करना होगा, जैसा कि यहां दिखाया गया है.
navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['local', 'hit-test']
})
.then((session) => {
// Do something with the session
});
सेशन में शामिल होना
पिछले लेखों में, मैंने एक्सआर सेशन में शामिल होने के लिए कोड दिया है. मैंने नीचे कुछ और जानकारी के साथ
इसका एक वर्शन दिखाया है. सबसे पहले, मैंने select इवेंट लिसनर जोड़ा है. जब उपयोगकर्ता स्क्रीन पर टैप करेगा, तो रेटिकल के पोज़ के आधार पर, कैमरे के व्यू में एक फूल दिखेगा. हम उस इवेंट लिसनर के बारे में बाद में बताएंगे.
function onSessionStarted(xrSession) {
xrSession.addEventListener('end', onSessionEnded);
xrSession.addEventListener('select', onSelect);
let canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
xrSession.requestReferenceSpace('viewer').then((refSpace) => {
xrViewerSpace = refSpace;
xrSession.requestHitTestSource({ space: xrViewerSpace })
.then((hitTestSource) => {
xrHitTestSource = hitTestSource;
});
});
xrSession.requestReferenceSpace('local').then((refSpace) => {
xrRefSpace = refSpace;
xrSession.requestAnimationFrame(onXRFrame);
});
}
एक से ज़्यादा रेफ़रंस स्पेस
ध्यान दें कि हाइलाइट किया गया कोड, XRSession.requestReferenceSpace() को दो बार कॉल करता है. मुझे शुरुआत में यह भ्रमित करने वाला लगा. मैंने पूछा कि हिट टेस्ट कोड, ऐनिमेशन फ़्रेम (फ़्रेम लूप शुरू करना) का अनुरोध क्यों नहीं करता. साथ ही, फ़्रेम लूप में हिट टेस्ट क्यों शामिल नहीं होते. इस भ्रम की वजह, रेफ़रंस स्पेस को लेकर हुई गलतफ़हमी थी. रेफ़रंस स्पेस, ओरिजिन और दुनिया के बीच के संबंधों को दिखाते हैं.
यह कोड क्या कर रहा है, यह समझने के लिए मान लें कि आप इस सैंपल को स्टैंडअलोन रिग का इस्तेमाल करके देख रहे हैं. साथ ही, आपके पास हेडसेट और कंट्रोलर, दोनों हैं. कंट्रोलर से दूरी मापने के लिए, कंट्रोलर-सेंटर्ड फ़्रेम ऑफ़ रेफ़रंस का इस्तेमाल किया जाएगा. हालांकि, स्क्रीन पर कुछ ड्रॉ करने के लिए, आपको उपयोगकर्ता के हिसाब से निर्देशांकों का इस्तेमाल करना होगा.
इस सैंपल में, व्यूअर और कंट्रोलर एक ही डिवाइस है. लेकिन मुझे एक समस्या है. मैं जो भी ड्रॉ करूं वह आस-पास के माहौल के हिसाब से स्टेबल होना चाहिए. हालांकि, मैं जिस 'कंट्रोलर' से ड्रॉ कर रहा हूं वह मूव कर रहा है.
इमेज ड्रॉइंग के लिए, मैं local रेफ़रंस स्पेस का इस्तेमाल करता हूं. इससे मुझे एनवायरमेंट के हिसाब से स्टेबिलिटी मिलती है. यह मिलने के बाद, मैं requestAnimationFrame() को कॉल करके फ़्रेम लूप शुरू करता हूं.
हिट टेस्टिंग के लिए, मैं viewer रेफ़रंस स्पेस का इस्तेमाल करता हूं. यह हिट टेस्ट के समय डिवाइस के पोज़ पर आधारित होता है. इस संदर्भ में 'व्यूअर' लेबल थोड़ा भ्रमित करने वाला है, क्योंकि मैं कंट्रोलर के बारे में बात कर रहा हूं. अगर कंट्रोलर को इलेक्ट्रॉनिक व्यूअर के तौर पर देखा जाए, तो यह बात सही लगती है. इसके बाद, मैं xrSession.requestHitTestSource() को कॉल करता हूं. इससे हिट टेस्ट डेटा का सोर्स बनता है. इसका इस्तेमाल मैं ड्रॉइंग के दौरान करूंगा.
फ़्रेम लूप चलाना
requestAnimationFrame() कॉलबैक को हिट टेस्टिंग मैनेज करने के लिए नया कोड भी मिलता है.
डिवाइस को मूव करने पर, रेटिकल को भी उसके साथ मूव करना चाहिए, क्योंकि वह सतहों को ढूंढने की कोशिश करता है. मूवमेंट का भ्रम पैदा करने के लिए, हर फ़्रेम में रेटिकल को फिर से ड्रॉ करें.
हालांकि, अगर हिट टेस्ट फ़ेल हो जाता है, तो रेटिकल न दिखाएं. इसलिए, मैंने पहले जो रेटिकल बनाया था उसकी visible प्रॉपर्टी को false पर सेट किया.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
reticle.visible = false;
// Reminder: the hitTestSource was acquired during onSessionStart()
if (xrHitTestSource && xrViewerPose) {
let hitTestResults = xrFrame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
let pose = hitTestResults[0].getPose(xrRefSpace);
reticle.visible = true;
reticle.matrix = pose.transform.matrix;
}
}
// Draw to the screen
}
एआर में कुछ भी ड्रॉ करने के लिए, मुझे यह जानना होगा कि दर्शक कहां है और वह कहां देख रहा है. इसलिए, मैं यह जांच करता हूं कि hitTestSource और xrViewerPose अब भी मान्य हैं या नहीं.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
reticle.visible = false;
// Reminder: the hitTestSource was acquired during onSessionStart()
if (xrHitTestSource && xrViewerPose) {
let hitTestResults = xrFrame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
let pose = hitTestResults[0].getPose(xrRefSpace);
reticle.visible = true;
reticle.matrix = pose.transform.matrix;
}
}
// Draw to the screen
}
अब मैं getHitTestResults() को कॉल करता हूं. यह hitTestSource को आर्ग्युमेंट के तौर पर लेता है और HitTestResult इंस्टेंस का कलेक्शन दिखाता है. हिट टेस्ट में कई सतहें मिल सकती हैं. कलेक्शन में मौजूद पहला ऑब्जेक्ट, कैमरे के सबसे नज़दीक होता है.
ज़्यादातर मामलों में इसका इस्तेमाल किया जाएगा. हालांकि, बेहतर इस्तेमाल के मामलों के लिए, एक कलेक्शन दिखाया जाता है. उदाहरण के लिए, मान लें कि आपका कैमरा किसी फ़्लोर पर रखी टेबल पर मौजूद बॉक्स की तरफ़ है. ऐसा हो सकता है कि हिट टेस्ट, कलेक्शन में मौजूद तीनों सरफ़ेस को दिखा दे. ज़्यादातर मामलों में, मुझे बॉक्स की ही चिंता होगी. अगर दिखाए गए कलेक्शन की लंबाई 0 है, यानी कि अगर कोई हिट टेस्ट नहीं दिखाया जाता है, तो आगे बढ़ें. अगले फ़्रेम में फिर से कोशिश करें.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
reticle.visible = false;
// Reminder: the hitTestSource was acquired during onSessionStart()
if (xrHitTestSource && xrViewerPose) {
let hitTestResults = xrFrame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
let pose = hitTestResults[0].getPose(xrRefSpace);
reticle.visible = true;
reticle.matrix = pose.transform.matrix;
}
}
// Draw to the screen
}
आखिर में, मुझे हिट टेस्ट के नतीजों को प्रोसेस करना होगा. इसकी बुनियादी प्रोसेस यह है. हिट टेस्ट के नतीजे से पोज़ पाएं. इसके बाद, रेटिकल इमेज को हिट टेस्ट की पोज़िशन पर ट्रांसफ़ॉर्म (मूव) करें. इसके बाद, इसकी visible प्रॉपर्टी को 'सही है' पर सेट करें. पोज़, किसी सतह पर मौजूद पॉइंट के पोज़ को दिखाता है.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
reticle.visible = false;
// Reminder: the hitTestSource was acquired during onSessionStart()
if (xrHitTestSource && xrViewerPose) {
let hitTestResults = xrFrame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
let pose = hitTestResults[0].getPose(xrRefSpace);
reticle.matrix = pose.transform.matrix;
reticle.visible = true;
}
}
// Draw to the screen
}
कोई ऑब्जेक्ट जोड़ना
जब उपयोगकर्ता स्क्रीन पर टैप करता है, तो एआर में कोई ऑब्जेक्ट दिखता है. मैंने सेशन में पहले ही एक select इवेंट हैंडलर जोड़ दिया है. (ऊपर देखें.)
इस चरण में सबसे ज़रूरी बात यह है कि आपको पता होना चाहिए कि इसे कहां रखना है. मूविंग रेटिकल, हिट टेस्ट के लिए लगातार सोर्स उपलब्ध कराता है. इसलिए, किसी ऑब्जेक्ट को प्लेस करने का सबसे आसान तरीका यह है कि उसे आखिरी हिट टेस्ट के दौरान रेटिकल की जगह पर ड्रॉ किया जाए.
function onSelect(event) {
if (reticle.visible) {
// The reticle should already be positioned at the latest hit point,
// so we can just use its matrix to save an unnecessary call to
// event.frame.getHitTestResults.
addARObjectAt(reticle.matrix);
}
}
नतीजा
इसे समझने का सबसे अच्छा तरीका है कि सैंपल कोड को देखें या कोड लैब को आज़माएं. मुझे उम्मीद है कि मैंने आपको दोनों के बारे में काफ़ी जानकारी दी है.
हम इमर्सिव वेब एपीआई बनाने के काम को अभी खत्म नहीं कर रहे हैं. जैसे-जैसे हम आगे बढ़ेंगे, हम यहां नए लेख पब्लिश करेंगे.