摘要:我们在此之前详细分析了arkit的开发原理。本篇我们将深入浅出讲一讲另一个在直播场景下同样实用的工具arcore。
其实关注 arcore也蛮久了,但一直没有腾出时间来写个总结。正好应朋友之约,我们今天就来好好聊一聊 arcore.
arcore的历史以及与苹果arkit的竞争我就不多讲了,在网上可以搜到一堆信息。但网上深入讲解arcore的确实不多。
本文主要有两个目的,一是向大家介绍一下arcore的基本概念,了解这些概念对于大家后续深入的学习 arcore具有关键的作用。二是深入剖析一下 arcore的工作机理,这样可以让大家更容易理解 arcore。
另外,arcore与arkit的基本概念很接近,只要了解了其中的一个,基本上也就掌握了另一个。
arcore的基本概念
arcore工作时要做两件事儿,首先跟踪手机的运动轨迹,然后构建出它对现实世界的理解。
arcore的运动跟踪技术是通过 camera 标识出特征点,并随着时间的推移跟踪这些特征点是如何移动的。通过这些特征点的运动数据及从手机惯性传感器读到的信息,arcore计算出手机移动的位置和方向,并称其为姿态。
除了识别出这些特征点外,arcore还能检测出像地板、桌面等平面信息以及在某个地方的光线强度。这些信息使得arcore能够构建出自己理解的真实世界。构建出这样一个模型后,可以在上面放置一些虚拟内容了。
arcore是如何做到的呢?它使用三项关键技术将虚拟内容与真实世界整合到一起,这三种技术分别是:
运动跟踪
环境理解
光线评估
运动跟踪
arcore 可以在手机移动的过程中知道,相对于真实世界手机所在的位置和方向(姿势)。
当手机在真实世界移动时,arcore使用称为并发测距和映射的过程来了解手机与周围世界的相对位置。
arcore能检测到camera捕获的图像在视觉上的不同特征,称为特征点。它使用这些点计算其位置变化。随着时间的推移,通过视觉信息与来自imu设备的惯性测量,arcore就可以估算出camera相对于真实世界的姿态(位置和方向)。
通过将渲染的3d虚拟内容与物理camera的姿势对齐,开发人员就可以从正确的角度渲染虚拟内容。 再通过将虚拟物品的图像渲染到从camera获得的图像之上,这样看起来就好像虚拟内容是真实世界的一部分似的。
环境理解
arcore可以让手机检测出一块水平面的位置和大小。如地面、桌子、书架等等。这样就可以将虚拟物体放置到检测出的水平面上了。
它是如何做到的呢?arcore通过检测特征点和平面不断改善对现实世界环境的理解。
arcore会查找常见水平表面(如桌面)上的特征点集群,除此之外,arcore还可以确定每个平面的边界,并将以上信息提供给您的应用程序。 这样,开发人员就可以使用这些信息,并将虚拟物体放置在平坦的表面上了。
由于arcore使用特征点检测平面,因此可能无法正确检测到没有纹理的平坦表面(如白色桌面)。
光线评估
用户交互
arcore使用 hit testing(命中测试) 获取与手机屏幕相对应的(x,y)坐标(如通过点击屏幕等交互方式),将其投射到 camera 的3d坐标系中,并返回与命中点射线相交的所有平面和特征点,以及在世界坐标系中该交叉点的姿态。这样就能实现用户与arcore环境中的对象交互了。
锚点与跟踪
arcore可以改变对自身位置和环境的理解来调整姿态。如我们要在arcore环境中放置一个虚拟对象,首先要确定一个锚点,以确保arcore能随着时间的推移不断跟踪对象的位置。通常情况下,会根据命中测试返回的姿势创建一个锚点。
姿势改变这项技术特别关键,只有得到姿势,arcore才可以随着时间的推移不断更新环境对象(像飞机和特征点)的位置。arcore将平面和点认为是可跟踪的特殊类型的对象。您可以将虚拟对象锚定到这些可追踪的对象上,以确保在设备移动时,虚拟对象和可跟踪对象之间保持稳定的关系。这就好像您在桌面上放置一个虚拟的花瓶,如果arcore稍后调整与桌面相关的姿势,那么花瓶仍然会保持在桌面上。
arcore 核心类介绍
session
com.google.ar.core.session类,session管理ar系统状态并处理session生命周期。 该类是arcore api的主要入口点。 该类允许用户创建session,配置session,启动/停止session,最重要的是接收视频帧,以允许访问camera图像和设备姿势。
config
com.google.ar.core.config类,用于保存session的设置。
frame
com.google.ar.core.frame类,该类通过调用update()方法,获取状态信息并更新ar系统。
hitresult
com.google.ar.core.hitresult类,该类定义了命中点射线与估算的真实几何世界之间的交集。
point
com.google.ar.core.point类,它代表arcore正在跟踪的空间点。 它是创建锚点(调用createanchor方法)时,或者进行命中检测(调用hittest方法)时,返回的结果。
pointcloud
com.google.ar.core.pointcloud类,它包含一组观察到的3d点和信心值。
plane
com.google.ar.core.plane类,描述了现实世界平面表面的最新信息。
anchor
com.google.ar.core.anchor类,描述了现实世界中的固定位置和方向。 为了保持物理空间的固定位置,这个位置的数字描述信息将随着arcore对空间的理解的不断改进而更新。
pose
com.google.ar.core.pose类, 姿势表示从一个坐标空间到另一个坐标空间位置不变的转换。 在所有的arcore api里,姿势总是描述从对象本地坐标空间到世界坐标空间的转换。
随着arcore对环境的了解不断变化,它将调整坐标系模式以便与真实世界保持一致。 这时,camera和锚点的位置(坐标)可能会发生明显的变化,以便它们所代表的物体处理恰当的位置。
这意味着,每一帧图像都应被认为是在一个完全独立的世界坐标空间中。锚点和camera的坐标不应该在渲染帧之外的地方使用,如果需考虑到某个位置超出单个渲染框架的范围,则应该创建一个锚点或者应该使用相对于附近现有锚点的位置。
imagemetadata
com.google.ar.core.imagemetadata类,提供了对camera图像捕捉结果的元数据的访问。
lightestimate
com.google.ar.core.lightestimate保存关于真实场景光照的估计信息。 通过 getlightestimate()得到。
实例分析
google发布的 arcore sdk 中包括了一些例子程序,有了上面的基本知识后,我们就很容易理解他所写的 demo 程序的流程了。
创建 session 和 conig
在 activity中的 oncreate 方法中创建 session 和 config是个不错的地方。
msession = new session(/*context=*/this);mdefaultconfig = config.createdefaultconfig();if (!msession.issupported(mdefaultconfig)) { toast.maketext(this, this device does not support ar, toast.length_long).show(); finish(); return;}
session: 是arcore的管理类,它非常重要。arcore的打开,关闭,视频帧的获取等都是通过它来管理的。
config:存放一些配置信息,如平面的查找模式,光照模式等信息都是记录在该类中。目前该类还比较简单,里边没存多少东西。
issupported:该方法主要是对 sdk的版本及机型做控制。目前官方只支持几款google和三星的机子做测试。其它机型还都不支持arcore,当然有一些机型通过破解后的sdk是可以使用 arcore的。该方法中的 config 参数没有用到。
创建 glsurfaceview 用于ar展示
在 google 提供的demo中,ar的展示部分使用的是 glsurfaceview。做视频开发的同学都清楚,android 可以使用三种view进行视频渲染。分别是:
surfaceview
glsurfaceview
textureview
其中,surfaceview最灵活,效率也最高,但使用起来比较烦锁。而glsurfaceview相对 surfaceview就是简单很多,只需要实现它的 render 接口即可。而 textureview使用最简单,很多工作都由 android 的窗口管理器帮你做了,但灵活性相对较差。
为了渲染的高效,google在demo中大量使用了opengl技术。由于opengl是图像处理非常大的一个领域,无法通过一两篇文章讲解清楚,同时也不是我们本文的重点,所以我们这里不对它做详细介绍,有兴趣的同学可以到网上自行学习。
msurfaceview = (glsurfaceview) findviewbyid(r.id.surfaceview);...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);
该段代码首先通过资源文件创建一个glsurfaceview对象,然后将 glsurfaceview 与 egl 上下文关联。并将activity作为glsurfaceview的回调对象(也就是说该activity要实现 glsurfaceview.renderer中定义的接口,如onsurfacecreated、onsurfacechanged、ondrawframe等),最后设置 msurfaceview 的渲染模式为 glsurfaceview.rendermode_continuously,即对 glsurfaceview 持续不断的渲染。
创建各种线程
要理解本节内容,首先大家要知道ar的详细工作原理是怎样的。我在这里再向大家做个简要的说明。
背景展示
用过ar的人都知道,ar是将一些虚拟物品放到真实的场景中。那么这个真实的场景从哪里来呢?当然是从手机的 camera上获取。
我们把从 camera中获取的视频当作 ar的背景。其实,ar 就是将虚拟物品放到视频上,只不过不是简单的放置,而是需要经过大量的计算,找到视频中的平面位置再放置。
而android中视频的采集相对比较简单,像直播系统,照像机都要使用该技术。
平台检测
上面我们已经说了,ar就是实时视频+虚拟物品。但虚拟物不能简单的放到视频上,而是先对视频中的每一帧进行检测,找到视频中的平面,确定好位置后,再将虚拟物品放置上去。这样才算是ar呀:)
点云
上面我们知道了,ar=实时视频+平面+虚拟物品。除此之外,它还应该能对虚拟物品进行跟踪,也就是可以在不同的角度观察同一个物品,并得出不同的姿态,所以就有了“点云” 技术。那什么是点云呢?顾名思义,形象的说就是一堆点,这些的形状有点像云。点云中的每个点都是一个特征点,它是通过camera获得的。
放置虚拟物品
找到了平面,有了跟踪手段,我们就可以将准备好的虚拟物品放置到平台上,现在才是真正的ar哈。
好,知道了这些基本原理后,我们来看看google demo是如何做的呢?
创建线程
对于上面的每一点,demo都启动了一个线程,代码如下:
...// create the texture and pass it to arcore session to be filled during update().mbackgroundrenderer.createonglthread(/*context=*/this);msession.setcameratexturename(mbackgroundrenderer.gettextureid());// prepare the other rendering objects.try { mvirtualobject.createonglthread(/*context=*/this, andy.obj, andy.png); mvirtualobject.setmaterialproperties(0.0f, 3.5f, 1.0f, 6.0f); ...} catch (ioexception e) { log.e(tag, failed to read obj file);}try { mplanerenderer.createonglthread(/*context=*/this, trigrid.png);} catch (ioexception e) { log.e(tag, failed to read plane texture);}mpointcloud.createonglthread(/*context=*/this);...
上面的代码中首先创建了一个背景线程,用来将从camera中获取的视频渲染到屏幕上当背景。数据是从哪里来的呢?就是通过 session.update 获取 camera 数据,再通过纹理交给背景线程。
对纹理没有概念的同学可以把它想像成一块内存空间。
然后启动虚拟物品线程,用于绘制虚拟物品,及发生角度变化时,更新虚拟物别的姿势。紧接着创建平面线程来绘制平面。最后启动点云线程绘制特征点。
到此,各种线程就创建完毕了。下面我们来说一下如何渲染。
命中检测与渲染
命中检测
当我们要向背景绘制虚拟物品时,首先要进行命中检测。代码如下:
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; } }}
在例子中,它查看是否有点击事件,且图像处理于跟踪状态?如果是,就对其进行命中检测,看是否可以找到一个平面,如果找到就创建一个锚点并将其与该平台绑定起来。
渲染背景
// draw background.mbackgroundrenderer.draw(frame);
通过上面的代码就可以将纹理中的内容推给 egl,上面创建的渲染线程从 egl 上下文中获取数据,最终将视频渲染到屏幕上。
绘制点云
mpointcloud.update(frame.getpointcloud());mpointcloud.draw(frame.getpointcloudpose(), viewmtx, projmtx);
同理,通过上面的代码,就可以将数据传给点云线程进行点云的绘制。
绘制平面
// visualize planes.mplanerenderer.drawplanes(msession.getallplanes(), frame.getpose(), projmtx);
通过上面代码将数据传给平面线程进行平面的绘制。
绘制虚拟物品
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);}
最后,遍历所有的锚点,在每个锚点上绘制虚拟物品。
至此,我们对arcore的分析就告一段落了。
小结
arcore相对于初学者来说还是有不少难度的。因为里面有很多新概念需要大家消化吸收。
另一方面,arcore目前只有几款机型可能做测试,而这几款机型在国内用的人不多,所以对于大多数人来说没法做实验,这也增加了学习的难度。
除了以上两点外,arcore中大量使用了 opengl的相关知识。而opengl又是一门很深的学问,所以学习的难度更加陡峭了。
通过以上三点,可以说目前学习arcore的门槛相较于苹果的arkit要难不少。
管道式质量流量计—MF在化工行业的氢气流量监控作用
科里奥利流量计发展趋势分析
CVPR2023:IDEA与清华提出首个一阶段3D全身人体网格重建算法
探讨生物识别技术在可穿戴领域的可持续发展
前端工程师的未来在哪
介绍一下ARCore的基本概念并剖析其工作机理
中芯国际:订单饱满和晶圆平均价格环比成长是三季度收入成长的主要因素
把计算机“织”进纤维里 织物电子技术的技术瓶颈
什么是Zcoin和Zcash
恩智浦和LivingPackets通过可重复使用的智能包装推动电子商务变革,实现更加环保的在线购物体验
什么是加热循环槽,它的特点是什么
四点让你明白 手机天线的辐射性能改善问题
真正可以在公路上行驶的太阳能汽车即将诞生
氢氧化钾在凸角处的蚀刻行为
各类常用三相异步电机产品型号、结构特点有哪些
Intevac Photonics将开发一款门控短波红外(SWIR)传感器
MICROTEST电子量测仪助力PCB智能制造
TMS320VC5402与PC机进行串行通信的两种方案
ZYNQ器件的启动配置方法
vivoZ5x游戏性能实测 到底怎么样