আপনার Android NDK অ্যাপে গভীরতা ব্যবহার করুন

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

গভীরতার তথ্য গতি থেকে গণনা করা হয় এবং একটি হার্ডওয়্যার গভীরতা সেন্সর থেকে তথ্যের সাথে মিলিত হতে পারে, যেমন একটি টাইম-অফ-ফ্লাইট (ToF) সেন্সর, যদি উপলব্ধ থাকে। Depth API সমর্থন করার জন্য একটি ডিভাইসের একটি ToF সেন্সর প্রয়োজন হয় না

পূর্বশর্ত

এগিয়ে যাওয়ার আগে নিশ্চিত করুন যে আপনি মৌলিক AR ধারণা এবং কীভাবে একটি ARCore সেশন কনফিগার করবেন তা বুঝতে পেরেছেন।

গভীরতা-সমর্থিত ডিভাইসগুলিতে অ্যাক্সেস সীমাবদ্ধ করুন

যদি আপনার অ্যাপের ডেপথ এপিআই সমর্থনের প্রয়োজন হয়, কারণ হয় AR অভিজ্ঞতার একটি মূল অংশ গভীরতার উপর নির্ভর করে বা গভীরতা ব্যবহার করে এমন অ্যাপের অংশগুলির জন্য কোনও আকর্ষণীয় ফলব্যাক না থাকায়, আপনি Google Play-তে আপনার অ্যাপের বিতরণ সীমাবদ্ধ করতে বেছে নিতে পারেন ARCore সক্ষম করুন গাইডে বর্ণিত AndroidManifest.xml পরিবর্তনগুলি ছাড়াও আপনার AndroidManifest.xml এ নিম্নলিখিত লাইন যোগ করে Depth API সমর্থন করে এমন ডিভাইসগুলিতে সঞ্চয় করুন:

<uses-feature android:name="com.google.ar.core.depth" />

গভীরতা সক্ষম করুন

একটি নতুন ARCore সেশনে , ব্যবহারকারীর ডিভাইস Depth সমর্থন করে কিনা তা পরীক্ষা করুন৷ প্রসেসিং পাওয়ার সীমাবদ্ধতার কারণে সমস্ত ARCore-সামঞ্জস্যপূর্ণ ডিভাইস ডেপথ API সমর্থন করে না। সম্পদ সংরক্ষণ করতে, ARCore-এ ডিফল্টরূপে গভীরতা অক্ষম করা হয়। আপনার অ্যাপ ডেপথ এপিআই ব্যবহার করতে গভীরতা মোড সক্ষম করুন।

// Check whether the user's device supports the Depth API.
int32_t is_depth_supported = 0;
ArSession_isDepthModeSupported(ar_session, AR_DEPTH_MODE_AUTOMATIC,
                               &is_depth_supported);

// Configure the session for AR_DEPTH_MODE_AUTOMATIC.
ArConfig* ar_config = NULL;
ArConfig_create(ar_session, &ar_config);
if (is_depth_supported) {
  ArConfig_setDepthMode(ar_session, ar_config, AR_DEPTH_MODE_AUTOMATIC);
}
CHECK(ArSession_configure(ar_session, ar_config) == AR_SUCCESS);
ArConfig_destroy(ar_config);

গভীরতা ছবি অর্জন

বর্তমান ফ্রেমের জন্য গভীরতার চিত্র পেতে ArFrame_acquireDepthImage16Bits() কল করুন।

// Retrieve the depth image for the current frame, if available.
ArImage* depth_image = NULL;
// If a depth image is available, use it here.
if (ArFrame_acquireDepthImage16Bits(ar_session, ar_frame, &depth_image) !=
    AR_SUCCESS) {
  // No depth image received for this frame.
  // This normally means that depth data is not available yet.
  // Depth data will not be available if there are no tracked
  // feature points. This can happen when there is no motion, or when the
  // camera loses its ability to track objects in the surrounding
  // environment.
  return;
}

প্রত্যাবর্তিত চিত্রটি কাঁচা চিত্রের বাফার প্রদান করে, যা প্রতিটি রেন্ডার করা বস্তুর জন্য GPU-তে ব্যবহারের জন্য একটি ফ্র্যাগমেন্ট শেডারে প্রেরণ করা যেতে পারে। এটি AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES এ ভিত্তিক এবং ArFrame_transformCoordinates2d() কল করে AR_COORDINATES_2D_TEXTURE_NORMALIZED এ পরিবর্তন করা যেতে পারে। একবার অবজেক্ট শেডারের মধ্যে গভীরতার চিত্রটি অ্যাক্সেসযোগ্য হয়ে গেলে, এই গভীরতা পরিমাপগুলি অক্লুশন পরিচালনার জন্য সরাসরি অ্যাক্সেস করা যেতে পারে।

গভীরতার মান বুঝুন

পর্যবেক্ষিত বাস্তব-বিশ্ব জ্যামিতিতে বিন্দু A এবং একটি 2D বিন্দু a গভীরতার চিত্রে একই বিন্দুকে উপস্থাপন করে, a তে Depth API দ্বারা প্রদত্ত মানটি প্রধান অক্ষের উপর প্রক্ষিপ্ত CA এর দৈর্ঘ্যের সমান। এটিকে ক্যামেরার উৎপত্তি C এর সাথে A এর z-অর্ডিনেট হিসাবেও উল্লেখ করা যেতে পারে। Depth API এর সাথে কাজ করার সময়, এটা বোঝা গুরুত্বপূর্ণ যে গভীরতার মানগুলি রশ্মির CA এর দৈর্ঘ্য নয়, বরং এটির অভিক্ষেপ

শেডারে গভীরতা ব্যবহার করুন

বর্তমান ফ্রেমের জন্য গভীরতার তথ্য পার্স করুন

বর্তমান স্ক্রিনের অবস্থানের জন্য গভীরতার তথ্য অ্যাক্সেস করতে একটি ফ্র্যাগমেন্ট শেডারে সাহায্যকারী ফাংশন DepthGetMillimeters() এবং DepthGetVisibility() ব্যবহার করুন। তারপরে রেন্ডার করা অবজেক্টের কিছু অংশ বেছে নিতে এই তথ্যটি ব্যবহার করুন।

// Use DepthGetMillimeters() and DepthGetVisibility() to parse the depth image
// for a given pixel, and compare against the depth of the object to render.
float DepthGetMillimeters(in sampler2D depth_texture, 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(depth_texture, depth_uv).xyz;
  return dot(packedDepthAndVisibility.xy, vec2(255.0, 256.0 * 255.0));
}

// Return a value representing how visible or occluded a pixel is relative
// to the depth image. The range is 0.0 (not visible) to 1.0 (completely
// visible).
float DepthGetVisibility(in sampler2D depth_texture, in vec2 depth_uv,
                         in float asset_depth_mm) {
  float depth_mm = DepthGetMillimeters(depth_texture, depth_uv);

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

 // Use visibility_depth_near to set the minimum depth value. If using
 // this value for occlusion, avoid setting it too close to zero. A depth value
 // of zero signifies that there is no depth data to be found.
  float visibility_depth_near = 1.0 - InverseLerp(
      depth_mm, /*min_depth_mm=*/150.0, /*max_depth_mm=*/200.0);

  // Use visibility_depth_far to set the maximum depth value. If the depth
  // value is too high (outside the range specified by visibility_depth_far),
  // the virtual object may get inaccurately occluded at further distances
  // due to too much noise.
  float visibility_depth_far = InverseLerp(
      depth_mm, /*min_depth_mm=*/7500.0, /*max_depth_mm=*/8000.0);

  const float kOcclusionAlpha = 0.0f;
  float visibility =
      max(max(visibility_occlusion, kOcclusionAlpha),
          max(visibility_depth_near, visibility_depth_far));

  return visibility;
}

ভার্চুয়াল বস্তু বন্ধ করুন

ফ্র্যাগমেন্ট শেডারের বডিতে ভার্চুয়াল অবজেক্ট আটকান। এর গভীরতার উপর ভিত্তি করে অবজেক্টের আলফা চ্যানেল আপডেট করুন। এটি একটি অবজেক্টেড অবজেক্ট রেন্ডার করবে।

// Occlude virtual objects by updating the object’s alpha channel based on its depth.
const float kMetersToMillimeters = 1000.0;

float asset_depth_mm = v_ViewPosition.z * kMetersToMillimeters * -1.;

// Compute the texture coordinates to sample from the depth image.
vec2 depth_uvs = (u_DepthUvTransform * vec3(v_ScreenSpacePosition.xy, 1)).xy;

gl_FragColor.a *= DepthGetVisibility(u_DepthTexture, depth_uvs, asset_depth_mm);

আপনি টু-পাস রেন্ডারিং বা প্রতি-অবজেক্ট, ফরওয়ার্ড-পাস রেন্ডারিং ব্যবহার করে অক্লুশন রেন্ডার করতে পারেন। প্রতিটি পদ্ধতির কার্যকারিতা দৃশ্যের জটিলতা এবং অন্যান্য অ্যাপ-নির্দিষ্ট বিবেচনার উপর নির্ভর করে।

প্রতি-অবজেক্ট, ফরোয়ার্ড-পাস রেন্ডারিং

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

দুই-পাস রেন্ডারিং

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

ক্যামেরা ইমেজ এবং গভীরতা ইমেজ মধ্যে স্থানাঙ্ক রূপান্তর

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

CPU ইমেজে স্থানাঙ্কের জন্য গভীরতার চিত্র স্থানাঙ্ক পেতে:

const float cpu_image_coordinates[] = {(float)cpu_coordinate_x,
                                 (float)cpu_coordinate_y};
float texture_coordinates[2];
ArFrame_transformCoordinates2d(
    ar_session, ar_frame, AR_COORDINATES_2D_IMAGE_PIXELS, 1,
    cpu_image_coordinates, AR_COORDINATES_2D_TEXTURE_NORMALIZED,
    texture_coordinates);
if (texture_coordinates[0] < 0 || texture_coordinates[1] < 0) {
  // There are no valid depth coordinates, because the coordinates in the CPU
  // image are in the cropped area of the depth image.
} else {
  int depth_image_width = 0;
  ArImage_getWidth(ar_session, depth_image, &depth_image_width);
  int depth_image_height = 0;
  ArImage_getHeight(ar_session, depth_image, &depth_image_height);

  int depth_coordinate_x = (int)(texture_coordinates[0] * depth_image_width);
  int depth_coordinate_y = (int)(texture_coordinates[1] * depth_image_height);
}

গভীরতার চিত্র স্থানাঙ্কের জন্য CPU চিত্র স্থানাঙ্ক পেতে:

int depth_image_width = 0;
ArImage_getWidth(ar_session, depth_image, &depth_image_width);
int depth_image_height = 0;
ArImage_getHeight(ar_session, depth_image, &depth_image_height);

float texture_coordinates[] = {
    (float)depth_coordinate_x / (float)depth_image_width,
    (float)depth_coordinate_y / (float)depth_image_height};
float cpu_image_coordinates[2];
ArFrame_transformCoordinates2d(
    ar_session, ar_frame, AR_COORDINATES_2D_TEXTURE_NORMALIZED, 1,
    texture_coordinates, AR_COORDINATES_2D_IMAGE_PIXELS,
    cpu_image_coordinates);

int cpu_image_coordinate_x = (int)cpu_image_coordinates[0];
int cpu_image_coordinate_y = (int)cpu_image_coordinates[1];

ডেপথ হিট টেস্ট

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

গভীরতা-সক্ষম হিট পরীক্ষাগুলি ব্যবহার করতে, ArFrame_hitTest() এ কল করুন এবং রিটার্ন তালিকায় AR_TRACKABLE_DEPTH_POINT গুলি পরীক্ষা করুন৷

// Create a hit test using the Depth API.
ArHitResultList* hit_result_list = NULL;
ArHitResultList_create(ar_session, &hit_result_list);
ArFrame_hitTest(ar_session, ar_frame, hit_x, hit_y, hit_result_list);

int32_t hit_result_list_size = 0;
ArHitResultList_getSize(ar_session, hit_result_list, &hit_result_list_size);

ArHitResult* ar_hit_result = NULL;
for (int32_t i = 0; i < hit_result_list_size; ++i) {
  ArHitResult* ar_hit = NULL;
  ArHitResult_create(ar_session, &ar_hit);
  ArHitResultList_getItem(ar_session, hit_result_list, i, ar_hit);

  ArTrackable* ar_trackable = NULL;
  ArHitResult_acquireTrackable(ar_session, ar_hit, &ar_trackable);
  ArTrackableType ar_trackable_type = AR_TRACKABLE_NOT_VALID;
  ArTrackable_getType(ar_session, ar_trackable, &ar_trackable_type);
  // Creates an anchor if a plane or an oriented point was hit.
  if (AR_TRACKABLE_DEPTH_POINT == ar_trackable_type) {
    // Do something with the hit result.
  }
  ArTrackable_release(ar_trackable);
  ArHitResult_destroy(ar_hit);
}

ArHitResultList_destroy(hit_result_list);

এরপর কি

  • Raw Depth API দিয়ে আরও সঠিক সেন্সিং সক্ষম করুন৷
  • ARCore Depth Lab দেখুন, যা গভীরতার ডেটা অ্যাক্সেস করার বিভিন্ন উপায় দেখায়।