বাস্তব-বিশ্বের দৃশ্যে ভার্চুয়াল বস্তুর অবস্থান

হিট টেস্ট API আপনাকে একটি বাস্তব-বিশ্বের দৃশ্যে ভার্চুয়াল আইটেমগুলিকে অবস্থান করতে দেয়৷

জো মেডলি
Joe Medley

ওয়েবএক্সআর ডিভাইস এপিআই ক্রোম 79-এ শেষ পতনে পাঠানো হয়েছে। তখন বলা হয়েছে, ক্রোমের এপিআই বাস্তবায়নের কাজ চলছে। কিছু কাজ শেষ হওয়ার ঘোষণা দিয়ে ক্রোম খুশি। Chrome 81 এ, দুটি নতুন বৈশিষ্ট্য এসেছে:

এই নিবন্ধটি WebXR হিট টেস্ট এপিআই কভার করে, একটি বাস্তব-বিশ্বের ক্যামেরা ভিউতে ভার্চুয়াল বস্তু স্থাপনের একটি মাধ্যম।

এই নিবন্ধে আমি অনুমান করছি আপনি ইতিমধ্যেই জানেন কিভাবে একটি অগমেন্টেড রিয়েলিটি সেশন তৈরি করতে হয় এবং আপনি জানেন কিভাবে একটি ফ্রেম লুপ চালাতে হয়। আপনি যদি এই ধারণাগুলির সাথে পরিচিত না হন তবে আপনার এই সিরিজের আগের নিবন্ধগুলি পড়া উচিত।

নিমজ্জিত AR সেশনের নমুনা

এই নিবন্ধের কোডটি ইমারসিভ ওয়েব ওয়ার্কিং গ্রুপের হিট টেস্ট নমুনা ( ডেমো , উৎস ) এর উপর ভিত্তি করে, কিন্তু এর সাথে অভিন্ন নয়। এই উদাহরণটি আপনাকে বাস্তব জগতের উপরিভাগে ভার্চুয়াল সূর্যমুখী স্থাপন করতে দেয়।

আপনি যখন প্রথম অ্যাপটি খুলবেন, আপনি মাঝখানে একটি বিন্দু সহ একটি নীল বৃত্ত দেখতে পাবেন। বিন্দু হল আপনার ডিভাইস থেকে পরিবেশের বিন্দু পর্যন্ত একটি কাল্পনিক রেখার মধ্যে ছেদ। আপনি ডিভাইসটি সরানোর সাথে সাথে এটি চলে যায়। এটি যখন ছেদ বিন্দু খুঁজে পায়, এটি মেঝে, টেবিলের শীর্ষ এবং দেয়ালের মতো পৃষ্ঠগুলিতে স্ন্যাপ করে বলে মনে হয়। এটি এটি করে কারণ হিট টেস্টিং ছেদ বিন্দুর অবস্থান এবং অভিযোজন প্রদান করে, কিন্তু সারফেস সম্পর্কে কিছুই নয়।

এই বৃত্তটিকে একটি রেটিকল বলা হয়, যা একটি অস্থায়ী চিত্র যা একটি বস্তুকে বর্ধিত বাস্তবতায় স্থাপন করতে সহায়তা করে। আপনি যদি স্ক্রীনে ট্যাপ করেন, আপনি যেখানেই স্ক্রীনে ট্যাপ করেছেন তা নির্বিশেষে রেটিকল পয়েন্টের রেটিকল অবস্থান এবং ওরিয়েন্টেশনে একটি সূর্যমুখী পৃষ্ঠে স্থাপন করা হয়। রেটিকল আপনার ডিভাইসের সাথে চলতে থাকে।

একটি দেয়ালে রেন্ডার করা একটি জালিকা, ল্যাক্স বা কঠোর তাদের প্রসঙ্গের উপর নির্ভর করে
রেটিকল হল একটি অস্থায়ী চিত্র যা একটি বস্তুকে বর্ধিত বাস্তবতায় স্থাপন করতে সহায়তা করে।

রেটিকল তৈরি করুন

আপনাকে অবশ্যই রেটিকল ইমেজ তৈরি করতে হবে কারণ এটি ব্রাউজার বা API দ্বারা সরবরাহ করা হয় না। এটি লোড করা এবং আঁকার পদ্ধতিটি কাঠামো নির্দিষ্ট। আপনি যদি সরাসরি 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
});

একটি অধিবেশন প্রবেশ

পূর্ববর্তী নিবন্ধগুলিতে আমি একটি XR সেশনে প্রবেশের জন্য কোড উপস্থাপন করেছি। আমি কিছু সংযোজন সহ নীচে এটির একটি সংস্করণ দেখিয়েছি। প্রথমে আমি 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
}

AR-তে যেকোন কিছু আঁকতে, আমাকে জানতে হবে দর্শক কোথায় এবং তারা কোথায় খুঁজছে। তাই আমি পরীক্ষা করি যে 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
}

একটি বস্তু স্থাপন

ব্যবহারকারী যখন স্ক্রীন ট্যাপ করেন তখন একটি বস্তু AR-তে রাখা হয়। আমি ইতিমধ্যে সেশনে একটি 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);
  }
}

উপসংহার

এটিতে একটি হ্যান্ডেল পাওয়ার সর্বোত্তম উপায় হল নমুনা কোডের মাধ্যমে ধাপে ধাপে যাওয়া বা কোডল্যাব চেষ্টা করা। আমি আশা করি আমি আপনাকে যথেষ্ট ব্যাকগ্রাউন্ড দিয়েছি যাতে উভয়ের অর্থ বোঝা যায়।

আমরা ইমারসিভ ওয়েব API তৈরি করিনি, দীর্ঘ শট দিয়ে নয়। আমরা অগ্রগতির সাথে সাথে এখানে নতুন নিবন্ধ প্রকাশ করব।

আনস্প্ল্যাশে ড্যানিয়েল ফ্র্যাঙ্কের ছবি