ফ্রেম লুপ সম্পর্কে সবকিছু
সম্প্রতি আমি ‘ভার্চুয়াল রিয়েলিটি ওয়েবে আসছে’ শিরোনামে একটি প্রবন্ধ প্রকাশ করেছি, যেখানে WebXR ডিভাইস এপিআই (API)-এর পেছনের মৌলিক ধারণাগুলো তুলে ধরা হয়েছে। আমি একটি এক্সআর (XR) সেশনের অনুরোধ করা, তাতে প্রবেশ করা এবং তা শেষ করার নির্দেশনাও দিয়েছি।
এই নিবন্ধে ফ্রেম লুপের বর্ণনা দেওয়া হয়েছে, যা হলো ইউজার-এজেন্ট দ্বারা নিয়ন্ত্রিত একটি অসীম লুপ, যেখানে স্ক্রিনে বারবার কন্টেন্ট আঁকা হয়। কন্টেন্টগুলো ফ্রেম নামক বিচ্ছিন্ন ব্লকে আঁকা হয়। ফ্রেমগুলোর এই ধারাবাহিকতা গতির একটি বিভ্রম তৈরি করে।
এই নিবন্ধটি যা নয়
একটি WebXR অ্যাপে ফ্রেম লুপ চলাকালীন কন্টেন্ট রেন্ডার করার একমাত্র উপায় হলো WebGL এবং WebGL2। সৌভাগ্যবশত, অনেক ফ্রেমওয়ার্ক WebGL এবং WebGL2-এর উপরে একটি অ্যাবস্ট্রাকশন লেয়ার প্রদান করে। এই ধরনের ফ্রেমওয়ার্কগুলোর মধ্যে রয়েছে three.js , babylonjs , এবং PlayCanvas , অন্যদিকে A-Frame এবং React 360 ডিজাইন করা হয়েছিল WebXR-এর সাথে ইন্টারঅ্যাক্ট করার জন্য।
এই নিবন্ধটি ইমারসিভ ওয়েব ওয়ার্কিং গ্রুপের ইমারসিভ ভিআর সেশন স্যাম্পল ( ডেমো , সোর্স ) ব্যবহার করে একটি ফ্রেম লুপের মূল বিষয়গুলো ব্যাখ্যা করে। আপনি যদি WebGL বা এর কোনো একটি ফ্রেমওয়ার্ক নিয়ে গভীরভাবে জানতে চান, তবে অনলাইনে রিসোর্সের একটি ক্রমবর্ধমান তালিকা রয়েছে।
খেলোয়াড়রা এবং খেলাটি
ফ্রেম লুপ বোঝার চেষ্টা করতে গিয়ে আমি বারবার খুঁটিনাটি বিবরণে হারিয়ে যাচ্ছিলাম। এখানে অনেকগুলো অবজেক্ট কাজ করে, এবং সেগুলোর মধ্যে কয়েকটির নামকরণ করা হয়েছে শুধুমাত্র অন্য অবজেক্টের রেফারেন্স প্রপার্টির মাধ্যমে। বিষয়টি আপনার কাছে পরিষ্কারভাবে তুলে ধরার জন্য, আমি অবজেক্টগুলোর বর্ণনা দেব, যেগুলোকে আমি 'প্লেয়ার' বলছি। এরপর আমি বর্ণনা করব তারা কীভাবে একে অপরের সাথে মিথস্ক্রিয়া করে, যেটাকে আমি 'গেম' বলছি।
খেলোয়াড়রা
XRViewerPose
পোজ হলো ত্রিমাত্রিক স্থানে কোনো কিছুর অবস্থান এবং অভিমুখ। ভিউয়ার এবং ইনপুট ডিভাইস উভয়েরই একটি পোজ থাকে, কিন্তু এখানে আমরা ভিউয়ারের পোজ নিয়ে আলোচনা করব। ভিউয়ার এবং ইনপুট ডিভাইস উভয়ের পোজে একটি transform অ্যাট্রিবিউট থাকে, যা অরিজিনের সাপেক্ষে এর অবস্থানকে একটি ভেক্টর হিসেবে এবং এর অভিমুখকে একটি কোয়াটারনিয়ন হিসেবে বর্ণনা করে। XRSession.requestReferenceSpace() কল করার সময় অনুরোধকৃত রেফারেন্স স্পেস টাইপের উপর ভিত্তি করে অরিজিন নির্দিষ্ট করা হয়।
রেফারেন্স স্পেস ব্যাখ্যা করতে কিছুটা সময় লাগে। আমি অগমেন্টেড রিয়েলিটি অংশে এ বিষয়ে বিস্তারিত আলোচনা করেছি। এই নিবন্ধের ভিত্তি হিসেবে আমি যে নমুনাটি ব্যবহার করছি, তাতে একটি 'local' রেফারেন্স স্পেস ব্যবহৃত হয়েছে। এর অর্থ হলো, সেশন তৈরির সময় দর্শকের অবস্থানেই এর মূলবিন্দুটি থাকে, যেখানে কোনো সুনির্দিষ্ট তল নেই এবং প্ল্যাটফর্ম ভেদে এর সঠিক অবস্থান ভিন্ন হতে পারে।
এক্সআরভিউ
একটি ভিউ ভার্চুয়াল দৃশ্যটি পর্যবেক্ষণকারী একটি ক্যামেরার সাথে সঙ্গতিপূর্ণ। একটি ভিউ-এর একটি transform অ্যাট্রিবিউটও থাকে, যা ভেক্টর হিসেবে এর অবস্থান এবং এর অভিমুখ বর্ণনা করে। এগুলো ভেক্টর/কোয়াটারনিয়ন জোড়া এবং একটি সমতুল্য ম্যাট্রিক্স উভয় রূপেই সরবরাহ করা হয়; আপনার কোডের জন্য যেটি সবচেয়ে উপযুক্ত, তার উপর নির্ভর করে আপনি যেকোনো একটি উপস্থাপনা ব্যবহার করতে পারেন। প্রতিটি ভিউ একটি ডিসপ্লে বা ডিসপ্লের একটি অংশের সাথে সঙ্গতিপূর্ণ, যা কোনো ডিভাইস দর্শকের কাছে চিত্রাবলী উপস্থাপন করতে ব্যবহার করে। XRViewerPose অবজেক্ট থেকে XRView অবজেক্টগুলো একটি অ্যারেতে ফেরত আসে। অ্যারেতে ভিউ-এর সংখ্যা বিভিন্ন হতে পারে। মোবাইল ডিভাইসে একটি AR দৃশ্যের একটি ভিউ থাকে, যা ডিভাইসের স্ক্রিনটি ঢাকতে পারে বা নাও পারে। হেডসেটে সাধারণত দুটি ভিউ থাকে, প্রতিটি চোখের জন্য একটি করে।
XRWebGLLayer
লেয়ারগুলো বিটম্যাপ ইমেজের উৎস এবং ডিভাইসে সেই ইমেজগুলো কীভাবে রেন্ডার করা হবে তার বিবরণ প্রদান করে। এই বিবরণটি এই প্লেয়ারটি আসলে কী করে তা পুরোপুরি তুলে ধরে না। আমি এটিকে একটি ডিভাইস এবং একটি WebGLRenderingContext মধ্যে মধ্যস্থতাকারী হিসেবে ভাবতে শুরু করেছি। MDN-ও প্রায় একই দৃষ্টিভঙ্গি পোষণ করে এবং বলে যে এটি উভয়ের মধ্যে একটি 'সংযোগ স্থাপন করে'। সেই হিসেবে, এটি অন্যান্য প্লেয়ারগুলোতে অ্যাক্সেস প্রদান করে।
সাধারণত, WebGL অবজেক্টগুলো ২ডি এবং ৩ডি গ্রাফিক্স রেন্ডার করার জন্য অবস্থার তথ্য সংরক্ষণ করে।
WebGLFramebuffer
একটি ফ্রেমবাফার WebGLRenderingContext কে ছবির ডেটা সরবরাহ করে। XRWebGLLayer থেকে এটি নেওয়ার পর, আপনি এটিকে বর্তমান WebGLRenderingContext এ পাঠিয়ে দেন। bindFramebuffer() কল করা ছাড়া (এ বিষয়ে পরে আরও বলা হবে) আপনি কখনও সরাসরি এই অবজেক্টটি অ্যাক্সেস করবেন না। আপনি কেবল এটিকে XRWebGLLayer থেকে WebGLRenderingContext-এ পাঠিয়ে দেবেন।
এক্সআরভিউপোর্ট
একটি ভিউপোর্ট WebGLFramebuffer মধ্যে থাকা একটি আয়তাকার অঞ্চলের স্থানাঙ্ক ও মাত্রা প্রদান করে।
WebGLRenderingContext
রেন্ডারিং কনটেক্সট হলো ক্যানভাসে (যে স্থানে আমরা আঁকি) প্রবেশের একটি প্রোগ্রাম্যাটিক পথ। এটি করার জন্য একটি WebGLFramebuffer এবং একটি XRViewport উভয়েরই প্রয়োজন হয়।
XRWebGLLayer এবং WebGLRenderingContext এর মধ্যকার সম্পর্কটি লক্ষ্য করুন। একটি দর্শকের ডিভাইসকে এবং অন্যটি ওয়েব পেজকে নির্দেশ করে। WebGLFramebuffer এবং XRViewport পূর্ববর্তীটি থেকে পরবর্তীটিতে পাঠানো হয়।

XRWebGLLayer এবং WebGLRenderingContext এর মধ্যে সম্পর্কখেলাটি
এখন যেহেতু আমরা জানি খেলোয়াড়রা কারা, চলুন দেখি তারা কোন খেলাটি খেলে। এটি এমন একটি খেলা যা প্রতিটি ফ্রেমে নতুন করে শুরু হয়। মনে রাখবেন যে, ফ্রেমগুলো একটি ফ্রেম লুপের অংশ, যা অন্তর্নিহিত হার্ডওয়্যারের উপর নির্ভর করে একটি নির্দিষ্ট হারে ঘটে। ভিআর অ্যাপ্লিকেশনের জন্য প্রতি সেকেন্ডে ফ্রেমের সংখ্যা ৬০ থেকে ১৪৪-এর মধ্যে হতে পারে। অ্যান্ড্রয়েডের জন্য এআর প্রতি সেকেন্ডে ৩০ ফ্রেমে চলে। আপনার কোডে কোনো নির্দিষ্ট ফ্রেম রেট ধরে নেওয়া উচিত নয়।
ফ্রেম লুপের মৌলিক প্রক্রিয়াটি দেখতে এইরকম:
-
XRSession.requestAnimationFrame()কল করুন। এর জবাবে, ইউজার এজেন্ট আপনার দ্বারা সংজ্ঞায়িতXRFrameRequestCallbackকে আহ্বান করে। - আপনার কলব্যাক ফাংশনের ভিতরে:
- আবার
XRSession.requestAnimationFrame()কল করুন। - দর্শকের ভঙ্গিটি বুঝুন।
-
XRWebGLLayerথেকেWebGLFramebufferটিWebGLRenderingContextএ পাস ('bind') করুন। - প্রতিটি
XRViewঅবজেক্টের উপর পুনরাবৃত্তি করুন,XRWebGLLayerথেকে এরXRViewportপুনরুদ্ধার করুন এবং সেটিWebGLRenderingContextএ প্রেরণ করুন। - ফ্রেমবাফারে কিছু আঁকুন।
- আবার
যেহেতু আগের প্রবন্ধে ধাপ ১ এবং ২ক আলোচনা করা হয়েছে, তাই আমি ধাপ ২খ থেকে শুরু করব।
দর্শকের ভঙ্গিটি বুঝুন
এটা সম্ভবত বলার অপেক্ষা রাখে না। 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.
}
}
একটি ভিউয়ার পোজ আছে যা ব্যবহারকারীর সামগ্রিক অবস্থানকে বোঝায়, অর্থাৎ হয় দর্শকের মাথা অথবা ফোনের ক্যামেরা। এই পোজটি আপনার অ্যাপ্লিকেশনকে বলে দেয় যে দর্শকটি কোথায় আছে। প্রকৃত ইমেজ রেন্ডারিং-এর জন্য XRView অবজেক্ট ব্যবহার করা হয়, যা নিয়ে আমি একটু পরেই আলোচনা করব।
সামনে এগোনোর আগে, আমি পরীক্ষা করে দেখি যে ভিউয়ার পোজটি ফেরত এসেছে কিনা, যদি সিস্টেমটি ট্র্যাকিং হারিয়ে ফেলে বা গোপনীয়তার কারণে পোজটি ব্লক করে দেয়। ট্র্যাকিং হলো পরিবেশের সাপেক্ষে এক্সআর ডিভাইসটি এবং এর ইনপুট ডিভাইসগুলো কোথায় আছে, তা জানার ক্ষমতা। বিভিন্ন উপায়ে ট্র্যাকিং হারিয়ে যেতে পারে এবং এটি ব্যবহৃত ট্র্যাকিং পদ্ধতির উপর নির্ভর করে। উদাহরণস্বরূপ, যদি হেডসেট বা ফোনের ক্যামেরা ট্র্যাকিংয়ের জন্য ব্যবহার করা হয়, তবে কম বা আলোহীন পরিস্থিতিতে অথবা ক্যামেরাগুলো ঢাকা থাকলে ডিভাইসটি তার অবস্থান নির্ণয় করার ক্ষমতা হারাতে পারে।
গোপনীয়তার কারণে পোজ ব্লক করার একটি উদাহরণ হলো, যদি হেডসেটটি কোনো নিরাপত্তা ডায়ালগ, যেমন অনুমতির অনুরোধ, প্রদর্শন করে, তবে এই সময় ব্রাউজারটি অ্যাপ্লিকেশনকে পোজ সরবরাহ করা বন্ধ করে দিতে পারে। কিন্তু আমি ইতিমধ্যেই XRSession.requestAnimationFrame() কল করেছি, যাতে সিস্টেমটি পুনরুদ্ধার করতে পারলে ফ্রেম লুপটি চলতে থাকে। অন্যথায়, ইউজার এজেন্ট সেশনটি শেষ করে দেবে এবং end ইভেন্ট হ্যান্ডলারকে কল করবে।
একটি সংক্ষিপ্ত পথ পরিবর্তন
পরবর্তী ধাপের জন্য সেশন সেট-আপের সময় তৈরি করা অবজেক্টগুলোর প্রয়োজন। মনে করুন, আমি একটি ক্যানভাস তৈরি করেছিলাম এবং এটিকে একটি XR-উপযোগী ওয়েব জিএল রেন্ডারিং কনটেক্সট তৈরি করার নির্দেশ দিয়েছিলাম, যা আমি canvas.getContext() কল করে পেয়েছিলাম। সমস্ত ড্রয়িং করা হয় WebGL API, WebGL2 API, অথবা Three.js-এর মতো কোনো WebGL-ভিত্তিক ফ্রেমওয়ার্ক ব্যবহার করে। এই কনটেক্সটটি, XRWebGLLayer এর একটি নতুন ইনস্ট্যান্সের সাথে, updateRenderState() ব্যবহার করে সেশন অবজেক্টে পাস করা হয়েছিল।
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
}
}
}
WebGLRenderingContext-এ XRViewport অবজেক্টটি পাস করুন।
একটি 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 অবজেক্টটির নামকরণ নিয়ে আমার কয়েকজন সহকর্মীর সাথে বিতর্ক হয়েছিল। স্যাম্পল স্ক্রিপ্ট এবং বেশিরভাগ WebXR কোডে এই ভেরিয়েবলটিকে gl বলা হয়। যখন আমি স্যাম্পলগুলো বোঝার চেষ্টা করছিলাম, তখন আমি বারবার ভুলে যাচ্ছিলাম যে gl বলতে কী বোঝায়। আমি এর নাম দিয়েছি webGLRenContext যাতে শেখার সময় আপনাদের মনে থাকে যে এটি WebGLRenderingContext এর একটি ইনস্ট্যান্স।
এর কারণ হলো, gl ব্যবহার করলে মেথডের নামগুলো OpenGL ES 2.0 API-এর অনুরূপ হয়, যা কম্পাইলড ল্যাঙ্গুয়েজে ভিআর তৈরির জন্য ব্যবহৃত হয়। আপনি যদি OpenGL ব্যবহার করে ভিআর অ্যাপ লিখে থাকেন, তবে এই বিষয়টি আপনার কাছে স্পষ্ট মনে হবে; কিন্তু এই প্রযুক্তিতে সম্পূর্ণ নতুন হলে বিষয়টি বিভ্রান্তিকর হতে পারে।
ফ্রেমবাফারে কিছু আঁকুন
যদি আপনি সত্যিই উচ্চাকাঙ্ক্ষী হন, তবে সরাসরি WebGL ব্যবহার করতে পারেন, কিন্তু আমি তার পরামর্শ দিই না। উপরে তালিকাভুক্ত ফ্রেমওয়ার্কগুলোর মধ্যে একটি ব্যবহার করা অনেক বেশি সহজ।
উপসংহার
WebXR-এর আপডেট বা আর্টিকেল এখানেই শেষ নয়। আপনি MDN-এ WebXR-এর সমস্ত ইন্টারফেস এবং সদস্যদের একটি রেফারেন্স খুঁজে পেতে পারেন। ইন্টারফেসগুলোর আসন্ন উন্নয়ন সম্পর্কে জানতে, Chrome Status- এ প্রতিটি ফিচার অনুসরণ করুন।