ইমারসিভ অগমেন্টেড রিয়েলিটি অভিজ্ঞতার জন্য ARCore Depth API ব্যবহার করুন

১. শুরু করার আগে

ARCore হলো মোবাইল ডিভাইসে অগমেন্টেড রিয়েলিটি (AR) অ্যাপ তৈরির একটি প্ল্যাটফর্ম। বিভিন্ন API ব্যবহার করে, ARCore ব্যবহারকারীর ডিভাইসকে তার পরিবেশ পর্যবেক্ষণ ও সে সম্পর্কে তথ্য গ্রহণ করতে এবং সেই তথ্যের সাথে মিথস্ক্রিয়া করতে সক্ষম করে।

এই কোডল্যাবে, আপনি ARCore Depth API ব্যবহার করে একটি সহজ AR-সক্ষম অ্যাপ তৈরি করার প্রক্রিয়াটি শিখবেন।

পূর্বশর্ত

এই কোডল্যাবটি সেইসব ডেভেলপারদের জন্য লেখা হয়েছে, যাঁদের AR-এর মৌলিক ধারণা সম্পর্কে জ্ঞান রয়েছে।

তুমি কী তৈরি করবে

1a0236e93212210c.gif

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

  • ফোনে ডেপথ এপিআই সমর্থনের জন্য পরীক্ষা করা হচ্ছে
  • প্রতিটি ফ্রেমের জন্য গভীরতার চিত্র পুনরুদ্ধার করা হচ্ছে
  • গভীরতার তথ্য বিভিন্ন উপায়ে কল্পনা করা (উপরের অ্যানিমেশনটি দেখুন)
  • অক্লুশন ব্যবহার করে অ্যাপের বাস্তবতা বাড়াতে ডেপথের ব্যবহার
  • যেসব ফোন ডেপথ এপিআই (Depth API) সমর্থন করে না, সেগুলোকে কীভাবে সুন্দরভাবে পরিচালনা করতে হয় তা শেখা।

আপনার যা যা লাগবে

হার্ডওয়্যারের প্রয়োজনীয়তা

  • একটি সমর্থিত ARCore ডিভাইস , যা একটি USB কেবলের মাধ্যমে আপনার ডেভেলপমেন্ট মেশিনের সাথে সংযুক্ত। এই ডিভাইসটিকে অবশ্যই Depth API সমর্থন করতে হবে। অনুগ্রহ করে সমর্থিত ডিভাইসগুলির এই তালিকাটি দেখুন। Depth API শুধুমাত্র Android-এ উপলব্ধ।
  • এই ডিভাইসের জন্য ইউএসবি ডিবাগিং সক্রিয় করুন।

সফটওয়্যার প্রয়োজনীয়তা

২. ARCore এবং ডেপথ এপিআই

ডেপথ এপিআই একটি সমর্থিত ডিভাইসের আরজিবি ক্যামেরা ব্যবহার করে ডেপথ ম্যাপ (যাকে ডেপথ ইমেজও বলা হয়) তৈরি করে। ডেপথ ম্যাপ থেকে প্রাপ্ত তথ্য ব্যবহার করে আপনি বাস্তব জগতের বস্তুর সামনে বা পিছনে ভার্চুয়াল বস্তুগুলোকে নির্ভুলভাবে ফুটিয়ে তুলতে পারেন, যা ব্যবহারকারীকে এক নিমগ্ন ও বাস্তবসম্মত অভিজ্ঞতা প্রদান করে।

ARCore ডেপথ এপিআই, ARCore-এর সেশন দ্বারা প্রদত্ত প্রতিটি ফ্রেমের সাথে মিলে যাওয়া ডেপথ ইমেজ অ্যাক্সেস করার সুযোগ দেয়। প্রতিটি পিক্সেল ক্যামেরা থেকে পারিপার্শ্বিক পরিবেশের দূরত্বের পরিমাপ প্রদান করে, যা আপনার এআর অ্যাপে উন্নততর বাস্তববাদিতা নিয়ে আসে।

ডেপথ এপিআই-এর একটি মূল সক্ষমতা হলো অক্লুশন : অর্থাৎ ডিজিটাল বস্তুগুলোকে বাস্তব জগতের বস্তুর সাপেক্ষে নির্ভুলভাবে প্রদর্শিত হওয়ার ক্ষমতা। এর ফলে বস্তুগুলোকে এমন মনে হয়, যেন সেগুলো ব্যবহারকারীর সাথে একই পরিবেশে সত্যিই উপস্থিত রয়েছে।

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

৩. প্রস্তুত হন

ডেভেলপমেন্ট মেশিন সেট আপ করুন

  1. আপনার ARCore ডিভাইসটি USB কেবলের মাধ্যমে কম্পিউটারের সাথে সংযুক্ত করুন। নিশ্চিত করুন যে আপনার ডিভাইসটিতে USB ডিবাগিংয়ের অনুমতি রয়েছে
  2. একটি টার্মিনাল খুলুন এবং নিচে দেখানো অনুযায়ী adb devices চালান:
adb devices

List of devices attached
<DEVICE_SERIAL_NUMBER>    device

<DEVICE_SERIAL_NUMBER> আপনার ডিভাইসের জন্য একটি অনন্য স্ট্রিং হবে। চালিয়ে যাওয়ার আগে নিশ্চিত করুন যে আপনি ঠিক একটি ডিভাইস দেখতে পাচ্ছেন।

কোডটি ডাউনলোড এবং ইনস্টল করুন

  1. আপনি রিপোজিটরিটি ক্লোন করতে পারেন:
git clone https://github.com/googlecodelabs/arcore-depth

অথবা একটি ZIP ফাইল ডাউনলোড করে তা এক্সট্র্যাক্ট করুন:

  1. অ্যান্ড্রয়েড স্টুডিও চালু করুন এবং 'Open an existing Android Studio project'-এ ক্লিক করুন।
  2. উপরে ডাউনলোড করা ZIP ফাইলটি যেখানে এক্সট্র্যাক্ট করেছেন সেই ডিরেক্টরিটি খুঁজুন এবং depth_codelab_io2020 ডিরেক্টরিটি খুলুন।

এটি একাধিক মডিউল সহ একটি একক গ্রেডল প্রজেক্ট। যদি অ্যান্ড্রয়েড স্টুডিওর উপরের বাম দিকের প্রজেক্ট প্যানেটি আগে থেকেই প্রদর্শিত না থাকে, তাহলে ড্রপ-ডাউন মেনু থেকে প্রজেক্টস- এ ক্লিক করুন।

ফলাফলটি দেখতে এইরকম হবে:

এই প্রকল্পে নিম্নলিখিত মডিউলগুলো রয়েছে:

  • part0_work : স্টার্টার অ্যাপ। এই কোডল্যাবটি করার সময় আপনাকে এই মডিউলটিতে পরিবর্তন আনতে হবে।
  • part1 : পার্ট ১ সম্পন্ন করার পর আপনার সম্পাদনাগুলো কেমন দেখতে হবে তার রেফারেন্স কোড।
  • part2 : পার্ট ২ সম্পন্ন করলে রেফারেন্স কোডটি পাবেন।
  • part3 : পার্ট ৩ সম্পন্ন করলে রেফারেন্স কোডটি পাবেন।
  • part4_completed : অ্যাপটির চূড়ান্ত সংস্করণ। পার্ট ৪ এবং এই কোডল্যাবটি সম্পন্ন করার পর এটি আপনার রেফারেন্স কোড হিসেবে ব্যবহার করুন।

আপনি part0_work মডিউলে কাজ করবেন। কোডল্যাবের প্রতিটি অংশের সম্পূর্ণ সমাধানও রয়েছে। প্রতিটি মডিউল একটি বিল্ডযোগ্য অ্যাপ।

৪. স্টার্টার অ্যাপটি চালান

  1. রান > রান... > 'part0_work'- এ ক্লিক করুন। প্রদর্শিত সিলেক্ট ডিপ্লয়মেন্ট টার্গেট ডায়ালগ বক্সে, আপনার ডিভাইসটি কানেক্টেড ডিভাইস-এর অধীনে তালিকাভুক্ত থাকা উচিত।
  2. আপনার ডিভাইসটি নির্বাচন করুন এবং OK- তে ক্লিক করুন। অ্যান্ড্রয়েড স্টুডিও প্রাথমিক অ্যাপটি তৈরি করে আপনার ডিভাইসে চালাবে।
  3. অ্যাপটি ক্যামেরার অনুমতি চাইবে। চালিয়ে যেতে 'অনুমতি দিন' -এ ট্যাপ করুন।

c5ef65f7a1da0d9.png

অ্যাপটি কীভাবে ব্যবহার করবেন

  1. অ্যাপটিকে একটি প্লেন খুঁজে পেতে সাহায্য করার জন্য ডিভাইসটি নাড়াচাড়া করুন । নিচের বার্তাটি নির্দেশ করে কখন নড়াচড়া চালিয়ে যেতে হবে।
  2. অ্যাঙ্কর স্থাপন করতে প্লেনের যেকোনো জায়গায় ট্যাপ করুন । যেখানে অ্যাঙ্করটি স্থাপন করা হয়েছে, সেখানে একটি অ্যান্ড্রয়েড চিত্র আঁকা হবে। এই অ্যাপটি আপনাকে একবারে শুধুমাত্র একটি অ্যাঙ্কর স্থাপন করার অনুমতি দেয়।
  3. ডিভাইসটি নাড়াচাড়া করুন । ডিভাইসটি নড়াচড়া করলেও চিত্রটিকে একই জায়গায় স্থির বলে মনে হওয়া উচিত।

বর্তমানে, আপনার অ্যাপটি খুবই সরল এবং বাস্তব জগতের দৃশ্যের জ্যামিতি সম্পর্কে তেমন কিছু জানে না।

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

6182cf62be13cd97.pngbeb0d327205f80ee.pnge4497751c6fad9a7.png

এই সমস্যাটি সমাধান করতে, আমরা এই অ্যাপটির নিমগ্নতা এবং বাস্তবতা উন্নত করার জন্য ডেপথ এপিআই (Depth API) ব্যবহার করব।

৫. ডেপথ এপিআই সমর্থিত কিনা তা যাচাই করুন (পর্ব ১)

ARCore ডেপথ এপিআই শুধুমাত্র সমর্থিত ডিভাইসগুলোর একটি নির্দিষ্ট অংশে চলে। এই ডেপথ ইমেজগুলো ব্যবহার করে কোনো অ্যাপে কার্যকারিতা যুক্ত করার আগে, আপনাকে প্রথমে নিশ্চিত করতে হবে যে অ্যাপটি একটি সমর্থিত ডিভাইসে চলছে।

DepthCodelabActivity তে একটি নতুন প্রাইভেট মেম্বার যোগ করুন যা একটি ফ্ল্যাগ হিসেবে কাজ করবে এবং বর্তমান ডিভাইসটি ডেপথ সাপোর্ট করে কিনা তা সংরক্ষণ করবে:

private boolean isDepthSupported;

আমরা onResume() ফাংশনের ভেতর থেকে এই ফ্ল্যাগটি পূরণ করতে পারি, যেখানে একটি নতুন সেশন তৈরি করা হয়।

বিদ্যমান কোডটি খুঁজুন:

// Creates the ARCore session.
session = new Session(/* context= */ this);

কোডটি আপডেট করে নিম্নরূপ করুন:

// Creates the ARCore session.
session = new Session(/* context= */ this);
Config config = session.getConfig();
isDepthSupported = session.isDepthModeSupported(Config.DepthMode.AUTOMATIC);
if (isDepthSupported) {
  config.setDepthMode(Config.DepthMode.AUTOMATIC);
} else {
  config.setDepthMode(Config.DepthMode.DISABLED);
}
session.configure(config);

এখন এআর সেশনটি যথাযথভাবে কনফিগার করা হয়েছে, এবং আপনার অ্যাপ জানে যে এটি গভীরতা-ভিত্তিক বৈশিষ্ট্যগুলি ব্যবহার করতে পারবে কি না।

এই সেশনের জন্য ডেপথ ব্যবহার করা হচ্ছে কি না, সেটাও ব্যবহারকারীকে জানিয়ে দেওয়া উচিত।

স্নাকবারে আরেকটি বার্তা যোগ করুন। এটি স্ক্রিনের নীচে প্রদর্শিত হবে:

// Add this line at the top of the file, with the other messages.
private static final String DEPTH_NOT_AVAILABLE_MESSAGE = "[Depth not supported on this device]";

onDrawFrame() ভিতরে, আপনি প্রয়োজন অনুযায়ী এই বার্তাটি প্রদর্শন করতে পারেন:

// Add this if-statement above messageSnackbarHelper.showMessage(this, messageToShow).
if (!isDepthSupported) {
  messageToShow += "\n" + DEPTH_NOT_AVAILABLE_MESSAGE;
}

আপনার অ্যাপটি যদি এমন কোনো ডিভাইসে চালানো হয় যা ডেপথ সাপোর্ট করে না, তাহলে আপনার যোগ করা বার্তাটি নীচে প্রদর্শিত হবে:

5c878a7c27833cb2.png

এরপরে, প্রতিটি ফ্রেমের জন্য ডেপথ এপিআই (Depth API) কল করতে এবং ডেপথ ইমেজ সংগ্রহ করতে আপনাকে অ্যাপটি আপডেট করতে হবে।

৬. গভীরতার চিত্রগুলি পুনরুদ্ধার করুন (পর্ব ২)

ডেপথ এপিআই ডিভাইসের পারিপার্শ্বিক পরিবেশের ত্রিমাত্রিক পর্যবেক্ষণ ধারণ করে এবং সেই ডেটা সহ একটি ডেপথ ইমেজ আপনার অ্যাপে ফেরত পাঠায়। ডেপথ ইমেজের প্রতিটি পিক্সেল ডিভাইস ক্যামেরা থেকে এর বাস্তব পরিবেশ পর্যন্ত দূরত্বের পরিমাপকে প্রতিনিধিত্ব করে।

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

প্রথমে, আপনার প্রজেক্টে একটি নতুন ক্লাস যোগ করুন।
DepthTextureHandler একটি প্রদত্ত ARCore ফ্রেমের জন্য ডেপথ ইমেজ পুনরুদ্ধার করার দায়িত্বে থাকে।
এই ফাইলটি যোগ করুন:

be8d14dfe9656551.png

src/main/java/com/google/ar/core/codelab/depth/DepthTextureHandler.java

package com.google.ar.core.codelab.depth;

import static android.opengl.GLES20.GL_CLAMP_TO_EDGE;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
import static android.opengl.GLES20.GL_TEXTURE_WRAP_S;
import static android.opengl.GLES20.GL_TEXTURE_WRAP_T;
import static android.opengl.GLES20.GL_UNSIGNED_BYTE;
import static android.opengl.GLES20.glBindTexture;
import static android.opengl.GLES20.glGenTextures;
import static android.opengl.GLES20.glTexImage2D;
import static android.opengl.GLES20.glTexParameteri;
import static android.opengl.GLES30.GL_LINEAR;
import static android.opengl.GLES30.GL_RG;
import static android.opengl.GLES30.GL_RG8;

import android.media.Image;
import com.google.ar.core.Frame;
import com.google.ar.core.exceptions.NotYetAvailableException;

/** Handle RG8 GPU texture containing a DEPTH16 depth image. */
public final class DepthTextureHandler {

  private int depthTextureId = -1;
  private int depthTextureWidth = -1;
  private int depthTextureHeight = -1;

  /**
   * Creates and initializes the depth texture. This method needs to be called on a
   * thread with a EGL context attached.
   */
  public void createOnGlThread() {
    int[] textureId = new int[1];
    glGenTextures(1, textureId, 0);
    depthTextureId = textureId[0];
    glBindTexture(GL_TEXTURE_2D, depthTextureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  }

  /**
   * Updates the depth texture with the content from acquireDepthImage16Bits().
   * This method needs to be called on a thread with an EGL context attached.
   */
  public void update(final Frame frame) {
    try {
      Image depthImage = frame.acquireDepthImage16Bits();
      depthTextureWidth = depthImage.getWidth();
      depthTextureHeight = depthImage.getHeight();
      glBindTexture(GL_TEXTURE_2D, depthTextureId);
      glTexImage2D(
          GL_TEXTURE_2D,
          0,
          GL_RG8,
          depthTextureWidth,
          depthTextureHeight,
          0,
          GL_RG,
          GL_UNSIGNED_BYTE,
          depthImage.getPlanes()[0].getBuffer());
      depthImage.close();
    } catch (NotYetAvailableException e) {
      // This normally means that depth data is not available yet.
    }
  }

  public int getDepthTexture() {
    return depthTextureId;
  }

  public int getDepthWidth() {
    return depthTextureWidth;
  }

  public int getDepthHeight() {
    return depthTextureHeight;
  }
}

এখন আপনি DepthCodelabActivity তে এই ক্লাসের একটি ইনস্ট্যান্স যোগ করবেন, যাতে প্রতিটি ফ্রেমের জন্য ডেপথ ইমেজের একটি সহজে অ্যাক্সেসযোগ্য কপি আপনার কাছে থাকে।

DepthCodelabActivity.java তে, আমাদের নতুন ক্লাসের একটি ইনস্ট্যান্সকে প্রাইভেট মেম্বার ভেরিয়েবল হিসেবে যোগ করুন:

private final DepthTextureHandler depthTexture = new DepthTextureHandler();

এরপরে, এই টেক্সচারটিকে ইনিশিয়ালাইজ করার জন্য onSurfaceCreated() মেথডটি আপডেট করুন, যাতে এটি আমাদের GPU শেডারগুলো দ্বারা ব্যবহারযোগ্য হয়:

// Put this at the top of the "try" block in onSurfaceCreated().
depthTexture.createOnGlThread();

অবশেষে, আপনি প্রতিটি ফ্রেমে এই টেক্সচারটিকে সর্বশেষ ডেপথ ইমেজ দিয়ে পূরণ করতে চাইবেন, যা session থেকে প্রাপ্ত সর্বশেষ ফ্রেমে আপনার উপরে তৈরি করা update() মেথডটি কল করার মাধ্যমে করা যেতে পারে।
যেহেতু এই অ্যাপটিতে ডেপথ সাপোর্ট ঐচ্ছিক, তাই শুধুমাত্র ডেপথ ব্যবহার করলেই এই কলটি ব্যবহার করুন।

// Add this just after "frame" is created inside onDrawFrame().
if (isDepthSupported) {
  depthTexture.update(frame);
}

এখন আপনার কাছে একটি ডেপথ ইমেজ আছে যা প্রতিটি ফ্রেমে আপডেট হয়। এটি আপনার শেডারগুলোতে ব্যবহারের জন্য প্রস্তুত।

তবে, অ্যাপটির আচরণে এখনও কোনো পরিবর্তন আসেনি। এখন আপনি আপনার অ্যাপকে আরও উন্নত করতে ডেপথ ইমেজ ব্যবহার করবেন।

৭. ডেপথ ইমেজ রেন্ডার করুন (পর্ব ৩)

এখন আপনার কাছে কাজ করার জন্য একটি ডেপথ ইমেজ আছে, আপনি দেখতে চাইবেন এটি কেমন দেখায়। এই অংশে, আপনি প্রতিটি ফ্রেমের জন্য ডেপথ রেন্ডার করতে অ্যাপটিতে একটি বাটন যোগ করবেন।

নতুন শেডার যোগ করুন

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

একটি নতুন .vert শেডার যোগ করুন

অ্যান্ড্রয়েড স্টুডিওতে:

  1. প্রথমে, src/main/assets/shaders/ ডিরেক্টরিতে নতুন .vert এবং .frag শেডারগুলো যোগ করুন।
  2. শেডার ডিরেক্টরিতে ডান-ক্লিক করুন
  3. নতুন -> ফাইল নির্বাচন করুন
  4. এর নাম দিন background_show_depth_map.vert
  5. এটিকে একটি টেক্সট ফাইল হিসেবে সেট করুন।

নতুন ফাইলটিতে নিম্নলিখিত কোডটি যোগ করুন:

src/main/assets/shaders/background_show_depth_map.vert

attribute vec4 a_Position;
attribute vec2 a_TexCoord;

varying vec2 v_TexCoord;

void main() {
   v_TexCoord = a_TexCoord;
   gl_Position = a_Position;
}

একই ডিরেক্টরিতে ফ্র্যাগমেন্ট শেডারটি তৈরি করতে উপরের ধাপগুলো পুনরাবৃত্তি করুন এবং এটির নাম দিন background_show_depth_map.frag

এই নতুন ফাইলে নিম্নলিখিত কোডটি যোগ করুন:

src/main/assets/shaders/background_show_depth_map.frag

precision mediump float;
uniform sampler2D u_Depth;
varying vec2 v_TexCoord;
const highp float kMaxDepth = 20000.0; // In millimeters.

float GetDepthMillimeters(vec4 depth_pixel_value) {
  return 255.0 * (depth_pixel_value.r + depth_pixel_value.g * 256.0);
}

// Returns an interpolated color in a 6 degree polynomial interpolation.
vec3 GetPolynomialColor(in float x,
  in vec4 kRedVec4, in vec4 kGreenVec4, in vec4 kBlueVec4,
  in vec2 kRedVec2, in vec2 kGreenVec2, in vec2 kBlueVec2) {
  // Moves the color space a little bit to avoid pure red.
  // Removes this line for more contrast.
  x = clamp(x * 0.9 + 0.03, 0.0, 1.0);
  vec4 v4 = vec4(1.0, x, x * x, x * x * x);
  vec2 v2 = v4.zw * v4.z;
  return vec3(
    dot(v4, kRedVec4) + dot(v2, kRedVec2),
    dot(v4, kGreenVec4) + dot(v2, kGreenVec2),
    dot(v4, kBlueVec4) + dot(v2, kBlueVec2)
  );
}

// Returns a smooth Percept colormap based upon the Turbo colormap.
vec3 PerceptColormap(in float x) {
  const vec4 kRedVec4 = vec4(0.55305649, 3.00913185, -5.46192616, -11.11819092);
  const vec4 kGreenVec4 = vec4(0.16207513, 0.17712472, 15.24091500, -36.50657960);
  const vec4 kBlueVec4 = vec4(-0.05195877, 5.18000081, -30.94853351, 81.96403246);
  const vec2 kRedVec2 = vec2(27.81927491, -14.87899417);
  const vec2 kGreenVec2 = vec2(25.95549545, -5.02738237);
  const vec2 kBlueVec2 = vec2(-86.53476570, 30.23299484);
  const float kInvalidDepthThreshold = 0.01;
  return step(kInvalidDepthThreshold, x) *
         GetPolynomialColor(x, kRedVec4, kGreenVec4, kBlueVec4,
                            kRedVec2, kGreenVec2, kBlueVec2);
}

void main() {
  vec4 packed_depth = texture2D(u_Depth, v_TexCoord.xy);
  highp float depth_mm = GetDepthMillimeters(packed_depth);
  highp float normalized_depth = depth_mm / kMaxDepth;
  vec4 depth_color = vec4(PerceptColormap(normalized_depth), 1.0);
  gl_FragColor = depth_color;
}

এরপরে, src/main/java/com/google/ar/core/codelab/common/rendering/BackgroundRenderer.java তে অবস্থিত BackgroundRenderer ক্লাসটিকে এই নতুন শেডারগুলো ব্যবহার করার জন্য আপডেট করুন।

ক্লাসের শীর্ষে শেডারগুলিতে ফাইল পাথগুলি যোগ করুন:

// Add these under the other shader names at the top of the class.
private static final String DEPTH_VERTEX_SHADER_NAME = "shaders/background_show_depth_map.vert";
private static final String DEPTH_FRAGMENT_SHADER_NAME = "shaders/background_show_depth_map.frag";

BackgroundRenderer ক্লাসে আরও মেম্বার ভেরিয়েবল যোগ করুন, যেহেতু এটি দুটি শেডার চালাবে:

// Add to the top of file with the rest of the member variables.
private int depthProgram;
private int depthTextureParam;
private int depthTextureId = -1;
private int depthQuadPositionParam;
private int depthQuadTexCoordParam;

এই ক্ষেত্রগুলি পূরণ করার জন্য একটি নতুন পদ্ধতি যোগ করুন:

// Add this method below createOnGlThread().
public void createDepthShaders(Context context, int depthTextureId) throws IOException {
  int vertexShader =
      ShaderUtil.loadGLShader(
          TAG, context, GLES20.GL_VERTEX_SHADER, DEPTH_VERTEX_SHADER_NAME);
  int fragmentShader =
      ShaderUtil.loadGLShader(
          TAG, context, GLES20.GL_FRAGMENT_SHADER, DEPTH_FRAGMENT_SHADER_NAME);

  depthProgram = GLES20.glCreateProgram();
  GLES20.glAttachShader(depthProgram, vertexShader);
  GLES20.glAttachShader(depthProgram, fragmentShader);
  GLES20.glLinkProgram(depthProgram);
  GLES20.glUseProgram(depthProgram);
  ShaderUtil.checkGLError(TAG, "Program creation");

  depthTextureParam = GLES20.glGetUniformLocation(depthProgram, "u_Depth");
  ShaderUtil.checkGLError(TAG, "Program parameters");

  depthQuadPositionParam = GLES20.glGetAttribLocation(depthProgram, "a_Position");
  depthQuadTexCoordParam = GLES20.glGetAttribLocation(depthProgram, "a_TexCoord");

  this.depthTextureId = depthTextureId;
}

এই পদ্ধতিটি যোগ করুন, যা প্রতিটি ফ্রেমে এই শেডারগুলি দিয়ে আঁকতে ব্যবহৃত হয়:

// Put this at the bottom of the file.
public void drawDepth(@NonNull Frame frame) {
  if (frame.hasDisplayGeometryChanged()) {
    frame.transformCoordinates2d(
        Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
        quadCoords,
        Coordinates2d.TEXTURE_NORMALIZED,
        quadTexCoords);
  }

  if (frame.getTimestamp() == 0 || depthTextureId == -1) {
    return;
  }

  // Ensure position is rewound before use.
  quadTexCoords.position(0);

  // No need to test or write depth, the screen quad has arbitrary depth, and is expected
  // to be drawn first.
  GLES20.glDisable(GLES20.GL_DEPTH_TEST);
  GLES20.glDepthMask(false);

  GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
  GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, depthTextureId);
  GLES20.glUseProgram(depthProgram);
  GLES20.glUniform1i(depthTextureParam, 0);

  // Set the vertex positions and texture coordinates.
  GLES20.glVertexAttribPointer(
        depthQuadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadCoords);
  GLES20.glVertexAttribPointer(
        depthQuadTexCoordParam, TEXCOORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadTexCoords);

  // Draws the quad.
  GLES20.glEnableVertexAttribArray(depthQuadPositionParam);
  GLES20.glEnableVertexAttribArray(depthQuadTexCoordParam);
  GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
  GLES20.glDisableVertexAttribArray(depthQuadPositionParam);
  GLES20.glDisableVertexAttribArray(depthQuadTexCoordParam);

  // Restore the depth state for further drawing.
  GLES20.glDepthMask(true);
  GLES20.glEnable(GLES20.GL_DEPTH_TEST);

  ShaderUtil.checkGLError(TAG, "BackgroundRendererDraw");
}

একটি টগল বোতাম যোগ করুন

এখন যেহেতু আপনার ডেপথ ম্যাপ রেন্ডার করার ক্ষমতা আছে, এটি ব্যবহার করুন! একটি বাটন যোগ করুন যা এই রেন্ডারিংটি চালু এবং বন্ধ করবে।

DepthCodelabActivity ফাইলের শীর্ষে, বাটনটি ব্যবহারের জন্য একটি ইম্পোর্ট যোগ করুন:

import android.widget.Button;

ডেপথ-রেন্ডারিং টগল করা আছে কিনা তা নির্দেশ করার জন্য একটি বুলিয়ান মেম্বার যোগ করতে ক্লাসটি আপডেট করুন: (এটি ডিফল্টরূপে বন্ধ থাকে):

private boolean showDepthMap = false;

এরপরে, onCreate() মেথডের শেষে showDepthMap বুলিয়ানটি নিয়ন্ত্রণকারী বাটনটি যোগ করুন:

final Button toggleDepthButton = (Button) findViewById(R.id.toggle_depth_button);
    toggleDepthButton.setOnClickListener(
        view -> {
          if (isDepthSupported) {
            showDepthMap = !showDepthMap;
            toggleDepthButton.setText(showDepthMap ? R.string.hide_depth : R.string.show_depth);
          } else {
            showDepthMap = false;
            toggleDepthButton.setText(R.string.depth_not_available);
          }
        });

এই স্ট্রিংগুলি res/values/strings.xml এ যোগ করুন:

<string translatable="false" name="show_depth">Show Depth</string>
<string translatable="false" name="hide_depth">Hide Depth</string>
<string translatable="false" name="depth_not_available">Depth Not Available</string>

res/layout/activity_main.xml ফাইলে অ্যাপ লেআউটের নীচে এই বাটনটি যোগ করুন:

<Button
    android:id="@+id/toggle_depth_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="20dp"
    android:gravity="center"
    android:text="@string/show_depth"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"/>

বাটনটি এখন showDepthMap বুলিয়ানটির মান নিয়ন্ত্রণ করে। ডেপথ ম্যাপটি রেন্ডার হবে কি না, তা নিয়ন্ত্রণ করতে এই ফ্ল্যাগটি ব্যবহার করুন।

DepthCodelabActivity এর onDrawFrame() মেথডে ফিরে এসে, যোগ করুন:

// Add this snippet just under backgroundRenderer.draw(frame);
if (showDepthMap) {
  backgroundRenderer.drawDepth(frame);
}

onSurfaceCreated() ফাংশনে নিম্নলিখিত লাইনটি যোগ করে ডেপথ টেক্সচারটি backgroundRenderer এ পাঠান:

// Add to onSurfaceCreated() after backgroundRenderer.createonGlThread(/*context=*/ this);
backgroundRenderer.createDepthShaders(/*context=*/ this, depthTexture.getDepthTexture());

এখন আপনি স্ক্রিনের উপরের-ডানদিকের বাটনটি চেপে প্রতিটি ফ্রেমের ডেপথ ইমেজ দেখতে পারবেন।

ডেপথ এপিআই সমর্থন ছাড়াই চালানো হচ্ছে

ডেপথ এপিআই সাপোর্টের সাথে চলছে

[ঐচ্ছিক] চমৎকার গভীরতা অ্যানিমেশন

অ্যাপটি বর্তমানে সরাসরি ডেপথ ম্যাপ দেখায়। লাল পিক্সেলগুলো কাছের এলাকা নির্দেশ করে। নীল পিক্সেলগুলো দূরের এলাকা নির্দেশ করে।

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

প্রথমে background_show_depth_map.frag ফাইলের শীর্ষে এই ভেরিয়েবলগুলো যোগ করুন:

uniform float u_DepthRangeToRenderMm;
const float kDepthWidthToRenderMm = 350.0;
  • তারপর, শেডারের main() ফাংশনে ডেপথ ভ্যালু দিয়ে কোন পিক্সেলগুলো কভার করা হবে তা ফিল্টার করতে এই মানগুলো ব্যবহার করুন:
// Add this line at the end of main().
gl_FragColor.a = clamp(1.0 - abs((depth_mm - u_DepthRangeToRenderMm) / kDepthWidthToRenderMm), 0.0, 1.0);

এরপরে, এই শেডার প্যারামিটারগুলো বজায় রাখার জন্য BackgroundRenderer.java আপডেট করুন। ক্লাসটির শুরুতে নিম্নলিখিত ফিল্ডগুলো যোগ করুন:

private static final float MAX_DEPTH_RANGE_TO_RENDER_MM = 20000.0f;
private float depthRangeToRenderMm = 0.0f;
private int depthRangeToRenderMmParam;

এই প্যারামিটারগুলোকে শেডার প্রোগ্রামের সাথে মেলানোর জন্য createDepthShaders() ` মেথডের ভিতরে নিম্নলিখিত কোডটি যোগ করুন:

depthRangeToRenderMmParam = GLES20.glGetUniformLocation(depthProgram, "u_DepthRangeToRenderMm");
  • অবশেষে, আপনি ` drawDepth() ` মেথডের মধ্যে সময়ের সাথে সাথে এই পরিসরটি নিয়ন্ত্রণ করতে পারেন। নিম্নলিখিত কোডটি যোগ করুন, যা প্রতিবার একটি ফ্রেম আঁকার সময় এই পরিসরটি বাড়িয়ে দেয়:
// Enables alpha blending.
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

// Updates range each time draw() is called.
depthRangeToRenderMm += 50.0f;
if (depthRangeToRenderMm > MAX_DEPTH_RANGE_TO_RENDER_MM) {
  depthRangeToRenderMm = 0.0f;
}

// Passes latest value to the shader.
GLES20.glUniform1f(depthRangeToRenderMmParam, depthRangeToRenderMm);

এখন গভীরতাটি আপনার দৃশ্যের মধ্য দিয়ে প্রবাহিত একটি অ্যানিমেটেড স্পন্দন হিসাবে দৃশ্যমান হয়।

b846e4365d7b69b1.gif

পালসটিকে ধীর, দ্রুত, প্রশস্ত, সংকীর্ণ ইত্যাদি করার জন্য এখানে দেওয়া মানগুলি নির্দ্বিধায় পরিবর্তন করুন। এছাড়াও, গভীরতার তথ্য দেখানোর জন্য শেডার পরিবর্তন করার একেবারে নতুন উপায়গুলিও খুঁজে দেখতে পারেন!

৮. অক্লুশনের জন্য ডেপথ এপিআই ব্যবহার করুন (পর্ব ৪)

এখন আপনি আপনার অ্যাপে অবজেক্ট অক্লুশন পরিচালনা করবেন।

অক্লুশন বলতে বোঝায় সেই পরিস্থিতি, যখন ভার্চুয়াল বস্তুটি এবং ক্যামেরার মাঝে বাস্তব বস্তু থাকার কারণে সেটিকে সম্পূর্ণরূপে রেন্ডার করা যায় না। এআর অভিজ্ঞতাকে ইমারসিভ বা নিমগ্নকারী করে তোলার জন্য অক্লুশন ব্যবস্থাপনা অপরিহার্য।

রিয়েল টাইমে ভার্চুয়াল অবজেক্টগুলোকে সঠিকভাবে রেন্ডার করা অগমেন্টেড দৃশ্যের বাস্তবতা ও বিশ্বাসযোগ্যতা বাড়িয়ে তোলে। আরও উদাহরণের জন্য, ডেপথ এপিআই (Depth API) ব্যবহার করে রিয়েলিটি ব্লেন্ডিং-এর উপর আমাদের ভিডিওটি দেখুন।

এই অংশে, আপনি আপনার অ্যাপটি এমনভাবে আপডেট করবেন যাতে শুধুমাত্র ডেপথ (গভীরতা) উপলব্ধ থাকলেই ভার্চুয়াল অবজেক্ট অন্তর্ভুক্ত হয়।

নতুন অবজেক্ট শেডার যোগ করা

আগের বিভাগগুলোর মতোই, আপনি ডেপথ ইনফরমেশন সাপোর্ট করার জন্য নতুন শেডার যোগ করবেন। এবার আপনি বিদ্যমান অবজেক্ট শেডারগুলো কপি করে অক্লুশন কার্যকারিতা যোগ করতে পারেন।

অবজেক্ট শেডারগুলোর উভয় সংস্করণ রাখা গুরুত্বপূর্ণ, যাতে আপনার অ্যাপ রান-টাইমে ডেপথ সাপোর্ট করবে কি না, সে বিষয়ে সিদ্ধান্ত নিতে পারে।

src/main/assets/shaders ডিরেক্টরিতে object.vert এবং object.frag শেডার ফাইলগুলোর কপি তৈরি করুন।

  • object.vert ফাইলটিকে src/main/assets/shaders/occlusion_object.vert নামক গন্তব্য ফাইলে কপি করুন।
  • object.frag ফাইলটিকে src/main/assets/shaders/occlusion_object.frag নামক গন্তব্য ফাইলে কপি করুন।

occlusion_object.vert ফাইলের ভিতরে, main() ফাংশনের উপরে নিম্নলিখিত ভেরিয়েবলটি যোগ করুন:

varying vec3 v_ScreenSpacePosition;

main() ফাংশনের শেষে এই ভেরিয়েবলটি সেট করুন:

v_ScreenSpacePosition = gl_Position.xyz / gl_Position.w;

ফাইলের শুরুতে main() উপরে এই ভেরিয়েবলগুলো যোগ করে occlusion_object.frag আপডেট করুন:

varying vec3 v_ScreenSpacePosition;

uniform sampler2D u_Depth;
uniform mat3 u_UvTransform;
uniform float u_DepthTolerancePerMm;
uniform float u_OcclusionAlpha;
uniform float u_DepthAspectRatio;
  • গভীরতার তথ্য নিয়ে কাজ করা সহজ করার জন্য শেডারে main() ফাংশনের উপরে এই হেল্পার ফাংশনগুলো যোগ করুন:
float GetDepthMillimeters(in vec2 depth_uv) {
  // Depth is packed into the red and green components of its texture.
  // The texture is a normalized format, storing millimeters.
  vec3 packedDepthAndVisibility = texture2D(u_Depth, depth_uv).xyz;
  return dot(packedDepthAndVisibility.xy, vec2(255.0, 256.0 * 255.0));
}

// Returns linear interpolation position of value between min and max bounds.
// E.g., InverseLerp(1100, 1000, 2000) returns 0.1.
float InverseLerp(in float value, in float min_bound, in float max_bound) {
  return clamp((value - min_bound) / (max_bound - min_bound), 0.0, 1.0);
}

// Returns a value between 0.0 (not visible) and 1.0 (completely visible)
// Which represents how visible or occluded is the pixel in relation to the
// depth map.
float GetVisibility(in vec2 depth_uv, in float asset_depth_mm) {
  float depth_mm = GetDepthMillimeters(depth_uv);

  // Instead of a hard z-buffer test, allow the asset to fade into the
  // background along a 2 * u_DepthTolerancePerMm * asset_depth_mm
  // range centered on the background depth.
  float visibility_occlusion = clamp(0.5 * (depth_mm - asset_depth_mm) /
    (u_DepthTolerancePerMm * asset_depth_mm) + 0.5, 0.0, 1.0);

  // Depth close to zero is most likely invalid, do not use it for occlusions.
  float visibility_depth_near = 1.0 - InverseLerp(
      depth_mm, /*min_depth_mm=*/150.0, /*max_depth_mm=*/200.0);

  // Same for very high depth values.
  float visibility_depth_far = InverseLerp(
      depth_mm, /*min_depth_mm=*/17500.0, /*max_depth_mm=*/20000.0);

  float visibility =
    max(max(visibility_occlusion, u_OcclusionAlpha),
      max(visibility_depth_near, visibility_depth_far));

  return visibility;
}

এখন occlusion_object.frag ফাইলের main() ডেপথ-অ্যাওয়ার করে আপডেট করুন এবং অক্লুশন প্রয়োগ করুন। ফাইলটির একদম শেষে নিম্নলিখিত লাইনগুলো যোগ করুন:

const float kMToMm = 1000.0;
float asset_depth_mm = v_ViewPosition.z * kMToMm * -1.;
vec2 depth_uvs = (u_UvTransform * vec3(v_ScreenSpacePosition.xy, 1)).xy;
gl_FragColor.a *= GetVisibility(depth_uvs, asset_depth_mm);

এখন যেহেতু আপনার অবজেক্ট শেডারগুলোর একটি নতুন সংস্করণ আছে, আপনি রেন্ডারার কোডটি পরিবর্তন করতে পারেন।

অবজেক্ট অক্লুশন রেন্ডার করা

এরপরে src/main/java/com/google/ar/core/codelab/common/rendering/ObjectRenderer.java তে অবস্থিত ObjectRenderer ক্লাসটির একটি অনুলিপি তৈরি করুন।

  • ObjectRenderer ক্লাসটি নির্বাচন করুন
  • ডান ক্লিক > কপি
  • রেন্ডারিং ফোল্ডার নির্বাচন করুন
  • ডান ক্লিক > পেস্ট

7487ece853690c31.png

  • ক্লাসটির নাম পরিবর্তন করে OcclusionObjectRenderer রাখুন

760a4c80429170c2.png

নতুন, নাম পরিবর্তন করা ক্লাসটি এখন একই ফোল্ডারে দেখা যাবে:

9335c373dc60cd17.png

নতুন তৈরি করা OcclusionObjectRenderer.java ফাইলটি খুলুন এবং ফাইলের শীর্ষে থাকা শেডার পাথগুলো পরিবর্তন করুন:

private static final String VERTEX_SHADER_NAME = "shaders/occlusion_object.vert";
private static final String FRAGMENT_SHADER_NAME = "shaders/occlusion_object.frag";
  • ক্লাসের শীর্ষে অন্যান্য ভেরিয়েবলগুলোর সাথে এই গভীরতা-সম্পর্কিত মেম্বার ভেরিয়েবলগুলো যোগ করুন। ভেরিয়েবলগুলো অক্লুশন বর্ডারের তীক্ষ্ণতা সমন্বয় করবে।
// Shader location: depth texture
private int depthTextureUniform;

// Shader location: transform to depth uvs
private int depthUvTransformUniform;

// Shader location: depth tolerance property
private int depthToleranceUniform;

// Shader location: maximum transparency for the occluded part.
private int occlusionAlphaUniform;

private int depthAspectRatioUniform;

private float[] uvTransform = null;
private int depthTextureId;

ক্লাসের শুরুতে ডিফল্ট মান সহ এই মেম্বার ভেরিয়েবলগুলো তৈরি করুন:

// These values will be changed each frame based on the distance to the object.
private float depthAspectRatio = 0.0f;
private final float depthTolerancePerMm = 0.015f;
private final float occlusionsAlpha = 0.0f;

createOnGlThread() মেথডে শেডারের জন্য ইউনিফর্ম প্যারামিটারগুলো ইনিশিয়ালাইজ করুন:

// Occlusions Uniforms.  Add these lines before the first call to ShaderUtil.checkGLError
// inside the createOnGlThread() method.
depthTextureUniform = GLES20.glGetUniformLocation(program, "u_Depth");
depthUvTransformUniform = GLES20.glGetUniformLocation(program, "u_UvTransform");
depthToleranceUniform = GLES20.glGetUniformLocation(program, "u_DepthTolerancePerMm");
occlusionAlphaUniform = GLES20.glGetUniformLocation(program, "u_OcclusionAlpha");
depthAspectRatioUniform = GLES20.glGetUniformLocation(program, "u_DepthAspectRatio");
  • প্রতিবার আঁকার সময় draw() মেথডটি আপডেট করে এই মানগুলি আপডেট করা নিশ্চিত করুন:
// Add after other GLES20.glUniform calls inside draw().
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, depthTextureId);
GLES20.glUniform1i(depthTextureUniform, 1);
GLES20.glUniformMatrix3fv(depthUvTransformUniform, 1, false, uvTransform, 0);
GLES20.glUniform1f(depthToleranceUniform, depthTolerancePerMm);
GLES20.glUniform1f(occlusionAlphaUniform, occlusionsAlpha);
GLES20.glUniform1f(depthAspectRatioUniform, depthAspectRatio);

রেন্ডারিং-এর সময় ব্লেন্ড-মোড চালু করতে draw() ফাংশনের মধ্যে নিম্নলিখিত লাইনগুলো যোগ করুন, যাতে ভার্চুয়াল অবজেক্টগুলো আড়াল হয়ে গেলেও সেগুলোতে স্বচ্ছতা প্রয়োগ করা যায়:

// Add these lines just below the code-block labeled "Enable vertex arrays"
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
// Add these lines just above the code-block labeled "Disable vertex arrays"
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDepthMask(true);
  • নিম্নলিখিত মেথডগুলো যোগ করুন যাতে OcclusionObjectRenderer এর কলাররা গভীরতার তথ্য প্রদান করতে পারে:
// Add these methods at the bottom of the OcclusionObjectRenderer class.
public void setUvTransformMatrix(float[] transform) {
  uvTransform = transform;
}

public void setDepthTexture(int textureId, int width, int height) {
  depthTextureId = textureId;
  depthAspectRatio = (float) width / (float) height;
}

বস্তুর অবরুদ্ধতা নিয়ন্ত্রণ করা

এখন যেহেতু আপনার একটি নতুন OcclusionObjectRenderer আছে, আপনি এটিকে আপনার DepthCodelabActivity তে যোগ করতে পারেন এবং কখন ও কীভাবে অক্লুশন রেন্ডারিং ব্যবহার করবেন তা বেছে নিতে পারেন।

অ্যাক্টিভিটিতে OcclusionObjectRenderer এর একটি ইনস্ট্যান্স যোগ করে এই লজিকটি সক্রিয় করুন, যাতে ObjectRenderer এবং OcclusionObjectRenderer উভয়ই DepthCodelabActivity এর সদস্য হয়:

// Add this include at the top of the file.
import com.google.ar.core.codelab.common.rendering.OcclusionObjectRenderer;
// Add this member just below the existing "virtualObject", so both are present.
private final OcclusionObjectRenderer occludedVirtualObject = new OcclusionObjectRenderer();
  • বর্তমান ডিভাইসটি ডেপথ এপিআই (Depth API) সমর্থন করে কিনা, তার উপর ভিত্তি করে আপনি এরপর নিয়ন্ত্রণ করতে পারবেন যে এই occludedVirtualObject কখন ব্যবহৃত হবে। onSurfaceCreated ) মেথডের ভিতরে, যেখানে virtualObject কনফিগার করা হয়েছে তার নিচে এই লাইনগুলো যোগ করুন:
if (isDepthSupported) {
  occludedVirtualObject.createOnGlThread(/*context=*/ this, "models/andy.obj", "models/andy.png");
  occludedVirtualObject.setDepthTexture(
     depthTexture.getDepthTexture(),
     depthTexture.getDepthWidth(),
     depthTexture.getDepthHeight());
  occludedVirtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);
}

যেসব ডিভাইসে ডেপথ সমর্থিত নয়, সেখানে occludedVirtualObject ইনস্ট্যান্সটি তৈরি হলেও অব্যবহৃত থাকে। ডেপথযুক্ত ফোনগুলিতে, উভয় সংস্করণই ইনিশিয়ালাইজ করা হয় এবং ড্রয়িং করার সময় রেন্ডারারের কোন সংস্করণটি ব্যবহার করা হবে, সে বিষয়ে রান-টাইমে সিদ্ধান্ত নেওয়া হয়।

onDrawFrame() মেথডের ভিতরে, বিদ্যমান কোডটি খুঁজুন:

virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, OBJECT_COLOR);

এই কোডটি নিম্নলিখিত কোড দিয়ে প্রতিস্থাপন করুন:

if (isDepthSupported) {
  occludedVirtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
  occludedVirtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, OBJECT_COLOR);
} else {
  virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
  virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, OBJECT_COLOR);
}

সবশেষে, নিশ্চিত করুন যে ডেপথ ইমেজটি আউটপুট রেন্ডারিং-এ সঠিকভাবে ম্যাপ করা হয়েছে। যেহেতু ডেপথ ইমেজের রেজোলিউশন এবং অ্যাস্পেক্ট রেশিও আপনার স্ক্রিনের থেকে ভিন্ন, তাই এর এবং ক্যামেরা ইমেজের টেক্সচার কোঅর্ডিনেট ভিন্ন হতে পারে।

  • ফাইলের শেষে getTextureTransformMatrix() হেল্পার মেথডটি যোগ করুন। এই মেথডটি একটি ট্রান্সফরমেশন ম্যাট্রিক্স রিটার্ন করে, যা প্রয়োগ করা হলে স্ক্রিন স্পেস UV-গুলো ক্যামেরা ফিড রেন্ডার করতে ব্যবহৃত কোয়াড টেক্সচার কোঅর্ডিনেটের সাথে সঠিকভাবে মিলে যায়। এটি ডিভাইসের ওরিয়েন্টেশনও বিবেচনায় রাখে।
private static float[] getTextureTransformMatrix(Frame frame) {
  float[] frameTransform = new float[6];
  float[] uvTransform = new float[9];
  // XY pairs of coordinates in NDC space that constitute the origin and points along the two
  // principal axes.
  float[] ndcBasis = {0, 0, 1, 0, 0, 1};

  // Temporarily store the transformed points into outputTransform.
  frame.transformCoordinates2d(
      Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
      ndcBasis,
      Coordinates2d.TEXTURE_NORMALIZED,
      frameTransform);

  // Convert the transformed points into an affine transform and transpose it.
  float ndcOriginX = frameTransform[0];
  float ndcOriginY = frameTransform[1];
  uvTransform[0] = frameTransform[2] - ndcOriginX;
  uvTransform[1] = frameTransform[3] - ndcOriginY;
  uvTransform[2] = 0;
  uvTransform[3] = frameTransform[4] - ndcOriginX;
  uvTransform[4] = frameTransform[5] - ndcOriginY;
  uvTransform[5] = 0;
  uvTransform[6] = ndcOriginX;
  uvTransform[7] = ndcOriginY;
  uvTransform[8] = 1;

  return uvTransform;
}

getTextureTransformMatrix() ফাংশনটি চালানোর জন্য ফাইলের শুরুতে নিম্নলিখিত ইম্পোর্টটি প্রয়োজন:

import com.google.ar.core.Coordinates2d;

যখনই স্ক্রিনের টেক্সচার পরিবর্তিত হয় (যেমন স্ক্রিন ঘোরানোর ক্ষেত্রে), আপনি এই টেক্সচার স্থানাঙ্কগুলির মধ্যে ট্রান্সফর্ম গণনা করতে চান। এই কার্যকারিতাটি গেটেড।

ফাইলের শীর্ষে নিম্নলিখিত ফ্ল্যাগটি যোগ করুন:

// Add this member at the top of the file.
private boolean calculateUVTransform = true;
  • onDrawFrame() ভিতরে, ফ্রেম এবং ক্যামেরা তৈরি হওয়ার পরে সংরক্ষিত ট্রান্সফরমেশনটি পুনরায় গণনা করার প্রয়োজন আছে কিনা তা পরীক্ষা করুন:
// Add these lines inside onDrawFrame() after frame.getCamera().
if (frame.hasDisplayGeometryChanged() || calculateUVTransform) {
  calculateUVTransform = false;
  float[] transform = getTextureTransformMatrix(frame);
  occludedVirtualObject.setUvTransformMatrix(transform);
}

এই পরিবর্তনগুলো করার ফলে, আপনি এখন ভার্চুয়াল অবজেক্ট অক্লুশন সহ অ্যাপটি চালাতে পারবেন!

আপনার অ্যাপটি এখন সব ফোনে নির্বিঘ্নে চলবে এবং সমর্থিত হলে স্বয়ংক্রিয়ভাবে ডেপথ-ফর-অক্লুশন ব্যবহার করবে।

ডেপথ এপিআই সমর্থন সহ চলমান অ্যাপ

ডেপথ এপিআই সমর্থন ছাড়া অ্যাপ চালানো

৯. [ঐচ্ছিক] অক্লুশনের মান উন্নত করুন

উপরে বর্ণিত গভীরতা-ভিত্তিক অবরুদ্ধকরণের পদ্ধতিটি সুস্পষ্ট সীমানাসহ অবরুদ্ধকরণ প্রদান করে। ক্যামেরা বস্তুটি থেকে যত দূরে সরে যায়, গভীরতার পরিমাপ তত কম নির্ভুল হতে পারে, যার ফলে দৃশ্যগত ত্রুটি দেখা দিতে পারে।

অক্লুশন টেস্টে অতিরিক্ত ব্লার যোগ করে আমরা এই সমস্যাটি প্রশমিত করতে পারি, যা লুকানো ভার্চুয়াল অবজেক্টগুলোর প্রান্তকে আরও মসৃণ করে তোলে।

occlusion_object.frag

occlusion_object.frag ফাইলের শীর্ষে নিম্নলিখিত ইউনিফর্ম ভেরিয়েবলটি যোগ করুন:

uniform float u_OcclusionBlurAmount;

শেডারের main() ফাংশনের ঠিক উপরে এই হেল্পার ফাংশনটি যোগ করুন, যা অক্লুশন স্যাম্পলিং-এ একটি কার্নেল ব্লার প্রয়োগ করে:

float GetBlurredVisibilityAroundUV(in vec2 uv, in float asset_depth_mm) {
  // Kernel used:
  // 0   4   7   4   0
  // 4   16  26  16  4
  // 7   26  41  26  7
  // 4   16  26  16  4
  // 0   4   7   4   0
  const float kKernelTotalWeights = 269.0;
  float sum = 0.0;

  vec2 blurriness = vec2(u_OcclusionBlurAmount,
                         u_OcclusionBlurAmount * u_DepthAspectRatio);

  float current = 0.0;

  current += GetVisibility(uv + vec2(-1.0, -2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, -2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-1.0, +2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, +2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-2.0, +1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+2.0, +1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-2.0, -1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+2.0, -1.0) * blurriness, asset_depth_mm);
  sum += current * 4.0;

  current = 0.0;
  current += GetVisibility(uv + vec2(-2.0, -0.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+2.0, +0.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+0.0, +2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-0.0, -2.0) * blurriness, asset_depth_mm);
  sum += current * 7.0;

  current = 0.0;
  current += GetVisibility(uv + vec2(-1.0, -1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, -1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-1.0, +1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, +1.0) * blurriness, asset_depth_mm);
  sum += current * 16.0;

  current = 0.0;
  current += GetVisibility(uv + vec2(+0.0, +1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-0.0, -1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-1.0, -0.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, +0.0) * blurriness, asset_depth_mm);
  sum += current * 26.0;

  sum += GetVisibility(uv , asset_depth_mm) * 41.0;

  return sum / kKernelTotalWeights;
}

main() ফাংশনে বিদ্যমান এই লাইনটি প্রতিস্থাপন করুন:

gl_FragColor.a *= GetVisibility(depth_uvs, asset_depth_mm);

এই লাইনটি দিয়ে:

gl_FragColor.a *= GetBlurredVisibilityAroundUV(depth_uvs, asset_depth_mm);

এই নতুন শেডার কার্যকারিতাটির সুবিধা নিতে রেন্ডারারটি আপডেট করুন।

OcclusionObjectRenderer.java

ক্লাসের শুরুতে নিম্নলিখিত মেম্বার ভেরিয়েবলগুলো যোগ করুন:

private int occlusionBlurUniform;
private final float occlusionsBlur = 0.01f;

createOnGlThread মেথডের ভিতরে নিম্নলিখিতটি যোগ করুন:

// Add alongside the other calls to GLES20.glGetUniformLocation.
occlusionBlurUniform = GLES20.glGetUniformLocation(program, "u_OcclusionBlurAmount");

draw মেথডের ভিতরে নিম্নলিখিতটি যোগ করুন:

// Add alongside the other calls to GLES20.glUniform1f.
GLES20.glUniform1f(occlusionBlurUniform, occlusionsBlur);

চাক্ষুষ তুলনা

এই পরিবর্তনগুলোর ফলে অবরুদ্ধ সীমানাটি এখন আরও মসৃণ হওয়া উচিত।

১০. নির্মাণ-চালনা-পরীক্ষা

আপনার অ্যাপ তৈরি করুন এবং চালান

  1. ইউএসবি-র মাধ্যমে একটি অ্যান্ড্রয়েড ডিভাইস সংযুক্ত করুন।
  2. ফাইল > বিল্ড এবং রান নির্বাচন করুন।
  3. সংরক্ষণ করুন: ARCodeLab.apk
  4. অ্যাপটি বিল্ড হয়ে আপনার ডিভাইসে ডেপ্লয় হওয়া পর্যন্ত অপেক্ষা করুন।

আপনি যখন প্রথমবার আপনার ডিভাইসে অ্যাপটি স্থাপন করার চেষ্টা করবেন:

  • আপনাকে ডিভাইসটিতে ইউএসবি ডিবাগিংয়ের অনুমতি দিতে হবে। চালিয়ে যাওয়ার জন্য ওকে (OK) নির্বাচন করুন।
  • অ্যাপটির ডিভাইস ক্যামেরা ব্যবহার করার অনুমতি আছে কিনা তা আপনাকে জিজ্ঞাসা করা হবে। এআর (AR) কার্যকারিতা ব্যবহার চালিয়ে যেতে অ্যাক্সেসের অনুমতি দিন।

আপনার অ্যাপ পরীক্ষা করা হচ্ছে

যখন আপনি আপনার অ্যাপটি চালাবেন, তখন আপনার ডিভাইসটি হাতে ধরে, চারপাশের জায়গায় ঘুরে এবং ধীরে ধীরে একটি এলাকা স্ক্যান করে এর প্রাথমিক আচরণ পরীক্ষা করতে পারেন। পরবর্তী ধাপে যাওয়ার আগে অন্তত ১০ সেকেন্ডের ডেটা সংগ্রহ করার এবং বিভিন্ন দিক থেকে এলাকাটি স্ক্যান করার চেষ্টা করুন।

সমস্যা সমাধান

ডেভেলপমেন্টের জন্য আপনার অ্যান্ড্রয়েড ডিভাইস সেট আপ করা

  1. একটি ইউএসবি ক্যাবলের মাধ্যমে আপনার ডিভাইসটিকে ডেভেলপমেন্ট মেশিনের সাথে সংযুক্ত করুন। আপনি যদি উইন্ডোজ ব্যবহার করে ডেভেলপ করেন, তাহলে আপনার ডিভাইসের জন্য উপযুক্ত ইউএসবি ড্রাইভার ইনস্টল করার প্রয়োজন হতে পারে।
  2. ডেভেলপার অপশন উইন্ডোতে ইউএসবি ডিবাগিং চালু করতে নিম্নলিখিত ধাপগুলো অনুসরণ করুন:
  3. সেটিংস অ্যাপটি খুলুন।
  4. আপনার ডিভাইসটি অ্যান্ড্রয়েড ৮.০ বা তার উচ্চতর সংস্করণ ব্যবহার করলে, সিস্টেম নির্বাচন করুন। অন্যথায়, পরবর্তী ধাপে এগিয়ে যান।
  5. একদম নিচে স্ক্রোল করুন এবং 'About phone' নির্বাচন করুন।
  6. একদম নিচে স্ক্রোল করুন এবং বিল্ড নম্বরে ৭ বার ট্যাপ করুন।
  7. আগের স্ক্রিনে ফিরে যান, একদম নিচে স্ক্রোল করুন এবং 'ডেভেলপার অপশনস'-এ ট্যাপ করুন।
  8. ডেভেলপার অপশন উইন্ডোতে, নিচে স্ক্রল করে ইউএসবি ডিবাগিং (USB debugging) খুঁজুন এবং এটি চালু করুন।

এই প্রক্রিয়াটি সম্পর্কে আরও বিস্তারিত তথ্য আপনি গুগলের অ্যান্ড্রয়েড ডেভেলপার ওয়েবসাইটে পেতে পারেন।

cfa20a722a68f54f.png

লাইসেন্স-সম্পর্কিত কোনো বিল্ড ব্যর্থতার সম্মুখীন হলে ( যেমন: কিছু লাইসেন্স গৃহীত না হওয়ায় নিম্নলিখিত অ্যান্ড্রয়েড এসডিকে প্যাকেজগুলি ইনস্টল করা যায়নি ), আপনি এই লাইসেন্সগুলি পর্যালোচনা ও গ্রহণ করার জন্য নিম্নলিখিত কমান্ডগুলি ব্যবহার করতে পারেন:

cd <path to Android SDK>

tools/bin/sdkmanager --licenses

১১. অভিনন্দন

অভিনন্দন, আপনি গুগলের ARCore Depth API ব্যবহার করে আপনার প্রথম ডেপথ-ভিত্তিক অগমেন্টেড রিয়েলিটি অ্যাপটি সফলভাবে তৈরি ও চালু করেছেন!

প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী