ARCore的原理和应用

ARCore 是 Google 的增强现实体验构建平台。 ARCore 利用不同的 API 让您的手机能够感知其环境、了解现实世界并与信息进行交互。 一些在 Android 和 iOS 上同时提供的 API 支持共享 AR 体验。

ARCore 使用三个主要功能将虚拟内容与通过手机摄像头看到的现实世界整合:

  • 运动跟踪让手机可以理解和跟踪它相对于现实世界的位置。
  • 环境理解让手机可以检测各类表面(例如地面、咖啡桌或墙壁等水平、垂直和倾斜表面)的大小和位置。
  • 光照估计让手机可以估测环境当前的光照条件。

支持的设备

ARCore 可以在运行 Android 7.0 (Nougat) 及更高版本系统的多种符合资格的 Android 手机上使用。 所有受支持设备的完整列表请参阅此处。

运动跟踪

当您的手机在现实世界中移动时,ARCore 会通过一个名为并行测距与映射(或 COM)的过程来理解手机相对于周围世界的位置。 ARCore 会检测捕获的摄像头图像中的视觉差异特征(称为特征点),并使用这些点来计算其位置变化。 这些视觉信息将与设备 IMU 的惯性测量结果结合,一起用于估测摄像头随着时间推移而相对于周围世界的姿态(位置和方向)。

通过将渲染 3D 内容的虚拟摄像头的姿态与 ARCore 提供的设备摄像头的姿态对齐,开发者能够从正确的透视角度渲染虚拟内容。 渲染的虚拟图像可以叠加到从设备摄像头获取的图像上,让虚拟内容看起来就像现实世界的一部分一样。

ARCore的原理和应用_第1张图片
image

环境理解

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

ARCore 可以查找看起来位于常见水平或垂直表面(例如桌子或墙)上的成簇特征点,并让这些表面可以由您的应用用作平面。 ARCore 也可以确定每个平面的边界,并将该信息提供给您的应用。 您可以使用此信息将虚拟物体置于平坦的表面上。

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

ARCore的原理和应用_第2张图片
image

光估测

ARCore 可以检测其环境光线的相关信息,并为您提供给定摄像头图像的平均光强度和色彩校正。 此信息让您能够使用与周围环境相同的光照来照亮您的虚拟物体,提升它们的真实感。

ARCore的原理和应用_第3张图片
image

用户交互

ARCore 利用命中测试来获取对应于手机屏幕的 (x,y) 坐标(通过点按或您希望应用支持的任何其他交互提供),并将一条射线投影到摄像头的视野中,返回这条射线贯穿的任何平面或特征点以及交叉位置在现实世界空间中的姿态。 这让用户可以选择环境中的物体或者与它们互动。

定向点

借助定向点,您可以将虚拟物体置于倾斜的表面上。 当您执行会返回特征点的命中测试时,ARCore 将查看附近的特征点并使用这些特征点估算表面在给定特征点处的角度。 然后,ARCore 会返回一个将该角度考虑在内的姿态。

由于 ARCore 使用成簇特征点来检测表面的角度,因此可能无法正确检测像白墙一样没有纹理的表面。

锚点和可跟踪对象

姿态会随着 ARCore 改进它对自身位置和环境的理解而变化。 当您想要放置一个虚拟物体时,您需要定义一个锚点来确保 ARCore 可以跟踪物体随时间推移的位置。 很多时候,您需要基于命中测试返回的姿态创建一个锚点,如用户交互中所述。

姿态会发生变化,这就意味着 ARCore 可能会更新平面和特征点等环境物体随时间推移的位置。 平面和特征点是一种特殊类型的物体,称为可跟踪对象。 顾名思义,ARCore 可以随着时间推移跟踪这些物体。 您可以将虚拟物体锚定到特定的可跟踪对象,确保您的虚拟物体与可跟踪对象之间的关系即使在设备移动时也能保持稳定。 这意味着,如果您将一个虚拟的 Android 小雕像放在您的书桌上,即使 ARCore 稍后调整了与书桌关联的平面的姿态,Android 小雕像仍会看起来位于桌子上。

注:为了减少 CPU 开销,请尽可能重用锚点并在不再需要时分离锚点。

增强图像

使用增强图像,您可以构建能够响应特定 2D 图像(如产品包装或电影海报)的 AR 应用。 用户可以在将手机的摄像头对准特定图像时触发 AR 体验,例如,他们可以将手机的摄像头对准电影海报,使人物弹出,然后引发一个场景。

可离线编译图像以创建图像数据库,也可以从设备实时添加单独的图像。 注册后,ARCore 将检测这些图像、图像边界,然后返回相应的姿态。

共享

借助 ARCore 的 Cloud Anchors API,您可以创建适用于 Android 和 iOS 设备的协作性或多人游戏应用。

使用云锚点,一台设备可以将锚点和附近的特征点发送到云端进行托管。 可以将这些锚点与同一环境中 Android 或 iOS 设备上的其他用户共享。 这使应用可以渲染连接到这些锚点的相同 3D 对象,从而让用户能够同步拥有相同的 AR 体验。


ARCore的原理和应用_第4张图片
1542355431(1).png

ARCore架构分析

  • 核心类
Session.java
Anchor.java
ArCoreApk.java
Camera.java
Frame.java
HitResult.java
Plane.java
Point.java
PointCloud.java
Trackable.java
TrackingState.java
  • Session 核心类,相当于Android里的Context,SDK对外的接口的核心,Session封装Camera 2的预览的现实世界(AR的背景)和手机IMU信息,经过Session的核心算法,获得平面,特征点,姿态,光照估计等。

Anchor
com.google.ar.core.Anchor类,描述了现实世界中的固定位置和方向。 为了保持物理空间的固定位置,这个位置的数字描述信息将随着ARCore对空间的理解的不断改进而更新。

Pose
com.google.ar.core.Pose类, 姿势表示从一个坐标空间到另一个坐标空间位置不变的转换。 在所有的ARCore API里,姿势总是描述从对象本地坐标空间到世界坐标空间的转换。随着ARCore对环境的了解不断变化,它将调整坐标系模式以便与真实世界保持一致。 这时,Camera和锚点的位置(坐标)可能会发生明显的变化,以便它们所代表的物体处理恰当的位置。每一帧图像都应被认为是在一个完全独立的世界坐标空间中。锚点和Camera的坐标不应该在渲染帧之外的地方使用,如果需考虑到某个位置超出单个渲染框架的范围,则应该创建一个锚点或者应该使用相对于附近现有锚点的位置。

LightEstimate
com.google.ar.core.LightEstimate保存关于真实场景光照的估计信息。 通过 getLightEstimate()得到。

Trackable
com.google.ar.core.Pose接口类,它是ARCore可以跟踪的,并能与锚点绑定在一起的东西,比如:Plane (平面)。

Camera
android.graphics.Camera类,它提供用于捕获图像的Camera的信息。 Camera是一个长期存活的对象,每次调用Session.update() 都会更新Camera的属性。

核心方法如下:

//构造函数,可以通过Session获取Frame和Plan等信息
public Session(Context context)
//resume时,打开Camera 和 IMU设备,获取图像和IMU信息,并执行核心算法
public void resume()
//从Session中得到Frame,Frame包含特征点,姿态,光照,图像(Camera预览)等信息
public Frame update()
//暂停
public void pause()
//用来获取现实世界(Camera预览)的纹理ID
public void setCameraTextureName(int textId)
//获取所有锚点
public Collection getAllAnchors()
//获取所有Trackable,目前Plane和Point都是继承于此
public  Collection getAllTrackables(Class trackable)
//根据Pose创建锚点
public Anchor createAnchor(Pose var1) 
//获取相机所有的支持项
public List getSupportedCameraConfigs()
//设置camera的config
public void setCameraConfig(CameraConfig config) 
  • 调用逻辑
    在onResume中创建Session,并resume session
protected void onResume() {
    super.onResume();
    if (session == null) {
      Exception exception = null;
      String message = null;
        // ARCore requires camera permissions to operate. If we did not yet obtain runtime
        // permission on Android M and above, now is a good time to ask the user for it.
        if (!CameraPermissionHelper.hasCameraPermission(this)) {
          CameraPermissionHelper.requestCameraPermission(this);
          return;
        }

        // Create the session.
        session = new Session(/* context= */ this);

      } catch (UnavailableArcoreNotInstalledException
          | UnavailableUserDeclinedInstallationException e) {
        message = "Please install ARCore";
        exception = e;
      } catch (UnavailableApkTooOldException e) {
        message = "Please update ARCore";
        exception = e;
      } catch (UnavailableSdkTooOldException e) {
        message = "Please update this app";
        exception = e;
      } catch (UnavailableDeviceNotCompatibleException e) {
        message = "This device does not support AR";
        exception = e;
      } catch (Exception e) {
        message = "Failed to create AR session";
        exception = e;
      }

      if (message != null) {
        messageSnackbarHelper.showError(this, message);
        Log.e(TAG, "Exception creating session", exception);
        return;
      }
    }

    // Note that order matters - see the note in onPause(), the reverse applies here.
    try {
      session.resume();
    } catch (CameraNotAvailableException e) {
      // In some cases (such as another camera app launching) the camera may be given to
      // a different app instead. Handle this properly by showing a message and recreate the
      session = null;
      return;
    }
    surfaceView.onResume();
    displayRotationHelper.onResume();
  }
  • 绘制时,从session中读取trackable,Frame,并从Frame中读取光照,Camera(相当于视角并非照相机),Pose,Image等
    更新display信息,主要是横竖屏切换等窗口变化
  @Override
  public void onDrawFrame(GL10 gl) {
    // Clear screen to notify driver it should not load any pixels from previous frame.
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

    if (session == null) {
      return;
    }
    // Notify ARCore session that the view size changed so that the perspective matrix and
    // the video background can be properly adjusted.
    displayRotationHelper.updateSessionIfNeeded(session);

从session中读取Frame,Plane,从Frame读取Camera(视角),Image,电源,光照估计等信息

      //更新camera 预览
      session.setCameraTextureName(backgroundRenderer.getTextureId());

      // Obtain the current frame from ARSession. When the configuration is set to
      // UpdateMode.BLOCKING (it is by default), this will throttle the rendering to the
      // camera framerate.
      Frame frame = session.update();
      Camera camera = frame.getCamera();
      // Handle one tap per frame.
      handleTap(frame, camera);

      // If not tracking, don't draw 3d objects.
      if (camera.getTrackingState() == TrackingState.PAUSED) {
        return;
      }
      // Get projection matrix.
      float[] projmtx = new float[16];
      camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
      // Get camera matrix and draw.
      //获取模型矩阵
      float[] viewmtx = new float[16];
      camera.getViewMatrix(viewmtx, 0);

      // Compute lighting from average intensity of the image.
      // The first three components are color scaling factors.
      // The last one is the average pixel intensity in gamma space.
      final float[] colorCorrectionRgba = new float[4];
      //获取光线
      frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0);
      // Visualize tracked points.
      //获取点云
      PointCloud pointCloud = frame.acquirePointCloud();

根据模型矩阵,光照估计,投影矩阵绘制虚拟物体

 backgroundRenderer.draw(frame);
 pointCloudRenderer.update(pointCloud);
 pointCloudRenderer.draw(viewmtx, projmtx);
 // Visualize planes.
 planeRenderer.drawPlanes(
          session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx);
   coloredAnchor.anchor.getPose().toMatrix(anchorMatrix, 0);

   // Update and draw the model and its shadow.
virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
virtualObjectShadow.updateModelMatrix(anchorMatrix, scaleFactor);
virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, coloredAnchor.color);
virtualObjectShadow.draw(viewmtx, projmtx, colorCorrectionRgba, coloredAnchor.color);

小结

ARCore在运行时同时打开两路stream,后续分析原因是和SLAM算法相关,SLAM算法中,FOV越大,效果越好,所以用于SLAM的运算的Stream是一路,渲染的是一路Stream
ARCore比较简单,但是需要有一定的OpenGL基础,ArCore里面有很多新概念需要理解;没有OpenGL的基础,理解起来会费力很多。
国内手机对ARCore支持不太好(好像很少人用过Google play),运行的时候比较坑爹,国内大部分机器用不了。
最重要的一点,理解ARCore,ArCore的核心是运动跟踪、环境理解、光估计等,通过核心算法,使虚拟在环境中更加真实,显示逼真是最终结果。

参考

Github:ARCore

你可能感兴趣的:(ARCore的原理和应用)