1. ก่อนเริ่มต้น
ARCore เป็นแพลตฟอร์มสำหรับสร้างแอป Augmented Reality (AR) ในอุปกรณ์เคลื่อนที่ ARCore จะช่วยให้อุปกรณ์ของผู้ใช้สังเกตและรับข้อมูลเกี่ยวกับสภาพแวดล้อมของตน รวมถึงโต้ตอบกับข้อมูลนั้นได้ด้วยการใช้ API ที่แตกต่างกัน
ใน Codelab นี้ คุณจะได้ทำตามขั้นตอนการสร้างแอปง่ายๆ ที่เปิดใช้ AR ซึ่งใช้ ARCore Depth API
ข้อกำหนดเบื้องต้น
Codelab นี้เขียนขึ้นสำหรับนักพัฒนาซอฟต์แวร์ที่มีความรู้เกี่ยวกับแนวคิด 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 แต่ละพิกเซลจะมีการวัดระยะทางจากกล้องถึงสิ่งแวดล้อม ซึ่งช่วยเพิ่มความสมจริงให้กับแอป AR ของคุณ
ความสามารถสำคัญเบื้องหลัง Depth API คือการบัง ซึ่งเป็นความสามารถในการให้วัตถุดิจิทัลปรากฏอย่างถูกต้องเมื่อเทียบกับวัตถุในโลกจริง ซึ่งจะทำให้วัตถุรู้สึกราวกับว่าวัตถุนั้นอยู่ในสภาพแวดล้อมจริงกับผู้ใช้
Codelab นี้จะแนะนำขั้นตอนการสร้างแอปที่ใช้งานง่ายซึ่งเปิดใช้ AR ซึ่งใช้รูปภาพความลึกในการบดบังวัตถุเสมือนที่อยู่หลังพื้นผิวโลกจริง และแสดงภาพรูปทรงเรขาคณิตที่ตรวจพบของอวกาศ
3. ตั้งค่า
ตั้งค่าเครื่องสำหรับการพัฒนา
- เชื่อมต่ออุปกรณ์ ARCore กับคอมพิวเตอร์ผ่านสาย USB ตรวจสอบว่าอุปกรณ์ของคุณอนุญาตให้แก้ไขข้อบกพร่อง USB
- เปิดเทอร์มินัลและเรียกใช้
adb devices
ตามที่แสดงด้านล่าง
adb devices List of devices attached <DEVICE_SERIAL_NUMBER> device
<DEVICE_SERIAL_NUMBER>
จะเป็นสตริงเฉพาะของอุปกรณ์ โปรดตรวจสอบว่าคุณเห็นอุปกรณ์รุ่นเดียวเท่านั้นก่อนดำเนินการต่อ
ดาวน์โหลดและติดตั้ง Code
- คุณจะโคลนที่เก็บได้ด้วยวิธีการต่อไปนี้
git clone https://github.com/googlecodelabs/arcore-depth
หรือดาวน์โหลดไฟล์ ZIP และแตกข้อมูลออกมา
- เปิด Android Studio แล้วคลิกเปิดโปรเจ็กต์ Android Studio ที่มีอยู่
- ค้นหาไดเรกทอรีที่คุณแตกไฟล์ ZIP ที่ดาวน์โหลดไว้ด้านบน แล้วเปิดไดเรกทอรี
depth_codelab_io2020
นี่คือโปรเจ็กต์ Gradle เดียวที่มีหลายโมดูล หากแผงโครงการที่ด้านซ้ายบนของ Android Studio ยังไม่ปรากฏในแผงโครงการ ให้คลิกโครงการจากเมนูแบบเลื่อนลง
ผลลัพธ์ที่ได้ควรมีลักษณะดังนี้
โครงการนี้มีโมดูลต่อไปนี้:
|
คุณจะได้ทำงานในโมดูล part0_work
นอกจากนี้ยังมีโซลูชันที่สมบูรณ์สำหรับแต่ละส่วนของ Codelab แต่ละโมดูลคือแอปที่บิลด์ได้
4. เรียกใช้แอปเริ่มต้น
- คลิกเรียกใช้ > เรียกใช้... "part0_work" ในกล่องโต้ตอบเลือกเป้าหมายการทำให้ใช้งานได้ที่ปรากฏขึ้น อุปกรณ์ของคุณควรอยู่ภายใต้อุปกรณ์ที่เชื่อมต่อ
- เลือกอุปกรณ์แล้วคลิกตกลง Android Studio จะสร้างแอปเริ่มต้นและเรียกใช้บนอุปกรณ์ของคุณ
- แอปจะขอสิทธิ์ใช้กล้อง แตะอนุญาตเพื่อดำเนินการต่อ
วิธีใช้แอป
|
ปัจจุบันแอปของคุณเรียบง่ายมากและไม่ค่อยมีความรู้เกี่ยวกับเรขาคณิตในฉากในโลกแห่งความเป็นจริงมากนัก
ตัวอย่างเช่น หากคุณวางรูป Android ไว้หลังเก้าอี้ การแสดงภาพจะลอยอยู่ด้านหน้า เนื่องจากแอปพลิเคชันไม่ทราบว่ามีเก้าอี้อยู่และควรซ่อน Android ไว้
ในการแก้ปัญหานี้ เราจะใช้ Depth API เพื่อปรับปรุงความสมจริงและความสมจริงในแอปนี้
5. ตรวจสอบว่าระบบรองรับ Depth API หรือไม่ (ส่วนที่ 1)
ARCore Depth API จะทำงานในอุปกรณ์ที่รองรับบางรุ่นเท่านั้น ก่อนที่จะผสานรวมฟังก์ชันเข้ากับแอปโดยใช้รูปภาพความลึกเหล่านี้ คุณต้องตรวจสอบก่อนว่าแอปทำงานอยู่ในอุปกรณ์ที่รองรับ
เพิ่มสมาชิกส่วนตัวคนใหม่ไปยัง DepthCodelabActivity
ซึ่งทำหน้าที่เป็นธงซึ่งจัดเก็บไว้ว่าอุปกรณ์ปัจจุบันรองรับความลึกหรือไม่:
private boolean isDepthSupported;
เราสามารถป้อนข้อมูล Flag นี้ได้จากภายในฟังก์ชัน 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;
}
หากแอปของคุณทำงานในอุปกรณ์ที่ไม่รองรับความลึก ข้อความที่คุณเพิ่งเพิ่มจะปรากฏที่ด้านล่าง
จากนั้น อัปเดตแอปให้เรียกใช้ Depth API และเรียกดูรูปภาพความลึกของแต่ละเฟรม
6. เรียกดูรูปภาพความลึก (ส่วนที่ 2)
Depth API จะบันทึกการสังเกตสภาพแวดล้อมของอุปกรณ์แบบ 3 มิติและแสดงผลรูปภาพความลึกที่มีข้อมูลดังกล่าวไปยังแอปของคุณ ภาพที่มีความลึกแต่ละพิกเซลจะแสดงการวัดระยะทางจากกล้องของอุปกรณ์ไปยังสภาพแวดล้อมจริง
ตอนนี้คุณจะใช้รูปภาพความลึกเหล่านี้เพื่อปรับปรุงการแสดงผลและการแสดงภาพในแอป ขั้นตอนแรกคือการดึงข้อมูลรูปภาพความลึกของแต่ละเฟรมและเชื่อมโยงพื้นผิวที่ 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 ใหม่ใน Android Studio ให้ทำดังนี้
|
เพิ่มโค้ดต่อไปนี้ในไฟล์ใหม่
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;
}
ทำขั้นตอนด้านบนซ้ำเพื่อทำให้ตัวควบคุมเงา Fragment อยู่ในไดเรกทอรีเดียวกันและตั้งชื่อว่า 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
เนื่องจากจะใช้ตัวปรับแสง 2 แบบ ดังนี้
// 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;
ถัดไป ให้เพิ่มปุ่มที่ควบคุมบูลีน 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);
}
ส่งพื้นผิวที่มีความลึกไปยัง backgroundRenderer
ด้วยการเพิ่มบรรทัดต่อไปนี้ใน onSurfaceCreated()
:
// 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)
ตอนนี้คุณจะจัดการการบล็อกวัตถุในแอปได้
การซ้อนทับหมายถึงสิ่งที่เกิดขึ้นเมื่อแสดงผลวัตถุเสมือนจริงได้ไม่สมบูรณ์เนื่องจากมีวัตถุจริงอยู่ระหว่างวัตถุเสมือนและกล้อง การจัดการการบังเป็นสิ่งสำคัญเพื่อให้ประสบการณ์ AR สมจริง
การแสดงภาพวัตถุเสมือนที่ถูกต้องในแบบเรียลไทม์จะช่วยเพิ่มความสมจริงและความน่าเชื่อถือของฉากที่เพิ่มเข้าไป ดูตัวอย่างเพิ่มเติมได้ที่วิดีโอเกี่ยวกับการผสานความจริงด้วย Depth API
ในส่วนนี้ คุณจะอัปเดตแอปให้รวมออบเจ็กต์เสมือนเฉพาะในกรณีที่มีความลึกเท่านั้น
การเพิ่มตัวปรับแสงเงาวัตถุใหม่
คุณจะเพิ่มเครื่องมือให้เฉดสีใหม่เพื่อรองรับข้อมูลเชิงลึกเช่นเดียวกับในส่วนก่อนหน้านี้ ในตอนนี้คุณสามารถคัดลอกตัวปรับแสงเงาวัตถุที่มีอยู่และเพิ่มฟังก์ชันการบังได้
คุณต้องเก็บตัวปรับแสงเงาวัตถุทั้ง 2 เวอร์ชันไว้ เพื่อให้แอปสามารถตัดสินใจเกี่ยวกับระยะเวลาที่จะรองรับความลึกได้
ทำสำเนาไฟล์ตัวปรับแสงสี 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;
อัปเดต occlusion_object.frag
ด้วยการเพิ่มตัวแปรเหล่านี้เหนือ main()
ที่ด้านบนของไฟล์
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
- คลิกขวา > สำเนา
- เลือกโฟลเดอร์การแสดงผล
- คลิกขวา > วาง
- เปลี่ยนชื่อชั้นเรียนเป็น
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()
เพื่อเปิดใช้โหมดผสานในการแสดงผล เพื่อให้สามารถใช้ความโปร่งใสกับวัตถุเสมือนเมื่อถูกบังอยู่ได้
// 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
แต่ไม่มีการใช้งานในอุปกรณ์ที่ไม่รองรับความลึก ในโทรศัพท์ที่มีความลึก ระบบจะเริ่มต้นทั้ง 2 เวอร์ชัน และตัดสินใจว่าจะใช้โหมดแสดงภาพเวอร์ชันใดเมื่อวาด
ภายในเมธอด 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
สร้างและเรียกใช้แอป
- เสียบอุปกรณ์ Android ผ่าน USB
- เลือก ไฟล์ > สร้างและเรียกใช้
- บันทึกเป็น ARCodeLab.apk
- รอให้แอปสร้างและทำให้ใช้งานได้ในอุปกรณ์ของคุณ
ครั้งแรกที่คุณพยายามทำให้แอปใช้งานได้ในอุปกรณ์ของคุณ ให้ทำดังนี้
- คุณจะต้องอนุญาตให้แก้ไขข้อบกพร่อง USB บนอุปกรณ์ เลือก "ตกลง" เพื่อดำเนินการต่อ
- ระบบจะถามว่าแอปมีสิทธิ์ใช้กล้องของอุปกรณ์หรือไม่ อนุญาตให้เข้าถึงเพื่อใช้ฟังก์ชัน AR ต่อไป
การทดสอบแอป
เมื่อเรียกใช้แอป คุณทดสอบลักษณะการทำงานเบื้องต้นของแอปได้โดยถืออุปกรณ์ไว้ เคลื่อนไปรอบๆ พื้นที่ของคุณ และสแกนพื้นที่ช้าๆ พยายามรวบรวมข้อมูลอย่างน้อย 10 วินาทีและสแกนพื้นที่จากหลายๆ เส้นทางก่อนไปยังขั้นตอนถัดไป
การแก้ปัญหา
ตั้งค่าอุปกรณ์ Android เพื่อการพัฒนา
- เชื่อมต่ออุปกรณ์กับเครื่องพัฒนาซอฟต์แวร์ด้วยสาย USB หากคุณพัฒนาโดยใช้ Windows คุณอาจต้องติดตั้งไดรเวอร์ USB ที่เหมาะสมสำหรับอุปกรณ์ของคุณ
- ทำตามขั้นตอนต่อไปนี้เพื่อเปิดใช้การแก้ไขข้อบกพร่อง USB ในหน้าต่างตัวเลือกสำหรับนักพัฒนาแอป
- เปิดแอปการตั้งค่า
- หากอุปกรณ์ใช้ Android เวอร์ชัน 8.0 ขึ้นไป ให้เลือกระบบ หรือไปยังขั้นตอนถัดไป
- เลื่อนไปด้านล่างแล้วเลือกเกี่ยวกับโทรศัพท์
- เลื่อนไปด้านล่างและแตะหมายเลขบิลด์ 7 ครั้ง
- กลับไปที่หน้าจอก่อนหน้า เลื่อนไปด้านล่าง แล้วแตะตัวเลือกสำหรับนักพัฒนาซอฟต์แวร์
- ในหน้าต่างตัวเลือกสำหรับนักพัฒนาซอฟต์แวร์ ให้เลื่อนลงเพื่อหาและเปิดใช้การแก้ไขข้อบกพร่อง USB
คุณสามารถดูข้อมูลโดยละเอียดเพิ่มเติมเกี่ยวกับกระบวนการนี้ได้ที่เว็บไซต์นักพัฒนาซอฟต์แวร์ Android ของ Google
ความล้มเหลวของบิลด์ที่เกี่ยวข้องกับใบอนุญาต
หากพบปัญหาเกี่ยวกับใบอนุญาต (ติดตั้งแพ็กเกจ Android SDK ต่อไปนี้ไม่สำเร็จเนื่องจากระบบไม่ยอมรับใบอนุญาตบางรายการ) คุณสามารถใช้คำสั่งต่อไปนี้เพื่อตรวจสอบและยอมรับใบอนุญาตเหล่านี้ได้
cd
<path to Android SDK>
tools/bin/sdkmanager --licenses
11. ขอแสดงความยินดี
ขอแสดงความยินดี คุณได้สร้างและเรียกใช้แอป Augmented Reality แบบเจาะลึกแอปแรกโดยใช้ ARCore Depth API ของ Google เรียบร้อยแล้ว