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

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

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

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

পূর্বশর্ত

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

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

অনুসরণ

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

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

তোমার যা লাগবে

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

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

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

২. ARCore এবং Depth API

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

ARCore Depth API ARCore-এর Session দ্বারা প্রদত্ত প্রতিটি ফ্রেমের সাথে মিলে যাওয়া গভীরতার ছবিগুলিতে অ্যাক্সেস প্রদান করে। প্রতিটি পিক্সেল ক্যামেরা থেকে পরিবেশের দূরত্ব পরিমাপ প্রদান করে, যা আপনার AR অ্যাপের জন্য উন্নত বাস্তবতা প্রদান করে।

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

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

৩. সেট আপ করুন

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

  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. অ্যান্ড্রয়েড স্টুডিও চালু করুন এবং একটি বিদ্যমান অ্যান্ড্রয়েড স্টুডিও প্রকল্প খুলুন এ ক্লিক করুন।
  2. উপরে ডাউনলোড করা ZIP ফাইলটি যে ডিরেক্টরি থেকে বের করেছেন সেটি খুঁজুন এবং depth_codelab_io2020 ডিরেক্টরিটি খুলুন।

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

ফলাফলটি এইরকম দেখা উচিত:

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

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

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

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

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

c5ef65f7a1da0d9.png সম্পর্কে

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

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

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

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

6182cf62be13cd97.png সম্পর্কেঅনুসরণe4497751c6fad9a7.png সম্পর্কে

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

৫. Depth API সমর্থিত কিনা তা পরীক্ষা করুন (পর্ব ১)

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

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

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);

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

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

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

// 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 সম্পর্কে

এরপর, আপনি ডেপথ এপিআই কল করার জন্য অ্যাপটি আপডেট করবেন এবং প্রতিটি ফ্রেমের জন্য ডেপথ ইমেজ পুনরুদ্ধার করবেন।

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

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

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

প্রথমে, আপনার প্রকল্পে একটি নতুন ক্লাস যুক্ত করুন।
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 তে এই ক্লাসের একটি উদাহরণ যোগ করবেন, যাতে প্রতিটি ফ্রেমের জন্য আপনার কাছে সহজেই অ্যাক্সেসযোগ্য depth ছবির কপি থাকবে।

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);

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

অনুসরণ

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

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

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

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

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

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

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

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

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

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

  • 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;
}

এখন depth-aware হতে occlusion_object.fragmain() আপডেট করুন এবং occlusion প্রয়োগ করুন। ফাইলের নীচে নিম্নলিখিত লাইনগুলি যোগ করুন:

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 কখন ব্যবহার করা হবে তা নিয়ন্ত্রণ করতে পারবেন। virtualObject কনফিগার করা আছে এমন স্থানে onSurfaceCreated পদ্ধতির ভিতরে এই লাইনগুলি যোগ করুন:
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);
}

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

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);
}

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

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

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

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

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

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

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

অক্লুশন_অবজেক্ট.ফ্র্যাগ

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);

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

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

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

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. USB এর মাধ্যমে একটি Android ডিভাইস প্লাগ ইন করুন।
  2. ফাইল > বিল্ড এবং রান নির্বাচন করুন।
  3. এইভাবে সংরক্ষণ করুন: ARCodeLab.apk
  4. অ্যাপটি তৈরি এবং আপনার ডিভাইসে স্থাপনের জন্য অপেক্ষা করুন।

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

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

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

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

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

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

  1. আপনার ডিভাইসটিকে একটি USB কেবল দিয়ে আপনার ডেভেলপমেন্ট মেশিনের সাথে সংযুক্ত করুন। আপনি যদি Windows ব্যবহার করে ডেভেলপ করেন, তাহলে আপনার ডিভাইসের জন্য উপযুক্ত USB ড্রাইভার ইনস্টল করার প্রয়োজন হতে পারে।
  2. ডেভেলপার অপশন উইন্ডোতে USB ডিবাগিং সক্ষম করতে নিম্নলিখিত পদক্ষেপগুলি সম্পাদন করুন:
  3. সেটিংস অ্যাপটি খুলুন।
  4. যদি আপনার ডিভাইসটি Android v8.0 বা তার পরবর্তী সংস্করণ ব্যবহার করে, তাহলে System নির্বাচন করুন। অন্যথায়, পরবর্তী ধাপে যান।
  5. নীচে স্ক্রোল করুন এবং ফোন সম্পর্কে নির্বাচন করুন।
  6. নীচে স্ক্রোল করুন এবং বিল্ড নম্বরে ৭ বার ট্যাপ করুন।
  7. পূর্ববর্তী স্ক্রিনে ফিরে যান, নীচে স্ক্রোল করুন এবং ডেভেলপার বিকল্পগুলি আলতো চাপুন।
  8. ডেভেলপার অপশন উইন্ডোতে, USB ডিবাগিং খুঁজে পেতে এবং সক্ষম করতে নিচে স্ক্রোল করুন।

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

cfa20a722a68f54f.png সম্পর্কে

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

cd <path to Android SDK>

tools/bin/sdkmanager --licenses

১১. অভিনন্দন

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

সচরাচর জিজ্ঞাস্য