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

1. আপনি শুরু করার আগে

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

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

পূর্বশর্ত

এই কোডল্যাবটি মৌলিক এআর ধারণার জ্ঞান থাকা বিকাশকারীদের জন্য লেখা হয়েছে।

আপনি কি নির্মাণ করবেন

1a0236e93212210c.gif

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

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

আপনি কি প্রয়োজন হবে

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

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

2. ARCore এবং গভীরতা API

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

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

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

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

3. সেট আপ করুন

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

  1. USB তারের মাধ্যমে আপনার ARCore ডিভাইসটিকে আপনার কম্পিউটারের সাথে সংযুক্ত করুন। নিশ্চিত করুন যে আপনার ডিভাইস 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

অথবা একটি জিপ ফাইল ডাউনলোড করুন এবং এটি বের করুন:

  1. অ্যান্ড্রয়েড স্টুডিও চালু করুন, এবং একটি বিদ্যমান অ্যান্ড্রয়েড স্টুডিও প্রকল্প খুলুন ক্লিক করুন।
  2. আপনি উপরে ডাউনলোড করা ZIP ফাইলটি যেখানে এক্সট্রাক্ট করেছেন সেই ডিরেক্টরিটি খুঁজুন এবং depth_codelab_io2020 ডিরেক্টরি খুলুন।

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

ফলাফল এই মত হওয়া উচিত:

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

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

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

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

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

c5ef65f7a1da0d9.png

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

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

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

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

6182cf62be13cd97.pngbeb0d327205f80ee.pnge4497751c6fad9a7.png

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

5. ডেপথ API সমর্থিত কিনা তা পরীক্ষা করুন (পর্ব 1)

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

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

এখন 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

এরপরে, আপনি Depth API কল করতে এবং প্রতিটি ফ্রেমের জন্য গভীরতার চিত্র পুনরুদ্ধার করতে অ্যাপটি আপডেট করবেন।

6. গভীরতার ছবি পুনরুদ্ধার করুন (পর্ব 2)

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

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

এখন আপনার কাছে একটি গভীরতার চিত্র রয়েছে যা প্রতিটি ফ্রেমের সাথে আপডেট করা হয়। এটা আপনার shaders দ্বারা ব্যবহার করার জন্য প্রস্তুত.

যাইহোক, অ্যাপটির আচরণ সম্পর্কে এখনও কিছুই পরিবর্তন হয়নি। এখন আপনি আপনার অ্যাপ উন্নত করতে গভীরতার চিত্র ব্যবহার করবেন।

7. গভীরতার চিত্র রেন্ডার করুন (পর্ব 3)

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

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

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

একটি নতুন .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 এর মান নিয়ন্ত্রণ করে। গভীরতার মানচিত্র রেন্ডার করা হয় কিনা তা নিয়ন্ত্রণ করতে এই পতাকাটি ব্যবহার করুন।

DepthCodelabActivityonDrawFrame() পদ্ধতিতে ফিরে আসুন, যোগ করুন:

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

এখন আপনি পর্দার উপরের ডানদিকে বোতাম টিপে প্রতিটি ফ্রেমের গভীরতার চিত্র দেখতে পারেন।

Depth API সমর্থন ছাড়াই চলছে

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

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

অ্যাপটি বর্তমানে সরাসরি গভীরতার মানচিত্র দেখায়। লাল পিক্সেলগুলি কাছাকাছি অবস্থিত অঞ্চলগুলিকে উপস্থাপন করে। নীল পিক্সেলগুলি দূরে অবস্থিত অঞ্চলগুলিকে প্রতিনিধিত্ব করে।

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

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

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

8. অবরোধের জন্য গভীরতা API ব্যবহার করুন (পর্ব 4)

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

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

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

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

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

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

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

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

  • src/main/assets/shaders/occlusion_object.vert গন্তব্য ফাইলে object.vert কপি করুন
  • গন্তব্য ফাইল src/main/assets/shaders/occlusion_object.frag 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.fragmain() আপডেট করুন। ফাইলের নীচে নিম্নলিখিত লাইন যোগ করুন:

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() যোগ করুন। এই পদ্ধতিটি একটি রূপান্তর ম্যাট্রিক্স প্রদান করে যা প্রয়োগ করা হলে, ক্যামেরা ফিড রেন্ডার করতে ব্যবহৃত কোয়াড টেক্সচার কোঅর্ডিনেটের সাথে স্ক্রীন স্পেস ইউভি সঠিকভাবে মেলে। এটি ডিভাইসের অভিযোজনও বিবেচনায় নেয়।
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);
}

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

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

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

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

9. [ঐচ্ছিক] অক্লুশন গুণমান উন্নত করুন

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

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

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

ভিজ্যুয়াল তুলনা

এই পরিবর্তনগুলির সাথে অক্লুশন সীমানা এখন মসৃণ হওয়া উচিত।

10. বিল্ড-রান-টেস্ট

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

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

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

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

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

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

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

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

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

আপনি Google এর অ্যান্ড্রয়েড বিকাশকারী ওয়েবসাইটে এই প্রক্রিয়া সম্পর্কে আরও বিস্তারিত তথ্য পেতে পারেন।

cfa20a722a68f54f.png

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

cd <path to Android SDK>

tools/bin/sdkmanager --licenses

11. অভিনন্দন

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

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