通过Google ARCore来认识AR是什么

2 阅读8分钟

ARCore

ARCore 是 Google 的增强现实体验构建平台。ARCore 利用不同的 API 让手机能够感知其环境、理解现实世界并与信息互动。

设备支持

并不是所有的设备都支持ARCore,首先需要Android 7.0 或更高版本,其次需要安装适用于 AR 的 Google Play 服务。

国内手机

在国内的手机上一般是没有Google Play的,但是部分设备可以安装适用于 AR 的 Google Play 服务,这样就可以运行ARCore。

具体哪些设备支持可以查看官方文档:developers.google.cn/ar/devices#…

当我们在这些设备上运行官方提供的hello ar demo的时候就会自动安装或更新Google Play Services for AR(我的华为手机会一直卡在更新,去应用商店中进行更新即可)。

模拟器

也可以使用Android模拟器来运行ARCore,首先要求:

  • Android Studio 3.1 或更高版本。
  • Android 模拟器 27.2.9 或更高版本。

还需要在Android SDK中在相应 Android 版本下,选择:
Google APIs Intel x86 Atom System Image API Level 27 或更高版本。

然后创建一个虚拟机,运行 API 级别 27 或更高版本的 x86 或 x86_64 系统映像,注意仅支持基于 x86 的 Android 模拟器架构。目前不支持其他架构,例如 arm64-v8aarmeabi-v7

在虚拟机的高级选项(Show Advanced Settings)中将后置摄像头(Camera Back)设置为 VirtualScene

注意:因为只能用x86架构,所以M1版本的mac上就不能使用,因为其上只能运行arm架构的虚拟机。

快速入门

下载官方提供的demo:github.com/google-ar/a…

git clone https://github.com/google-ar/arcore-android-sdk.git

然后打开其中hello_ar_kotlin项目,运行起来。

应用启动后,如果设备上没有Google Play Services for AR或者版本低,就会下载或更新安装。如果符合条件,则会进入AR场景。

进入AR场景后会先扫描环境来寻找surface,这就涉及到一个基本概念:环境理解。我们来看看官方解释。

ARCore 会通过检测特征点和平面来不断改进对现实世界环境的理解。

ARCore 可以查找看起来位于常见水平或垂直表面(例如桌子或墙)上的聚类特征点,并将这些表面作为几何平面提供给您的应用。****ARCore 也可以确定每个几何平面的边界,并将该信息提供给您的应用。您可以使用这些信息将虚拟对象置于平坦的表面上。

由于 ARCore 使用特征点来检测平面,因此可能无法正确检测没有纹理的平坦表面(例如白墙)。

简单来说就是ARCore会分析摄像头返回的图像,会试图找出图像中的平面,然后将这些平台提供给开发者来进行处理。

当扫描完后,就可以看到用白线和点标志出来的平面,这样我们就可以在平面上放置虚拟物品,点击右上角选择Instant Placement,然后在点击屏幕就可以在相应的位置放置一个三维 ARCore 兵。如下:

image.png

启用ARCore

AR必备和AR可选

首先先来了解一下什么是AR必备和AR可选。

官方对这个解释的很详细,大家可以参考developers.google.cn/ar/develop/…

这里我简单说说。

如果一个APP是AR必备的,那么只有那些支持ARCore的设备才能在GooglePlay中找到它,而且安装APP的同时GooglePlay会自动安装面向 AR 的 Google Play 服务;AR可选则没有上面这两个特点。

所以这个设定其实是针对GooglePlay的,在国内可以忽略不计的。根据我的测试,即使APP设置了AR必备,一样可以安装到非支持设备并且可以正常启动,只是AR功能无法使用。

所以无论应用是否是AR必备的,都必须使用ArCoreApk.checkAvailability()来检查 ARCore 支持和安装状态,对于不支持的设备要不起用AR相关功能。

另外要注意,AR必备要求minSdkVersion是24及以上,AR可选则要求19及以上。

配置

如何配置AR必备和AR可选?

AR必备

首先在AndroidManifest.xml中添加:

<uses-permission android:name="android.permission.CAMERA" />

<!-- Limits app visibility in the Google Play Store to ARCore supported devices
     (https://developers.google.com/ar/devices). -->
<uses-feature android:name="android.hardware.camera.ar" />

<application …>
    …

    <!-- "AR Required" app, requires "Google Play Services for AR" (ARCore)
         to be installed, as the app does not include any non-AR features. -->
    <meta-data android:name="com.google.ar.core" android:value="required" />
</application>

然后配置minSdkVersion为24及以上就可以了。

AR可选

首先在AndroidManifest.xml中添加:

<uses-permission android:name="android.permission.CAMERA" />

<!-- If your app was previously AR Required, don't forget to remove the
     `<uses-feature android:name="android.hardware.camera.ar" />` entry, as
     this would limit app visibility in the Google Play Store to only
     ARCore supported devices. -->

<application …>
    …

    <!-- "AR Optional" app, contains non-AR features that can be used when
         "Google Play Services for AR" (ARCore) is not available. -->
    <meta-data android:name="com.google.ar.core" android:value="optional" />
</application>

然后配置minSdkVersion为19及以上就可以了。

添加ARCore

最新的 ARCore 库作为依赖项添加到应用的 build.gradle 文件中:

dependencies {
    …
    implementation 'com.google.ar:core:1.33.0'
}

运行时检查

检查是否支持ARCore

上面说过无论是AR必备还是AR可选,都需要用 ArCoreApk.checkAvailability() 来检查设备是否支持 ARCore,如果不支持就需要停用或隐藏相关的AR功能。

由于checkAvailability是需要查询网络资源来确定是否支持,所以应该在应用早期调用一次,因为checkAvailability可以缓存结果,这样后续再执行的时候可以直接使用缓存结果即可。

所以我们在Application中先执行一次:

override fun onCreate() {
    super.onCreate()
    ArCoreApk.getInstance().checkAvailability(this)
}

然后在相关的页面再进行检查,代码如下

fun checkAr(){
    val arEnable = ArCoreApk.getInstance().checkAvailability(activity)
    if(arEnable.isTransient){
        Handler().postDelayed({checkAr()}, 200)
    }
    else if(arEnable.isSupported){
        //支持,显示ar相关功能
    }
    else{
        //不支持arcore,隐藏ar相关功能
    }
}

请求相机权限

创建AR会话前需要请求相机权限,否则会话会创建失败,代码如下:

activity?.let{
    if(ContextCompat.checkSelfPermission(it, Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED){
        ActivityCompat.requestPermissions(it, arrayOf(Manifest.permission.CAMERA), 0)
        return
    }

    ...
}

为了防止用户拒绝权限后导致无法使用,还应该在onRequestPermissionsResult中判断用户拒绝了权限就弹窗提示用户,指引用户去设置页面开启权限,这里就不放代码了。

检查是否已安装面向 AR 的 Google Play 服务

在创建AR会话前还要检查是否已安装面向 AR 的 Google Play 服务,或者确保是最新版本,代码如下:

fun installARCore(){
    activity?.let{
         if(ContextCompat.checkSelfPermission(it, Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED){
            ActivityCompat.requestPermissions(it, arrayOf(Manifest.permission.CAMERA), 0)
            return
        }

        try {
            if (mSession == null) {
                when(ArCoreApk.getInstance().requestInstall(it, mUserRequestedInstall)){
                    ArCoreApk.InstallStatus.INSTALLED -> {
                        mSession = Session(it)
                        //创建会话成功,可以使用AR
                    }
                    ArCoreApk.InstallStatus.INSTALL_REQUESTED -> {
                        // When this method returns `INSTALL_REQUESTED`:
                        // 1. ARCore pauses this activity.
                        // 2. ARCore prompts the user to install or update Google Play
                        //    Services for AR (market://details?id=com.google.ar.core).
                        // 3. ARCore downloads the latest device profile data.
                        // 4. ARCore resumes this activity. The next invocation of
                        //    requestInstall() will either return `INSTALLED` or throw an
                        //    exception if the installation or update did not succeed.
                        mUserRequestedInstall = false
                    }
                }
            }
            else{
                //创建会话成功,可以使用AR
            }
        }
        catch (e: UnavailableUserDeclinedInstallationException) {
            //用户先前拒绝安装(有可能是无法安装,有可能是取消了)
            Log.e("arcore", "install fial", e)
        }
        catch (e : Exception){
            Log.e("arcore", "install fial", e)
        }
    }
}

当没有安装相关服务的时候,会自动安装面向 AR 的 Google Play 服务;如果发现版本低则会更新(不过我这更新一直不成功,在应用商店中手动更新成功的)。

当检查成功直接创建AR会话,就可以使用AR相关功能了。

一般在onResume来执行检查,这样当安装完成后返回可以继续创建会话。

override fun onResume() {
    super.onResume()
    installARCore()
}

这里注意因为创建AR会话前需要相机权限,所以在最前面申请了相机权限,如果没有相机权限则创建会话的时候会报错UnavailableDeviceNotCompatibleException

上面我们通过ArCoreApk.checkAvailability() 来检查设备是否支持 ARCore,也可以将两个检查联动起来,ArCoreApk.checkAvailability()的返回值有多种状态,可以通过状态来判断是否需要检查服务,改动后代码如下:

fun checkAr(){
    val arEnable = ArCoreApk.getInstance().checkAvailability(activity)
    if(arEnable.isTransient){
        Handler().postDelayed({checkAr()}, 200)
    }
    else if(arEnable.isSupported){
        when(arEnable){
            ArCoreApk.Availability.SUPPORTED_INSTALLED -> {
                mSession = Session(activity)
            }
            ArCoreApk.Availability.SUPPORTED_APK_TOO_OLD,  ArCoreApk.Availability.SUPPORTED_NOT_INSTALLED -> {
                installARCore()
            }
        }
    }
    else{
        Log.d("arcore", "不支持arcore")
    }
}

如果设备支持且已经安装最新版本的服务,则直接创建会话;否则支持但是没安装或者版本比较旧,则执行安装即可。

ARCore会话

这样我们就成功在项目中启用了ARCore,并且创建了AR会话,所有 AR 流程(例如运动跟踪、环境理解和光照估算)都发生在 ARCore 会话中。创建会话后可以通过Config来配置会话,如下:

  session = Session(this)
  val config = Config(session)
  ...
  session.configure(config)

关闭

Session 拥有大量原生堆内存。如果未显式关闭会话,则可能导致应用耗尽原生内存并崩溃。当不再需要 AR 会话时,调用 close() 以释放资源。可以在onDestroy() 中调用 close():

总结

这样我们就可以开始通过ARCore开发AR功能了,不过因为ARCore不是sdk而是帮助sdk渲染对象的引擎,如果使用它来进行开发还要非常熟悉openGL。

为此Google提供了Sceneform SDK让开发者可以很轻松的构建AR应用,但是很遗憾的是Sceneform与VR sdk的命运是一样的,github上的开源项目最后的提交已经是两年前了。

不过因为使用起来比较简单,所以我们可以先用Sceneform来了解一下AR。

后面我会先用几篇文章来介绍一下Sceneform SDK,让大家简单了解一下AR。至于ARCore的后续文章,需要我深入学习一下openGL再来完成,目前我简单看了一下ARCore的示例,不熟练掌握openGL真的是寸步难行。