1. قبل از شروع
ARCore پلتفرمی برای ساخت اپلیکیشن های واقعیت افزوده (AR) در دستگاه های تلفن همراه است. با استفاده از API های مختلف، ARCore این امکان را برای دستگاه کاربر فراهم می کند تا اطلاعات محیط خود را مشاهده و دریافت کند و با آن اطلاعات تعامل داشته باشد.
در این کد لبه، فرآیند ساخت یک برنامه ساده با قابلیت AR را طی خواهید کرد که از ARCore Depth API استفاده می کند.
پیش نیازها
این کد لبه برای توسعه دهندگان با دانش مفاهیم اساسی AR نوشته شده است.
چیزی که خواهی ساخت
شما یک برنامه خواهید ساخت که از تصویر عمقی برای هر فریم برای تجسم هندسه صحنه و انجام انسداد روی دارایی های مجازی قرار داده شده استفاده می کند. شما مراحل خاصی را طی خواهید کرد:
- بررسی پشتیبانی Depth API در تلفن
- بازیابی عمق تصویر برای هر فریم
- تجسم اطلاعات عمقی به روش های مختلف (به انیمیشن بالا مراجعه کنید)
- استفاده از عمق برای افزایش واقع گرایی برنامه های دارای انسداد
- آموزش نحوه کار با گوشی هایی که از Depth API پشتیبانی نمی کنند
آنچه شما نیاز دارید
الزامات سخت افزاری
- یک دستگاه ARCore پشتیبانی شده ، که از طریق یک کابل USB به دستگاه توسعه شما متصل شده است. این دستگاه همچنین باید از Depth API پشتیبانی کند. لطفاً این لیست دستگاه های پشتیبانی شده را ببینید. Depth API فقط در Android در دسترس است.
- اشکال زدایی USB را برای این دستگاه فعال کنید.
نرم افزار مورد نیاز
- ARCore SDK 1.31.0 یا بالاتر.
- یک ماشین توسعه با Android Studio (نسخه 3.0 یا بالاتر).
2. ARCore و Depth API
Depth API از دوربین RGB دستگاه پشتیبانی شده برای ایجاد نقشه های عمق (که تصاویر عمق نیز نامیده می شود) استفاده می کند. شما می توانید از اطلاعات ارائه شده توسط یک نقشه عمق استفاده کنید تا اشیاء مجازی را به طور دقیق در جلو یا پشت اشیاء دنیای واقعی نشان دهید و تجربه کاربر واقعی و غوطه ور را فراهم کنید.
ARCore Depth API دسترسی به تصاویر عمقی مطابق با هر فریم ارائه شده توسط ARCore's Session را فراهم می کند. هر پیکسل اندازهگیری فاصله بین دوربین تا محیط را فراهم میکند که واقعگرایی را برای برنامه AR شما فراهم میکند.
یکی از قابلیتهای کلیدی در پشت API عمق، انسداد است: توانایی اشیاء دیجیتالی که به طور دقیق نسبت به اشیاء دنیای واقعی ظاهر شوند. این باعث می شود که اشیا احساس کنند که واقعاً در محیط با کاربر هستند.
این لبه کد شما را در فرآیند ساخت یک برنامه ساده با قابلیت AR راهنمایی می کند که از تصاویر عمقی برای انسداد اشیاء مجازی در پشت سطوح دنیای واقعی و تجسم هندسه شناسایی شده فضا استفاده می کند.
3. راه اندازی شوید
دستگاه توسعه را راه اندازی کنید
- دستگاه ARCore خود را از طریق کابل USB به رایانه خود وصل کنید. مطمئن شوید که دستگاه شما اجازه اشکال زدایی USB را می دهد .
- یک ترمینال را باز کنید و
adb devices
مانند تصویر زیر اجرا کنید:
adb devices List of devices attached <DEVICE_SERIAL_NUMBER> device
<DEVICE_SERIAL_NUMBER>
یک رشته منحصر به فرد برای دستگاه شما خواهد بود. قبل از ادامه، مطمئن شوید که دقیقا یک دستگاه را می بینید.
کد e را دانلود و نصب کنید
- می توانید مخزن را شبیه سازی کنید:
git clone https://github.com/googlecodelabs/arcore-depth
یا یک فایل ZIP دانلود کنید و آن را استخراج کنید:
- Android Studio را اجرا کنید و روی Open an Android Studio موجود پروژه کلیک کنید.
- دایرکتوری را که در آن فایل ZIP دانلود شده در بالا را استخراج کردید پیدا کنید و دایرکتوری
depth_codelab_io2020
را باز کنید.
این یک پروژه Gradle با چندین ماژول است. اگر پنجره پروژه در سمت چپ بالای Android Studio از قبل در قسمت Project نمایش داده نشده است، از منوی کشویی روی Projects کلیک کنید.
نتیجه باید به این صورت باشد:
این پروژه شامل ماژول های زیر است:
|
شما در ماژول part0_work
کار خواهید کرد. همچنین راه حل های کاملی برای هر قسمت از Codelab وجود دارد. هر ماژول یک برنامه قابل ساخت است.
4. Starter App را اجرا کنید
- روی Run > Run... > 'part0_work' کلیک کنید. در کادر گفتگوی Select Deployment Target که نمایش داده می شود، دستگاه شما باید در لیست دستگاه های متصل قرار گیرد.
- دستگاه خود را انتخاب کنید و روی OK کلیک کنید. Android Studio برنامه اولیه را می سازد و آن را روی دستگاه شما اجرا می کند.
- این برنامه مجوز دوربین را درخواست می کند. برای ادامه روی Allow ضربه بزنید.
نحوه استفاده از برنامه
|
در حال حاضر، برنامه شما بسیار ساده است و چیز زیادی در مورد هندسه صحنه های دنیای واقعی نمی داند.
به عنوان مثال، اگر یک فیگور اندروید را پشت صندلی قرار دهید، رندر به نظر می رسد که در جلوی آن قرار دارد، زیرا برنامه نمی داند که صندلی آنجاست و باید اندروید را مخفی کند.
برای رفع این مشکل، از Depth API برای بهبود غوطه وری و واقع گرایی در این برنامه استفاده می کنیم.
5. بررسی کنید که آیا Depth API پشتیبانی میشود (قسمت 1)
ARCore Depth API فقط روی زیرمجموعه ای از دستگاه های پشتیبانی شده اجرا می شود. قبل از ادغام عملکرد در یک برنامه با استفاده از این تصاویر عمقی، ابتدا باید مطمئن شوید که برنامه بر روی دستگاه پشتیبانی شده اجرا می شود.
یک عضو خصوصی جدید به DepthCodelabActivity
اضافه کنید که به عنوان پرچمی عمل می کند که ذخیره می کند آیا دستگاه فعلی از عمق پشتیبانی می کند یا خیر:
private boolean isDepthSupported;
میتوانیم این پرچم را از داخل تابع onResume()
پر کنیم، جایی که یک Session جدید ایجاد میشود.
کد موجود را پیدا کنید:
// 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);
اکنون Session AR به درستی پیکربندی شده است و برنامه شما می داند که آیا می تواند از ویژگی های مبتنی بر عمق استفاده کند یا خیر.
همچنین باید به کاربر اطلاع دهید که آیا از عمق برای این جلسه استفاده شده است یا خیر.
یک پیام دیگر به Snackbar اضافه کنید. در پایین صفحه ظاهر می شود:
// 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;
}
اگر برنامه شما روی دستگاهی اجرا می شود که از عمق پشتیبانی نمی کند، پیامی که به تازگی اضافه کرده اید در پایین ظاهر می شود:
در مرحله بعد، برنامه را برای فراخوانی Depth API و بازیابی تصاویر عمقی برای هر فریم به روز می کنید.
6. بازیابی تصاویر عمقی (قسمت 2)
Depth API مشاهدات سه بعدی از محیط دستگاه را می گیرد و یک تصویر عمقی را با آن داده ها به برنامه شما برمی گرداند. هر پیکسل در تصویر عمق نشان دهنده اندازه گیری فاصله بین دوربین دستگاه تا محیط واقعی آن است.
اکنون از این تصاویر عمقی برای بهبود رندر و تجسم در برنامه استفاده خواهید کرد. اولین گام این است که تصویر عمق را برای هر فریم بازیابی کنید و آن بافت را برای استفاده توسط GPU متصل کنید.
ابتدا یک کلاس جدید به پروژه خود اضافه کنید.
DepthTextureHandler
مسئول بازیابی تصویر عمقی برای یک قاب ARCore معین است.
این فایل را اضافه کنید:
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();
در نهایت، میخواهید این بافت را در هر فریم با آخرین تصویر عمق پر کنید، این کار را میتوانید با فراخوانی متد update()
در بالا ایجاد کردید در آخرین فریم بازیابی شده از session
انجام دهید.
از آنجایی که پشتیبانی عمق برای این برنامه اختیاری است، فقط در صورتی از این تماس استفاده کنید که از عمق استفاده می کنید.
// Add this just after "frame" is created inside onDrawFrame().
if (isDepthSupported) {
depthTexture.update(frame);
}
اکنون یک تصویر عمقی دارید که با هر فریم به روز می شود. برای استفاده توسط شیدرهای شما آماده است.
با این حال، هنوز چیزی در مورد رفتار برنامه تغییر نکرده است. اکنون از تصویر عمق برای بهبود برنامه خود استفاده خواهید کرد.
7. تصویر عمق را رندر کنید (قسمت 3)
اکنون یک تصویر عمقی برای بازی دارید، می خواهید ببینید که چگونه به نظر می رسد. در این بخش، یک دکمه به برنامه اضافه میکنید تا عمق هر فریم را نمایش دهد.
سایه بان های جدید اضافه کنید
راه های زیادی برای مشاهده عمق تصویر وجود دارد. سایه بان های زیر یک تجسم نقشه رنگی ساده را ارائه می دهند.
یک سایه زن vert جدید اضافه کنیددر اندروید استودیو:
|
در فایل جدید کد زیر را اضافه کنید:
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;
}
مراحل بالا را تکرار کنید تا shader قطعه را در همان دایرکتوری ایجاد کنید و نام آن را 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;
}
سپس، کلاس BackgroundRenderer
را برای استفاده از این سایهزنهای جدید، که در src/main/java/com/google/ar/core/codelab/common/rendering/BackgroundRenderer.java
قرار دارند، بهروزرسانی کنید.
مسیرهای فایل را به سایه بان های بالای کلاس اضافه کنید:
// 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 برای استفاده از دکمه اضافه کنید:
import android.widget.Button;
کلاس را به روز کنید تا یک عضو بولی اضافه کنید که نشان می دهد رندر عمق تغییر کرده است یا خیر: (به طور پیش فرض خاموش است):
private boolean showDepthMap = false;
سپس دکمه ای را که بولین showDepthMap
را کنترل می کند به انتهای متد onCreate()
اضافه کنید:
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
بولی را کنترل می کند. از این پرچم برای کنترل رندر شدن نقشه عمق استفاده کنید.
به متد onDrawFrame()
در DepthCodelabActivity
برگردید، اضافه کنید:
// 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 | در حال اجرا با پشتیبانی از 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);
اکنون عمق به عنوان یک پالس متحرک در صحنه شما تجسم می شود.
به راحتی می توانید مقادیر ارائه شده در اینجا را تغییر دهید تا پالس آهسته تر، سریع تر، گسترده تر، باریک تر و غیره شود. همچنین می توانید روش های کاملاً جدید برای تغییر سایه زن را برای نشان دادن اطلاعات عمق کاوش کنید!
8. از Depth API برای انسداد استفاده کنید (قسمت 4)
اکنون شما انسداد شی را در برنامه خود مدیریت خواهید کرد.
Occlusion به اتفاقی اشاره دارد که وقتی شی مجازی نمی تواند به طور کامل رندر شود، اتفاق می افتد، زیرا اشیاء واقعی بین شی مجازی و دوربین وجود دارد. مدیریت انسداد برای همهجانبه بودن تجربیات AR ضروری است.
رندر صحیح اشیاء مجازی در زمان واقعی، واقع گرایی و باورپذیری صحنه تقویت شده را افزایش می دهد. برای مثالهای بیشتر، لطفاً ویدیوی ما را در مورد ترکیب واقعیتها با Depth API ببینید.
در این بخش، برنامه خود را بهروزرسانی میکنید تا فقط در صورت دسترسی به عمق، اشیاء مجازی را شامل شود.
اضافه کردن شیدرهای جدید
مانند بخش های قبلی، سایه بان های جدیدی را برای پشتیبانی از اطلاعات عمق اضافه خواهید کرد. این بار می توانید شیدرهای موجود را کپی کنید و قابلیت انسداد را اضافه کنید.
مهم است که هر دو نسخه شیدرهای شیدر را حفظ کنید تا برنامه شما بتواند در زمان اجرا تصمیم بگیرد که از عمق پشتیبانی کند یا خیر.
از فایل های شیدر object.vert
و object.frag
در دایرکتوری src/main/assets/shaders
کپی کنید.
|
در داخل 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;
}
اکنون main()
در occlusion_object.frag
به روز کنید تا از عمق آگاه باشید و انسداد را اعمال کنید. خطوط زیر را در پایین فایل اضافه کنید:
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);
اکنون که نسخه جدیدی از شیدرهای آبجکت خود دارید، می توانید کد رندر را تغییر دهید.
رندر انسداد شی
یک کپی از کلاس ObjectRenderer
بعدی که در src/main/java/com/google/ar/core/codelab/common/rendering/ObjectRenderer.java
یافت می شود، تهیه کنید.
- کلاس
ObjectRenderer
را انتخاب کنید - کلیک راست کنید > کپی
- پوشه رندر را انتخاب کنید
- کلیک راست کنید > Paste
- نام کلاس را به
OcclusionObjectRenderer
تغییر دهید
کلاس جدید و تغییر نام یافته اکنون باید در همان پوشه ظاهر شود:
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()
اضافه کنید تا blend-mode در رندرینگ فعال شود تا بتوان شفافیت را در زمانی که اشیاء مجازی مسدود شده اند اعمال کرد:
// 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();
- بعداً میتوانید زمان استفاده از این
occludedVirtualObject
را بر اساس اینکه آیا دستگاه فعلی از Depth API پشتیبانی میکند، کنترل کنید. این خطوط را در داخل متدonSurfaceCreated
، در زیر جایی کهvirtualObject
پیکربندی شده است، اضافه کنید:
if (isDepthSupported) {
occludedVirtualObject.createOnGlThread(/*context=*/ this, "models/andy.obj", "models/andy.png");
occludedVirtualObject.setDepthTexture(
depthTexture.getDepthTexture(),
depthTexture.getDepthWidth(),
depthTexture.getDepthHeight());
occludedVirtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);
}
در دستگاه هایی که عمق پشتیبانی نمی شود، نمونه occludedVirtualObject
ایجاد می شود اما استفاده نمی شود. در تلفنهای دارای عمق، هر دو نسخه مقداردهی اولیه میشوند و تصمیمگیری در مورد زمان اجرا گرفته میشود که از کدام نسخه رندر هنگام ترسیم استفاده شود.
در متد onDrawFrame()
کد موجود را پیدا کنید:
virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, OBJECT_COLOR);
این کد را با کد زیر جایگزین کنید:
if (isDepthSupported) {
occludedVirtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
occludedVirtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, OBJECT_COLOR);
} else {
virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, OBJECT_COLOR);
}
در نهایت، اطمینان حاصل کنید که تصویر عمق به درستی بر روی رندر خروجی نگاشت شده است. از آنجایی که تصویر عمق وضوح متفاوتی دارد و احتمالاً نسبت ابعاد متفاوتی با صفحه نمایش شما دارد، مختصات بافت ممکن است بین خودش و تصویر دوربین متفاوت باشد.
- متد کمکی
getTextureTransformMatrix()
را به پایین فایل اضافه کنید. این روش یک ماتریس تبدیل را برمیگرداند که وقتی اعمال میشود، UVهای فضای صفحه نمایش را به درستی با مختصات بافت چهارگانه که برای رندر فید دوربین استفاده میشود، مطابقت میدهد. جهت گیری دستگاه را نیز در نظر می گیرد.
private static float[] getTextureTransformMatrix(Frame frame) {
float[] frameTransform = new float[6];
float[] uvTransform = new float[9];
// XY pairs of coordinates in NDC space that constitute the origin and points along the two
// principal axes.
float[] ndcBasis = {0, 0, 1, 0, 0, 1};
// Temporarily store the transformed points into outputTransform.
frame.transformCoordinates2d(
Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
ndcBasis,
Coordinates2d.TEXTURE_NORMALIZED,
frameTransform);
// Convert the transformed points into an affine transform and transpose it.
float ndcOriginX = frameTransform[0];
float ndcOriginY = frameTransform[1];
uvTransform[0] = frameTransform[2] - ndcOriginX;
uvTransform[1] = frameTransform[3] - ndcOriginY;
uvTransform[2] = 0;
uvTransform[3] = frameTransform[4] - ndcOriginX;
uvTransform[4] = frameTransform[5] - ndcOriginY;
uvTransform[5] = 0;
uvTransform[6] = ndcOriginX;
uvTransform[7] = ndcOriginY;
uvTransform[8] = 1;
return uvTransform;
}
getTextureTransformMatrix()
به واردات زیر در بالای فایل نیاز دارد:
import com.google.ar.core.Coordinates2d;
شما می خواهید هر زمان که بافت صفحه تغییر می کند (مانند چرخش صفحه) تبدیل بین این مختصات بافت را محاسبه کنید. این قابلیت دارای دروازه است.
پرچم زیر را در بالای فایل اضافه کنید:
// Add this member at the top of the file.
private boolean calculateUVTransform = true;
- در داخل
onDrawFrame()
، بررسی کنید که آیا تبدیل ذخیره شده پس از ایجاد کادر و دوربین باید دوباره محاسبه شود:
// Add these lines inside onDrawFrame() after frame.getCamera().
if (frame.hasDisplayGeometryChanged() || calculateUVTransform) {
calculateUVTransform = false;
float[] transform = getTextureTransformMatrix(frame);
occludedVirtualObject.setUvTransformMatrix(transform);
}
با اعمال این تغییرات، اکنون می توانید برنامه را با انسداد شی مجازی اجرا کنید!
اکنون برنامه شما باید بر روی همه تلفنها بهخوبی اجرا شود و زمانی که پشتیبانی میشود، بهطور خودکار از عمق برای انسداد استفاده میکند.
در حال اجرا برنامه با پشتیبانی از Depth API | اجرای برنامه بدون پشتیبانی از Depth API |
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. Build-Run-Test
اپلیکیشن خود را بسازید و اجرا کنید
- یک دستگاه اندرویدی را از طریق USB وصل کنید.
- File > Build and Run را انتخاب کنید.
- ذخیره به عنوان: ARCodeLab.apk .
- منتظر بمانید تا برنامه ساخته و در دستگاه شما مستقر شود.
اولین باری که سعی می کنید برنامه را در دستگاه خود نصب کنید:
- باید به اشکال زدایی USB در دستگاه اجازه دهید. برای ادامه، OK را انتخاب کنید.
- از شما پرسیده می شود که آیا برنامه مجوز استفاده از دوربین دستگاه را دارد یا خیر. اجازه دسترسی برای ادامه استفاده از عملکرد AR.
در حال آزمایش برنامه شما
هنگامی که برنامه خود را اجرا می کنید، می توانید با نگه داشتن دستگاه خود، حرکت در فضای خود و اسکن آهسته یک منطقه، رفتار اصلی آن را آزمایش کنید. سعی کنید حداقل 10 ثانیه داده جمع آوری کنید و قبل از رفتن به مرحله بعدی، منطقه را از چند جهت اسکن کنید.
عیب یابی
راه اندازی دستگاه اندرویدی خود برای توسعه
- دستگاه خود را با یک کابل USB به دستگاه توسعه خود وصل کنید. اگر با استفاده از ویندوز توسعه میدهید، ممکن است لازم باشد درایور USB مناسب دستگاه خود را نصب کنید.
- مراحل زیر را برای فعال کردن اشکال زدایی USB در پنجره Developer options انجام دهید:
- برنامه تنظیمات را باز کنید.
- اگر دستگاه شما از Android نسخه ۸.۰ یا بالاتر استفاده میکند، سیستم را انتخاب کنید. در غیر این صورت به مرحله بعدی بروید.
- به پایین بروید و درباره تلفن را انتخاب کنید.
- به پایین بروید و روی Build number 7 بار ضربه بزنید.
- به صفحه قبلی برگردید، به پایین بروید و روی گزینههای برنامهنویس ضربه بزنید.
- در پنجره Developer options ، به پایین بروید تا اشکال زدایی USB را پیدا کرده و فعال کنید.
میتوانید اطلاعات دقیقتری درباره این فرآیند در وبسایت توسعهدهنده اندروید Google بیابید.
خرابی های ساخت مربوط به مجوزها
اگر با مشکل ساخت مرتبط با مجوزها مواجه شدید ( بستههای Android SDK زیر نصب نشد زیرا برخی از مجوزها پذیرفته نشدند )، میتوانید از دستورات زیر برای بررسی و پذیرش این مجوزها استفاده کنید:
cd
<path to Android SDK>
tools/bin/sdkmanager --licenses
11. تبریک می گویم
تبریک میگوییم، شما با موفقیت اولین برنامه واقعیت افزوده مبتنی بر عمق خود را با استفاده از ARCore Depth API Google ساخته و اجرا کردهاید!