开始用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导致的。
如果你用的是 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,这里可以选择插入到场景的对象类型。
你可以看到底部有一个snackbar,提示“Searching for surfaces…”。你还可能会看到一些亮点,这些亮点是被跟踪识别的点。
把设备对着一个平面,将会检测出一个水平面:
一旦第一个水平面检测出来,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。
马上你就可以看到你的维京武士做打靶练习了:]
绘制对象
最后一步需要做的就是在屏幕中绘制对象。此前我们已经实现了当用户触碰平面时创建 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 中选择一个对象模型。然后使用相机找到一个平面并放置好对象。当所有对象都放置好了之后,如果你旋转手机,将看到如下场景:
你可以移动场景看维京准备开炮的样子了。
接下来做什么?
你刚刚只是在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的简单介绍!