本篇文章較長,目的是能夠在手機中預覽數據。但願讀者可以認真讀下去。無論你是剛接觸ARCore以及OpenGL的 程序員。仍是立刻要畢業,把該項目拿來用做畢設的學生。我相信只要你能堅持下去,必定會對你有所幫助。 該文章代碼較多。部分解釋都集中在代碼的註釋中。在最後還有項目的GitHub地址。但願你們耐着性子看下去。我會盡可能寫的詳細,但願能幫到你們。固然也算是本身對知識的一種積累。php
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.jtl.arcoredemo">
//聲明Camera權限
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
//添加AR選項,在啓動應用時會判斷是否安裝了ARCore
<meta-data android:name="com.google.ar.core" android:value="required" />
</application>
</manifest>
複製代碼
android {
...
//要求JDK 1.8
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
//在APP Gradle中添加ARCore的依賴庫(截至2019.5.4,最新版本爲1.8.0)
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation "com.google.ar.sceneform:core:1.8.0"
}
複製代碼
這裏引入一個PermissionHelper類。java
/** * 權限幫助類 */
public final class PermissionHelper {
private static final int CAMERA_PERMISSION_CODE = 0;
private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA;
/** * 是否有相機權限 * @param activity * @return */
public static boolean hasCameraPermission(Activity activity) {
return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION)
== PackageManager.PERMISSION_GRANTED;
}
/** * 請求相機權限 * @param activity */
public static void requestCameraPermission(Activity activity) {
ActivityCompat.requestPermissions(
activity, new String[]{CAMERA_PERMISSION}, CAMERA_PERMISSION_CODE);
}
/** * 展現申請權限的相應解釋 * @param activity * @return */
public static boolean shouldShowRequestPermissionRationale(Activity activity) {
return ActivityCompat.shouldShowRequestPermissionRationale(activity, CAMERA_PERMISSION);
}
/** * 打開設置界面 * * @param activity */
public static void launchPermissionSettings(Activity activity) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", activity.getPackageName(), null));
activity.startActivity(intent);
}
}
複製代碼
在Activity中作相應的權限申請操做android
@Override
protected void onResume() {
super.onResume();
// ARCore 申請相機權限操做
if (!PermissionHelper.hasCameraPermission(this)) {
PermissionHelper.requestCameraPermission(this);
return;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (!PermissionHelper.hasCameraPermission(this)) {
Toast.makeText(this, "該應用須要相機權限", Toast.LENGTH_LONG)
.show();
//彈出相應解釋
if (!PermissionHelper.shouldShowRequestPermissionRationale(this)) {
// 直接跳至設置 修改權限
PermissionHelper.launchPermissionSettings(this);
}
finish();
}
}
複製代碼
Session也稱爲會話,是ARCore最核心的一個類。他能夠獲取相機數據。並算出相應的錨點信息以及視矩陣,投影矩陣等。這裏的部份內容會在後面進行具體敘述。git
首先判斷設備是否支持ARCore程序員
@Override
protected void onResume() {
super.onResume();
// ARCore 申請相機權限操做
...
Exception exception =null;
String msg =null;
//初始化Session
if (mSession==null){
try {
//判斷是否安裝ARCore
switch (ArCoreApk.getInstance().requestInstall(this,!isInstallRequested)){
case INSTALL_REQUESTED:
isInstallRequested=true;
break;
case INSTALLED:
Log.i(TAG,"已安裝ARCore");
break;
}
mSession=new Session(this);
} catch (UnavailableArcoreNotInstalledException
| UnavailableUserDeclinedInstallationException e) {
msg = "Please install ARCore";
exception = e;
} catch (UnavailableApkTooOldException e) {
msg = "Please update ARCore";
exception = e;
} catch (UnavailableSdkTooOldException e) {
msg = "Please update this app";
exception = e;
} catch (UnavailableDeviceNotCompatibleException e) {
msg = "This device does not support AR";
exception = e;
} catch (Exception e) {
msg = "Failed to create AR session";
exception = e;
}
//有異常說明不支持或者沒安裝ARCore
if (msg != null) {
Log.e(TAG, "Exception creating session", exception);
return;
}
}
//該設備支持而且已安裝ARCore
try {
//Session 恢復resume狀態
mSession.resume();
} catch (CameraNotAvailableException e) {
Log.e(TAG, "Camera not available. Please restart the app.");
mSession = null;
return;
}
}
複製代碼
@Override
protected void onResume() {
super.onResume();
// ARCore 申請相機權限操做
...
Exception exception =null;
String msg =null;
//初始化Session
if (mSession==null){
//判斷是否支持ARCore
...
}
//該設備支持而且已安裝ARCore
try {
//Session 恢復resume狀態
mSession.resume();
} catch (CameraNotAvailableException e) {
Log.e(TAG, "Camera not available. Please restart the app.");
mSession = null;
return;
}
}
複製代碼
@Override
protected void onPause() {
super.onPause();
if (mSession!=null){
mSession.pause();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mSession!=null){
mSession.close();
}
}
複製代碼
首先這裏只是簡單介紹一下OpenGL,具體的會在後面進行具體敘述。 OpenGL是一個渲染協議。不少渲染引擎底層就是用OpenGL實現的。如今的移動手機都是用的OpenGL ES2.0,幾乎涵蓋了全部的蘋果和Android手機。Android上有個叫作GLSurfaceView的控件。就是Google已經封裝好的一個渲染控件。它的底層API都是Google 封裝好的native方法,也就是俗稱的JNI方法。他須要實現一個Render接口。這個接口有三個回調方法。每個GLSurfaceView都會有一個相對應的GL線程,專門用來繪製。每一個GL線程都有相應的resume和pause方法。用來resume繪製和pause繪製。github
xml佈局session
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">
<android.opengl.GLSurfaceView android:id="@+id/gl_main_show" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/>
</android.support.constraint.ConstraintLayout>
複製代碼
初始化app
public class MainActivity extends AppCompatActivity implements GLSurfaceView.Renderer {
//用來使Session可以根據手機橫豎屏,輸出相應分辨率的數據
private DisplayRotationHelper mDisplayRotationHelper;
private GLSurfaceView mShowGLSurface;
//初始化相應數據
private void initData(){
mShowGLSurface=findViewById(R.id.gl_main_show);
mDisplayRotationHelper=new DisplayRotationHelper(this);
// Set up renderer.
mShowGLSurface.setPreserveEGLContextOnPause(true);
mShowGLSurface.setEGLContextClientVersion(2);//OpenGL版本爲2.0
mShowGLSurface.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
mShowGLSurface.setRenderer(this);//實現Render接口
mShowGLSurface.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//RENDERMODE_CONTINUOUSLY渲染模式爲實時渲染。
}
}
複製代碼
實現 Render接口的三個回調方法ide
/** * GLSurfaceView建立時被回調,能夠作一些初始化操做 * @param gl * @param config */
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//設置每一幀清屏顏色 傳入參輸爲RGBA
GLES20.glClearColor(0.1f,0.1f,0.1f,1.0f);
}
/** * GLSurfaceView 大小改變時調用 * @param gl * @param width GLSurfaceView寬 * @param height GLSurfaceView高 */
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//改變視口 方便 OpenGLES作 視口變換
GLES20.glViewport(0,0,width,height);
}
/** * GLSurfaceView繪製每一幀調用,此處不在主線程中,而是在GL線程中。 * 部分跨線程數據,須要作線程同步。不能直接更新UI(不在主線程) * @param gl */
@Override
public void onDrawFrame(GL10 gl) {
//清空彩色緩衝和深度緩衝 清空後的顏色爲GLES20.glClearColor()時設置的顏色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
}
複製代碼
GLSurfaceView的 resume和pause佈局
@Override
protected void onResume() {
super.onResume();
//ARCore的相應初始化操做
...
//GLSurfaceView onResume
mShowGLSurface.onResume();
mDisplayRotationHelper.onResume();
}
@Override
protected void onPause() {
super.onPause();
if (mSession!=null){
//因爲GLSurfaceView須要Session的數據。因此若是Session先pause會致使沒法獲取Session中的數據
mShowGLSurface.onPause();//GLSurfaceView onPause
mDisplayRotationHelper.onPause();
mSession.pause();
}
}
複製代碼
這裏引入了一個BackgroundRenderer。它纔是抽離出來的真正的用來渲染的類。具體寫法以及用途將在下一章介紹。
private BackgroundRenderer mBackgroundRenderer;
/** * GLSurfaceView建立時被回調,能夠作一些初始化操做 * @param gl * @param config */
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//設置每一幀清屏顏色 傳入參輸爲RGBA
GLES20.glClearColor(0.1f,0.1f,0.1f,1.0f);
mBackgroundRenderer=new BackgroundRenderer();
mBackgroundRenderer.createOnGlThread(this);
}
/** * GLSurfaceView 大小改變時調用 * @param gl * @param width GLSurfaceView寬 * @param height GLSurfaceView高 */
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//方便 OpenGLES作 視口變換
GLES20.glViewport(0,0,width,height);
mDisplayRotationHelper.onSurfaceChanged(width,height);
}
/** * GLSurfaceView繪製每一幀調用,此處不在主線程中,而是在GL線程中。 * 部分跨線程數據,須要作線程同步。不能直接更新UI(不在主線程) * @param gl */
@Override
public void onDrawFrame(GL10 gl) {
//清空彩色緩衝和深度緩衝 清空後的顏色爲GLES20.glClearColor()時設置的顏色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
if (mSession==null){
return;
}
//設置紋理ID
mSession.setCameraTextureName(mBackgroundRenderer.getTextureId());
//根據設備渲染Rotation,width,height。session.setDisplayGeometry(displayRotation, viewportWidth, viewportHeight);
mDisplayRotationHelper.updateSessionIfNeeded(mSession);
try {
Frame frame=mSession.update();//獲取Frame數據
mBackgroundRenderer.draw(frame);//渲染frame數據
} catch (CameraNotAvailableException e) {
e.printStackTrace();
}
}
複製代碼
這裏的ARCore調用邏輯,來源Google官方的ARCore示例。 渲染部分的代碼,有的沒說清的,後續都會補上。OpenGL的知識會很難。估計我要寫一陣了。若是有大神看到這篇文章中的錯誤,煩請及時指出。但願你們能共同進步,各位感興趣的加油吧!