1. 簡介
ARCore 是 google 官方出的一款 AR SDK啦鸣,其基本原理為:
ARCore 使用手機(jī)攝像頭來辨識特征點(diǎn)碘裕,并跟蹤這些特征點(diǎn)的移動軌跡常遂。結(jié)合特征點(diǎn)的移動軌跡和手機(jī)的慣性傳感器彪腔,ARCore 就可以在手機(jī)移動時判定它的位置侥锦、角度等信息。識別出特征點(diǎn)德挣,就能在特征點(diǎn)的基礎(chǔ)上恭垦,偵測平面,如地板、桌子等番挺。另外能 ARCore 也支持估測周圍的平均光照強(qiáng)度唠帝。有了手機(jī)自身的位置角度信息和周圍的光照強(qiáng)度信息,ARCore 就可以構(gòu)建周邊世界的模型玄柏。
-
運(yùn)動跟蹤
它利用 IMU 傳感器和設(shè)備的相機(jī)來發(fā)現(xiàn)空間的特征點(diǎn)襟衰,由此確定 Android 設(shè)備的位置和方向。此外粪摘,使用 VPS瀑晒,可以讓 AR 物體每次看起來似乎都在同一位置。
-
環(huán)境感知
虛擬物體一般都是放置于平坦平面上的徘意,用 ARCore 可以檢測物體的水平表面苔悦,建立環(huán)境認(rèn)知感,以保證虛擬的對象可以準(zhǔn)確放置椎咧,然后讓您看到放置在這些表面上的 AR 物體间坐。
-
光線預(yù)測
ARCore 根據(jù)環(huán)境的光強(qiáng)度,使開發(fā)人員可以與周圍環(huán)境相匹配的方式點(diǎn)亮虛擬對象邑退。此外竹宋,最近的一個實(shí)驗(yàn)發(fā)現(xiàn),虛擬陰影在真實(shí)環(huán)境光照下的調(diào)整功能也是如此地技,這樣就可以使 AR 物體的外觀更為逼真蜈七。
2. Android Studio 工程配置
- 安裝 Android Studio 2.3 及以上,使用 Android SDK Platform 版本 7.0
- 一臺支持的 Android 設(shè)備 (暫時僅支持 Google Pixel 和 Samsung Galaxy S8 的2款設(shè)備)
- 獲取 ARCore SDK
鑒于支持 ARCore 的 Android 設(shè)備太少莫矗,為此可通過修改 ARCore 的設(shè)備支持接口飒硅,修改方式如下:
- Open a command line interface
- Unzip the AAR to a temporary directory: unzip arcore_client-original.aar -d aar-tmp
- Enter the temporary aar directory: cd aar-tmp
- Unzip classes.jar to a temporary directory: unzip classes.jar -d classes-tmp
- Enter the temporary classes directory: cd classes-tmp
- Enter the directory containing the SupportedDevices class: cd com/google/atap/tangoservice
- Decompile the SupportedDevices class: java -jar /path/to/cfr.jar SupportedDevices.class > SupportedDevices.java
- Open a text editor and delete return false from the end of isSupported()
- Compile the modified SupportedDevice class: javac -cp /path/to/sdk/platform/android.jar -source 1.7 -target 1.7 SupportedDevices.java
- Delete the Java source: rm SupportedDevices.java
- Change directory back to aar-tmp: cd ../../../../../
- Create a JAR from the modified classes directory: jar cvf classes.jar -C classes-tmp .
- Change directory back to repo root: cd ..
- Create an AAR from the modified aar directory: jar cvf arcore_client.aar -C aar-tmp .
強(qiáng)行修改 ARCore 的支持函數(shù)判斷后,各機(jī)型的支持程度如下:
Manufacturer | Device | Model | GPU | 64-bit? | Official Support? | Functional? |
---|---|---|---|---|---|---|
Pixel | All | Adreno 530 | √ | √ | √ | |
Pixel XL | All | Adreno 530 | √ | √ | √ | |
Samsung | Galaxy S6 | G920 | Mali-T760MP8 | √ | × | ×× |
Samsung | Galaxy S7 | G930F | Mali-T880 MP12 | √ | × | × |
Samsung | Galaxy S7 Edge | G9350 (Hong Kong) | Adreno 530 | √ | × | ? |
Samsung | Galaxy S7 Edge | G935FD, G935F, G935W8 | Mali-T880 MP12 | √ | × | × |
Samsung | Galaxy S8 | USA & China | Adreno 540 | √ | √ | √ |
Samsung | Galaxy S8 | EMEA | Mali-G71 MP20 | √ | √ | ? |
Samsung | Galaxy S8+ | USA & China | Adreno 540 | √ | × | √ |
Samsung | Galaxy S8+ | G955F (EMEA) | Mali-G71 MP20 | √ | × | √ |
HTC | HTC 10 | All | Adreno 530 | √ | × | × |
Huawei | Nexus 6P | All | Adreno 430 | √ | × | √ |
Huawei | P9 Lite | All | Mali-T830MP2 | √ | × | × |
Huawei | P10 | All | Mali-G71 MP8 | √ | × | × |
LG | G2 | All | Adreno 330 | × | × | × |
LG | V20 | US996 | Adreno 530 | √ | × | × |
LG | Nexus 5 | All | Adreno 330 | √ | × | × |
LG | Nexus 5X | All | Adreno 418 | √ | × | × |
OnePlus | 3 | All | Adreno 530 | √ | × | × |
OnePlus | 3T | All | Adreno 530 | √ | × | × |
OnePlus | X | All | Adreno 330 | × | × | × |
OnePlus | 5 | All | Adreno 540 | √ | × | √ |
Nvidia | Shield K1 | All | ULP GeForce Kepler | √ | × | × |
Xiaomi | Redmi Note 4 | All | Adreno 506 | √ | × | × |
Xiaomi | Mi 5s | capricorn | Adreno 530 | √ | × | × |
Xiaomi | Mi Mix | All | Adreno 530 | √ | × | × |
Motorola | Moto G4 | All | Adreno 405 | √ | × | × |
Motorola | Nexus 6 | All | Adreno 420 | × | × | × |
ZTE | Axon 7 | A2017 | Adreno 530 | √ | × | × |
Sony | Xperia XZs | All | Adreno 530 | √ | × | × |
實(shí)際使用 Nexus 6P 運(yùn)行 arcore-for-all sample.apk 效果并不如意作谚,平面監(jiān)測效果較差三娩,較難形成平面
3. demo 簡析
3.0 demo 效果
3.1 配置工程
-
配置 sdk 版本信息
compileSdkVersion 25 buildToolsVersion "25.0.0" defaultConfig { applicationId "com.google.ar.core.examples.java.helloar" minSdkVersion 19 targetSdkVersion 25 versionCode 1 versionName "1.0" }
其中配置的
minSdkVersion
最小為 19 -
引入需要的第三方庫
dependencies { compile (name: 'arcore_client', ext: 'aar') compile (name: 'obj-0.2.1', ext: 'jar') ... }
其中 obj-0.2.1.jar 包用于加載解析 obj 文件為模型數(shù)據(jù)
3.2 顯示 Activity 布局和準(zhǔn)備對象
-
布局
<android.opengl.GLSurfaceView android:id="@+id/surfaceview" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="top"/>
-
渲染封裝對象
-
ObjectRenderer mVirtualObject
google 機(jī)器人模型
-
ObjectRenderer mVirtualObjectShadow
google 機(jī)器人陰影模型
-
PointCloudRenderer mPointCloud
平面監(jiān)測特征點(diǎn)
-
PlaneRenderer mPlaneRenderer
平面識別成功之后的網(wǎng)格模型
-
BackgroundRenderer mBackgroundRenderer
相機(jī)視頻流數(shù)據(jù)顯示至紋理
-
3.3 onCreate 初始化
setContentView(R.layout.activity_main);
mSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceview);
// 1.
mSession = new Session(/*context=*/this);
// 2.
// Create default config, check is supported, create session from that config.
mDefaultConfig = Config.createDefaultConfig();
if (!mSession.isSupported(mDefaultConfig)) {
Toast.makeText(this, "This device does not support AR", Toast.LENGTH_LONG).show();
finish();
return;
}
// 3.
// 創(chuàng)建并設(shè)置 SurfaceView Tap 事件
...
// 4.
// Set up renderer.
mSurfaceView.setPreserveEGLContextOnPause(true);
mSurfaceView.setEGLContextClientVersion(2);
mSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
mSurfaceView.setRenderer(this);
mSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
-
創(chuàng)建 Session 對象
Session
用于處理 ARCore 狀態(tài),處理當(dāng)前 AR 的生命周期(resume妹懒,pause)雀监,綁定背景相機(jī)圖像紋理,設(shè)置視口顯示大小眨唬,從視圖中獲取 frame 數(shù)據(jù)(可得到估計光照強(qiáng)度会前、投影矩陣、模型變換矩陣匾竿、圖像特征點(diǎn)數(shù)據(jù)和模型矩陣瓦宜、監(jiān)測平面數(shù)據(jù)等) -
創(chuàng)建
mDefaultConfig
并判斷當(dāng)前機(jī)型是否支持 ARCore暫時支持的機(jī)型,見 Supported Devices
-
創(chuàng)建并設(shè)置 SurfaceView Tap 事件
記錄用戶在 Surface Tap 的位置信息岭妖,用于創(chuàng)建 Google 機(jī)器人
-
SurfaceView 相關(guān)設(shè)置
設(shè)置在 Pause 時保留 GL 上下文環(huán)境临庇,設(shè)置 EGL 版本為 2.0反璃,設(shè)置各個通道的大小,設(shè)置渲染監(jiān)聽實(shí)現(xiàn)假夺,設(shè)置為主動渲染
3.4 處理生命周期
-
onResume
@Override protected void onResume() { super.onResume(); if (CameraPermissionHelper.hasCameraPermission(this)) { showLoadingMessage(); // Note that order matters - see the note in onPause(), the reverse applies here. mSession.resume(mDefaultConfig); mSurfaceView.onResume(); } else { CameraPermissionHelper.requestCameraPermission(this); } }
在頁面 onResume 時調(diào)用
mSession.resume(mDefaultConfig)
-
onPause
@Override public void onPause() { super.onPause(); mSurfaceView.onPause(); mSession.pause(); }
在頁面 onPause 時調(diào)用
mSession.pause()
版扩,停止頁面查詢 Session注意:mSession.pause() 必須在 mSurfaceView.onPause() 后面執(zhí)行,否則 可能發(fā)生在 mSession.pause() 之后繼續(xù)調(diào)用 mSession.update()侄泽,進(jìn)而發(fā)生 SessionPausedException 異常
3.5 SurfaceView 顯示準(zhǔn)備
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 1.
GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
// 2.
mBackgroundRenderer.createOnGlThread(/*context=*/this);
mSession.setCameraTextureName(mBackgroundRenderer.getTextureId());
try {
// 3.
mVirtualObject.createOnGlThread(/*context=*/this, "andy.obj", "andy.png");
mVirtualObject.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);
// 4.
mVirtualObjectShadow.createOnGlThread(/*context=*/this,
"andy_shadow.obj", "andy_shadow.png");
mVirtualObjectShadow.setBlendMode(BlendMode.Shadow);
mVirtualObjectShadow.setMaterialProperties(1.0f, 0.0f, 0.0f, 1.0f);
} catch (IOException e) {
Log.e(TAG, "Failed to read obj file");
}
try {
// 5.
mPlaneRenderer.createOnGlThread(/*context=*/this, "trigrid.png");
} catch (IOException e) {
Log.e(TAG, "Failed to read plane texture");
}
// 6.
mPointCloud.createOnGlThread(/*context=*/this);
}
- 設(shè)置 opengl 幀緩存清空顏色為灰白色
- 初始化背景紋理對象(后續(xù)詳細(xì)介紹),并將創(chuàng)建的紋理 id 綁定給 mSession 的相機(jī)紋理對象蜻韭,用于顯示相機(jī)產(chǎn)生的視頻內(nèi)容
- 初始化 Android 機(jī)器人顯示對象悼尾,并設(shè)置材料反射光照(環(huán)境光無, 漫反射光 3.5, 鏡面光 1.0, 光照聚焦 6.0)
- 初始化 Android 機(jī)器人陰影顯示對象,設(shè)置顯示混合模式(開啟透明度肖方,GLES20.GL_ONE_MINUS_SRC_ALPHA)闺魏,設(shè)置顯示材料反射光照信息(1.0f, 0.0f, 0.0f, 1.0f)
- 初始化平面監(jiān)測結(jié)果顯示對象
- 初始化特征點(diǎn)云顯示對象
3.6 SurfaceView 大小改變
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 1. 設(shè)置 opengl 的視口大小
GLES20.glViewport(0, 0, width, height);
// 2. 設(shè)置 mSession 中的顯示視口大小(和后續(xù)計算 frame 有關(guān))
mSession.setDisplayGeometry(width, height);
}
3.7 SurfaceView 繪制
@Override
public void onDrawFrame(GL10 gl) {
// 1.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
try {
// 2.
Frame frame = mSession.update();
// 3.
MotionEvent tap = mQueuedSingleTaps.poll();
if (tap != null && frame.getTrackingState() == TrackingState.TRACKING) {
for (HitResult hit : frame.hitTest(tap)) {
// Check if any plane was hit, and if it was hit inside the plane polygon.
if (hit instanceof PlaneHitResult && ((PlaneHitResult) hit).isHitInPolygon()) {
// Cap the number of objects created. This avoids overloading both the
// rendering system and ARCore.
if (mTouches.size() >= 16) {
mSession.removeAnchors(Arrays.asList(mTouches.get(0).getAnchor()));
mTouches.remove(0);
}
// Adding an Anchor tells ARCore that it should track this position in
// space. This anchor will be used in PlaneAttachment to place the 3d model
// in the correct position relative both to the world and to the plane.
mTouches.add(new PlaneAttachment(
((PlaneHitResult) hit).getPlane(),
mSession.addAnchor(hit.getHitPose())));
// Hits are sorted by depth. Consider only closest hit on a plane.
break;
}
}
}
// 4.
mBackgroundRenderer.draw(frame);
// 5.
if (frame.getTrackingState() == TrackingState.NOT_TRACKING) {
return;
}
// 6.
float[] projmtx = new float[16];
mSession.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
// 7.
float[] viewmtx = new float[16];
frame.getViewMatrix(viewmtx, 0);
// 8.
// Compute lighting from average intensity of the image.
final float lightIntensity = frame.getLightEstimate().getPixelIntensity();
// 9.
// Visualize tracked points.
mPointCloud.update(frame.getPointCloud());
mPointCloud.draw(frame.getPointCloudPose(), viewmtx, projmtx);
// 10.
// Check if we detected at least one plane. If so, hide the loading message.
...
// 11. Visualize planes.
mPlaneRenderer.drawPlanes(mSession.getAllPlanes(), frame.getPose(), projmtx);
// 12. Visualize anchors created by touch.
float scaleFactor = 1.0f;
for (PlaneAttachment planeAttachment : mTouches) {
if (!planeAttachment.isTracking()) {
continue;
}
// Get the current combined pose of an Anchor and Plane in world space. The Anchor
// and Plane poses are updated during calls to session.update() as ARCore refines
// its estimate of the world.
planeAttachment.getPose().toMatrix(mAnchorMatrix, 0);
// Update and draw the model and its shadow.
mVirtualObject.updateModelMatrix(mAnchorMatrix, scaleFactor);
mVirtualObjectShadow.updateModelMatrix(mAnchorMatrix, scaleFactor);
mVirtualObject.draw(viewmtx, projmtx, lightIntensity);
mVirtualObjectShadow.draw(viewmtx, projmtx, lightIntensity);
}
} catch (Throwable t) {
// Avoid crashing the application due to unhandled exceptions.
Log.e(TAG, "Exception on the OpenGL thread", t);
}
}
- 清除顏色和深度緩沖區(qū)
- 從 mSession 得到最新的 frame
- 遍歷前面 tap 操作得到的點(diǎn)列表(可以理解以此為起點(diǎn)俯画,垂直向下的一條射線)析桥,計算射線是否和 frame 中的平面有交點(diǎn),且交點(diǎn)是否處理平面監(jiān)測得到的區(qū)域里面艰垂。若是泡仗,則保存平面信息和 pose 信息(可得到模型的位置和轉(zhuǎn)向信息)
- 繪制相機(jī)背景視圖
- 判斷是否已經(jīng)檢測到平面,沒有的話猜憎,不在顯示后續(xù)內(nèi)容
- 從 frame 中得到投影矩陣
- 從 frame 中得到模型變換矩陣
- 得到預(yù)計的環(huán)境光照強(qiáng)度
- 更新檢測的特征點(diǎn)云的坐標(biāo)并繪制
- 取消顯示正在檢測中的 toast(和 AR 無關(guān))
- 繪制檢測到的平面(顯示為網(wǎng)格狀)
- 遍歷第 3 步記錄的 PlaneAttachment 列表娩怎,得到投影矩陣和模型變換矩陣,并結(jié)合第 8 步得到的光照強(qiáng)度胰柑,繪制 Android 機(jī)器人和陰影
3.8 顯示相機(jī)捕捉的視頻內(nèi)容
OpenGL 相關(guān)截亦,和 AR 無關(guān)
3.8.1 初始化背景紋理對象
public void createOnGlThread(Context context) {
// 1.
int textures[] = new int[1];
GLES20.glGenTextures(1, textures, 0);
mTextureId = textures[0];
GLES20.glBindTexture(mTextureTarget, mTextureId);
GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
int numVertices = 4;
if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) {
throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer.");
}
// 2.
ByteBuffer bbVertices = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE);
bbVertices.order(ByteOrder.nativeOrder());
mQuadVertices = bbVertices.asFloatBuffer();
mQuadVertices.put(QUAD_COORDS);
mQuadVertices.position(0);
// 3.
ByteBuffer bbTexCoords = ByteBuffer.allocateDirect(
numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
bbTexCoords.order(ByteOrder.nativeOrder());
mQuadTexCoord = bbTexCoords.asFloatBuffer();
mQuadTexCoord.put(QUAD_TEXCOORDS);
mQuadTexCoord.position(0);
// 4.
ByteBuffer bbTexCoordsTransformed = ByteBuffer.allocateDirect(
numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
bbTexCoordsTransformed.order(ByteOrder.nativeOrder());
mQuadTexCoordTransformed = bbTexCoordsTransformed.asFloatBuffer();
// 5.
int vertexShader = ShaderUtil.loadGLShader(TAG, context,
GLES20.GL_VERTEX_SHADER, R.raw.screenquad_vertex);
// 6.
int fragmentShader = ShaderUtil.loadGLShader(TAG, context,
GLES20.GL_FRAGMENT_SHADER, R.raw.screenquad_fragment_oes);
// 7.
mQuadProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mQuadProgram, vertexShader);
GLES20.glAttachShader(mQuadProgram, fragmentShader);
GLES20.glLinkProgram(mQuadProgram);
GLES20.glUseProgram(mQuadProgram);
// 8.
ShaderUtil.checkGLError(TAG, "Program creation");
// 9.
mQuadPositionParam = GLES20.glGetAttribLocation(mQuadProgram, "a_Position");
mQuadTexCoordParam = GLES20.glGetAttribLocation(mQuadProgram, "a_TexCoord");
ShaderUtil.checkGLError(TAG, "Program parameters");
}
-
創(chuàng)建紋理對象id,并綁定到
GL_TEXTURE_EXTERNAL_OES
柬讨,設(shè)置紋理貼圖的效果和縮放效果綁定的紋理不是
GL_TEXTURE_2D
崩瓤,而是GL_TEXTURE_EXTERNAL_OES
,是因?yàn)?Camera
使用的輸出texture
是一種特殊的格式踩官。同樣的却桶,在 shader 中也必須使用SamperExternalOES
的變量類型來訪問該紋理#extension GL_OES_EGL_image_external : require precision mediump float; varying vec2 v_TexCoord; uniform samplerExternalOES sTexture; void main() { gl_FragColor = texture2D(sTexture, v_TexCoord); }
片元顯示器
設(shè)置紋理幾何的頂點(diǎn)坐標(biāo)
設(shè)置紋理的初始貼圖坐標(biāo)
設(shè)置紋理最終的貼圖坐標(biāo),可從 Frame 中計算得到
-
加載頂點(diǎn)顯示器
attribute vec4 a_Position; attribute vec2 a_TexCoord; varying vec2 v_TexCoord; void main() { gl_Position = a_Position; v_TexCoord = a_TexCoord; }
加載片元顯示器
opengl 程序連接編譯
檢查 opengl 錯誤
初始化背景矩形的頂點(diǎn)坐標(biāo)對象蔗牡,和紋理坐標(biāo)對象
3.8.2 繪制相機(jī)捕獲的視頻幀至紋理
3.7 SurfaceView 繪制
的第 4 步調(diào)用了 mBackgroundRenderer.draw(frame)
其內(nèi)容如下:
public class BackgroundRenderer {
...
public void draw(Frame frame) {
// 1.
if (frame.isDisplayRotationChanged()) {
frame.transformDisplayUvCoords(mQuadTexCoord, mQuadTexCoordTransformed);
}
// 2.
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(false);
// 3.
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId);
// 4.
GLES20.glUseProgram(mQuadProgram);
// 5.
GLES20.glVertexAttribPointer(
mQuadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, mQuadVertices);
GLES20.glVertexAttribPointer(mQuadTexCoordParam, TEXCOORDS_PER_VERTEX,
GLES20.GL_FLOAT, false, 0, mQuadTexCoordTransformed);
GLES20.glEnableVertexAttribArray(mQuadPositionParam);
GLES20.glEnableVertexAttribArray(mQuadTexCoordParam);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDisableVertexAttribArray(mQuadPositionParam);
GLES20.glDisableVertexAttribArray(mQuadTexCoordParam);
// 6.
GLES20.glDepthMask(true);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
// 7.
ShaderUtil.checkGLError(TAG, "Draw");
}
}
-
當(dāng)顯示角度發(fā)生變化或者 SurfaceView 大小發(fā)生變化肾扰,重新計算背景的紋理 uv 坐標(biāo)
是否發(fā)生變化,如何計算蛋逾,均有 frame 接口提供
-
關(guān)閉深度測試和深度緩沖區(qū)的可讀性為不可讀
相機(jī)視頻幀數(shù)據(jù)是在所有模型的后面
綁定 mTextureId 至 GLES11Ext.GL_TEXTURE_EXTERNAL_OES
設(shè)置使用繪制背景的 shader 程序
將背景四邊形頂點(diǎn)數(shù)據(jù)和紋理貼圖的 uv 數(shù)據(jù)設(shè)置給綁定的 shader 變量集晚,設(shè)置數(shù)據(jù)在著色器端可見,繪制矩形內(nèi)容区匣,關(guān)閉數(shù)據(jù)在著色器端可見
重新打開深度測試和深度緩沖區(qū)的可讀性
檢查錯誤
其他內(nèi)容顯示類似偷拔,不再贅述
4. 總結(jié)
由上其實(shí)可以看到場景的顯示等蒋院,其實(shí)并不是 ARCore 關(guān)心的,ARCore 提供給我們的數(shù)據(jù)或幫我們完成的功能有:
- 判斷當(dāng)前機(jī)型是否支持 ARCore
- 提供接口判斷當(dāng)前角度是否發(fā)生變化莲绰,轉(zhuǎn)換相機(jī)幀紋理的 uv 坐標(biāo)
- 提供接口提取可用的 frame欺旧,得到投影矩陣和模型變換矩陣,得到特征點(diǎn)云蛤签,監(jiān)測出來的平面辞友,用于后續(xù)業(yè)務(wù)開發(fā)顯示場景
- 提供接口獲取估計光照強(qiáng)度
- 提供相機(jī)幀中的特征點(diǎn)和檢測得到的平面數(shù)據(jù)
- 提供接口計算點(diǎn)擊操作和場景是否相交,和交點(diǎn)信息(包括交點(diǎn)垂直平面的位置和方向)
- 暫時未見如果識別特定圖片震肮,顯示模型的 demo
相比其他第三方庫称龙,如 EasyAR 收費(fèi)版本,支持估計周圍環(huán)境光照強(qiáng)度戳晌,slam 算法更加穩(wěn)定強(qiáng)大鲫尊,效果更好;相比 Vuforia 支持平面監(jiān)測沦偎,但當(dāng)前可支持機(jī)型還比較少疫向。而非支持機(jī)型,通過改 ARR 代碼強(qiáng)制支持豪嚎,效果也不太理想搔驼,如 Nexus 6P。