开始用Kotlin开发ARCore

原文:Getting Started with ARCore with Kotlin 

真实的维京海盗有加农炮吗?我不晓得,但是增强现实里的维京海盗是可以有的!

WWDC 2017苹果发布了ARKit,杀入了AR开发的世界。Google也不甘落后,就在上周,谷歌 发布了 ARCore,一个从 Tango 项目中提取出来的项目。Tango依赖于具有深度传感器的特定设备,而ARCore将(最终)可以跑在大多数设备上。

探索这个新领域的竞赛已经开始,demo项目如雨后春笋般冒出来。你可以AR Experiments网站上找到一些ARCore demo。

ARCore可以使用OpenGL, Unity, 以及 Unreal开发,本教程中,我们将在一个修改过的谷歌提供的OpenGL示例项目之上构建,完全使用Kotlin开发,整个过程都在Android Studio中完成。

如果你还没有用过Kotlin,请看Kotlin For Android: An Introduction

ARCore不能运行在Android模拟器上,目前如果你想完整的体验,你需要一台运行Android Nougat (7.0) 及以上的Samsung Galaxy S8 或者 Google Pixel/Pixel XL。如果以上设备你一台也没有,你仍然能找到开发ARCore SDK的感觉。

准备好探索这个崭新的世界了吗?Let’s go!

开始

首先从这里下载这个项目。用Android Studio 3.0 Beta 4或者以上版本打开项目。

注:当你打开这个项目,你会看到下面的窗口,记住不要选择“Update” ,而是选择 “Remind me tomorrow” 或者 “Don’t remind me again for this project”,因为如果你切换到 3.0.0-beta4 plugin可能会遇到编译错误。这是由于Android Studio 3.0 Beta 4中的DexArchiveMergerException导致的。

Screen-Shot-2017-09-05-at-7.04.04-PM.png

如果你用的是 Android Studio 2.3.3 ,那么安装了Kotlin插件也是可以的:]。

接下来,确保你的设备启用开发者选项 以及  启用 USB 调试。在开始运行项目之前,你需要下载安装谷歌提供的的ARCore Service。ARCore Service可以使用下面的adb命令行安装:

$ adb install -r -d arcore-preview.apk

更多信息请看adb 文档

现在你就可以点击Run/Run ‘app’ 或者 按 Ctrl-R运行项目了。

首先会询问提供camera权限,运行权限之后,在上方会有一个radio group,这里可以选择插入到场景的对象类型。

device-2017-09-05-121813-281x500.png

你可以看到底部有一个snackbar,提示“Searching for surfaces…”。你还可能会看到一些亮点,这些亮点是被跟踪识别的点。

把设备对着一个平面,将会检测出一个水平面:

device-2017-09-05-122206-281x500.png

一旦第一个水平面检测出来,snackbar将消失并且这个水平面突出显示在平面中。可以看到颜色偏亮的平面不易被检测到。

到现在为止,我们的起始app并没有做多少事情,但是在设置带加农炮的维京之前,我们先研究一下它的代码。

ARCode SDK

起始项目用到了一些3D模型,它们在Android Studio Project视图下的main/assets目录中。这些3D模型分别用于维京海盗,加农炮,以及靶子。3D模型文件用Blender来做出来的,参考了这篇教程:如何把Blender Models导出到 OpenGL ES: Part 1/3 。

在res/raw中是一些 OpenGL shader,都是来自 Google ARCore的示例项目(参见http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0901/8458.html  )。

你可以在起始项目中看到一个名为rendering的包,里面包含了一些从 Google ARCore sample app中复制来的 OpenGL renderer 和 utility。里面有一个被转换为了Kotlin的名为PlaneAttachment的类,它里面使用了 ARCore SDK。

Planes, Anchors, and Poses

PlaneAttachment类使用Plane和Anchor构建,同时可以使用它来构建一个Pose。以上三个类都来自ARCore SDK。

Plane描述的是真实世界的平坦表面,Anchor描述的是空间中某个固定的位置与方向,Pose描述从一个系统到另一个系统,从一个对象的局部坐标系到世界坐标系的坐标变换。

关于上述类的详细解释,请看官方文文档

简单来说,我们可以使用PlaneAttachment将某个anchor关联到平面上,并获取对应的pose。

ARCore Session

起始项目中的MainActivity中有一个ARCore Session对象,session描述了整个AR的状态,当用户触碰屏幕时,我们可以使用它将anchor关联到平面上。

onCreate()调用setupSession()方法,从而让应用检测设备是否支持ARCore。如果不是,将显示一个Toast,并结束activity。

假设你已经有一台支持ARCore的设备,那么接下来就是添加一些对象模型并渲染到场景中了。

添加对象

打开MainActivity,添加如下的变量

private val vikingObject = ObjectRenderer()
private val cannonObject = ObjectRenderer()
private val targetObject = ObjectRenderer()

每个变量都定义为ARCore sample app中的ObjectRenderer。

在这些对象下面,我们添加三个PlaneAttachment变量:

private var vikingAttachment: PlaneAttachment? = null
private var cannonAttachment: PlaneAttachment? = null
private var targetAttachment: PlaneAttachment? = null

这些是初始化为null的Kotlin nullable对象,稍后用户点击屏幕的时候被创建。

你需要在onSurfaceCreated(...)中设置这些对象,找到这个方法中已经有的try-catch块,在它之前添加下面的 try-catch:

// Prepare the other rendering objects.
try {
  vikingObject.createOnGlThread(this, "viking.obj", "viking.png")
  vikingObject.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f)
  cannonObject.createOnGlThread(this, "cannon.obj", "cannon.png")
  cannonObject.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f)
  targetObject.createOnGlThread(this, "target.obj", "target.png")
  targetObject.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f)
} catch (e: IOException) {
  Log.e(TAG, "Failed to read obj file")
}

这里使用起始项目中提供的3D模型文件来设置这三个对象,以及设置各自的material properties。

将Anchor关联到Session上

找到MainActivity中的handleTaps(...) 。在最里面的那个if语句中的break语句前面的注释之前添加下面的代码:

when (mode) {
  Mode.VIKING -> vikingAttachment = addSessionAnchorFromAttachment(vikingAttachment, hit)
  Mode.CANNON -> cannonAttachment = addSessionAnchorFromAttachment(cannonAttachment, hit)
  Mode.TARGET -> targetAttachment = addSessionAnchorFromAttachment(targetAttachment, hit)
}

mode的值是由屏幕顶端的radio按钮控制的。Mode是一个枚举类,它里面的每个mode是一个float类型的缩放比例 。这些缩放比例用于调整3D模型的大小。

在when语句中,我们对每一个mode下相应的PlaneAttachment设置了一个新的值,该值使用旧的attachment以及代表点击的hit值计算出来,hit是一个ARCore PlaneHitResult 。

现在你需要添加addSessionAnchorFromAttachment(…):

private fun addSessionAnchorFromAttachment(
  previousAttachment: PlaneAttachment?, hit: PlaneHitResult): PlaneAttachment {
  previousAttachment?.let {
    session.removeAnchors(Arrays.asList(previousAttachment.anchor))
  }
  return PlaneAttachment(hit.plane, session.addAnchor(hit.hitPose))
}

如果previousAttachment不为null,那么我们首先需要从session中移除其anchor,然后向session中添加一个新的anchor,并根据PlaneHitResult的plane和PlaneHitResult的pose中的anchor返回一个新的PlaneAttachment。

vikingcannons (1).png

马上你就可以看到你的维京武士做打靶练习了:]

绘制对象

最后一步需要做的就是在屏幕中绘制对象。此前我们已经实现了当用户触碰平面时创建 plane attachments ,而现在要做的就是在屏幕的渲染中绘制对象。

找到onDrawFrame(…)函数,然后在try语句块的底部添加以下语句:

drawObject(vikingObject, vikingAttachment, Mode.VIKING.scaleFactor,
  projectionMatrix, viewMatrix, lightIntensity)
drawObject(cannonObject, cannonAttachment, Mode.CANNON.scaleFactor,
  projectionMatrix, viewMatrix, lightIntensity)
drawObject(targetObject, targetAttachment, Mode.TARGET.scaleFactor,
  projectionMatrix, viewMatrix, lightIntensity)

在以上代码中,我们调用了已经定义好的辅助函数drawObject(...) ,drawObject(...) 以对象,对象对应的的attachment ,对应的的缩放比例以及OpenGL绘制所需的matrix和其它值 作为参数。

而这些viewmatrix值则是通过起始项目中的以下辅助函数来获取:

private fun computeProjectionMatrix(): FloatArray {
  val projectionMatrix = FloatArray(16)
  session.getProjectionMatrix(projectionMatrix, 0, 0.1f, 100.0f)
  return projectionMatrix
}
private fun computeViewMatrix(frame: Frame): FloatArray {
  val viewMatrix = FloatArray(16)
  frame.getViewMatrix(viewMatrix, 0)
  return viewMatrix
}
private fun computeLightIntensity(frame: Frame) = frame.lightEstimate.pixelIntensity

projectionMatrix 由ARCore Session计算得来。viewMatrix由ARCore Frame计算得来,ARCore Frame描述的是特定时间AR的状态。lightIntensity也是由frame决定的。

ok,现在运行app。从顶部的 radio button 中选择一个对象模型。然后使用相机找到一个平面并放置好对象。当所有对象都放置好了之后,如果你旋转手机,将看到如下场景:

Screenshot_20170905-134115-650x366.png

你可以移动场景看维京准备开炮的样子了。

接下来做什么?

你刚刚只是在Android Studio中简单的尝试了下OpenGL下的ARCore。更多资料请看 ARCore API 以及 ARCore Overview

本教程的最终app可以在这里下载。

你还可以使用ARCore with Unity 和 ARCore with Unreal。鉴于大部分ARCore开发都依赖于Unity,我强烈推荐你看看我们的Unity 相关内容

除了Android之外,ARCore还面向web平台,详情请看 这里。最后介绍一些使用ARCore开发的很酷的demo:Google experiments site

希望你喜欢本文对Kotlin开发ARCore的简单介绍!