在本文中,我们将深入研究 Android AR 的世界,即增强现实,特别是 ARCore,谷歌用于构建 AR 体验的平台。我们将看到 ARCore 如何通过抽象出复杂的矩阵和矢量数学并为我们提供用于 AR 开发的漂亮 API 来改变 AR 应用程序开发。

首先,让我们看看增强现实是什么,以及为什么我们作为开发人员应该对这项新技术感到非常兴奋!

1、什么是增强现实?

根据 定义,增强现实是“一种将计算机生成的图像叠加在用户对现实世界的看法上,从而提供复合视图的技术”。

从本质上讲,AR 是一种技术,它使我们能够将计算机生成的 3D 对象模型渲染到现实世界中,并让它与周围环境进行交互,就好像它实际存在于同一位置一样。

该技术在以下领域有广泛的应用:

  • 教育:想象一下在你的办公桌上有一个人脑的 3D 模型。
  • 旅游:将流行古迹的 3D 模型放置在物理世界中。
  • 家具零售:购买前检查椅子在客厅中的外观指南。
  • 电子商务:在你面前以 3D 形式查看新服装。
  • 医学和医疗保健:在化学实验室内拥有药物中存在的各种蛋白质的 3D 模型。
  • 还有很多…

几年前,开发 AR 应用程序意味着学习 OpenGL 和复杂的矢量数学。2018 年,Google 发布了 ARCore 以及 Sceneform SDK(适用于 android),以使每个人都可以更轻松地进行 AR 开发。那么,让我们来看看 ARCore 提供了什么。

2、什么是 ARCore?

根据 Google提供的定义,ARCore 是一个用于构建 Android AR 体验的平台。它使你的手机能够感知其环境、了解世界并与信息交互。

ARCore 遵循 3 个原则:

  • 运动跟踪:它允许手机了解其相对于现实世界的当前位置。
  • 了解环境:它允许手机检测所有类型表面的大小和位置:垂直、水平和倾斜。
  • 光照估计:它可以让手机感知环境的光照条件。

当用户在现实世界中移动他/她的手机时,ARCore 能够了解其周围环境并以数字方式模拟现实世界,它可以在其中放置物体。运动跟踪帮助 ARCore 识别特征,使其能够跟踪其与真实环境相关的位置。

截至目前,ARCore 可用于:

  • Java (安卓)
  • 统一(iOS 和 Android)
  • 虚幻引擎
  • iOS

此列表涵盖了大多数用于 AR 应用程序开发的设备和开发平台:

3、场景形式

ARCore 本身不是 SDK,而是帮助 SDK 渲染对象的引擎。因此,为了利用这一功能,Google 发布了 Sceneform SDK,让开发者无需学习 OpenGL 即可构建 Android AR 应用。

Sceneform 具有许多漂亮的功能,例如:

  • 对支持 ARCore 的手机进行自动兼容性检查。
  • 检查相机权限。
  • 用于抽象所有复杂性的场景图 API。
  • 用于操作 3D 资产的插件。

我们现在将深入研究使用 Sceneform 构建示例 Android AR 应用程序,构建一个简单的 Android AR 应用程序并将一些 3D 对象渲染到现实世界中,这将帮助你更深入地了解 Sceneform 和 ARCore。

完成的应用程序如下所示:

WhatsApp视频20190518at93-4n5qy

4、开发环境设置

让我们开始吧,首先创建一个带有空活动的新项目。

5、添加 Sceneform 插件

你需要将 Sceneform 插件安装到 android studio。Sceneform 插件将帮助你完成诸如将模型导入 Android 项目等任务。

为了安装插件,请按照以下步骤操作:

  • 对于 Windows 用户:转到:文件-> 设置-> 插件
  • 对于 macOS 用户:转到:Android Studio-> 首选项-> 插件
  • 现在在搜索栏中输入“场景形式”。它将位于名为 Google Sceneform Tools 的顶部。
  • 安装插件并重启android studio。
  • 添加依赖项

将以下依赖项添加到你的应用级 build.gradle 文件中:

implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.9.0'

重要提示:Sceneform SDK 要求 minSdkVersion 大于或等于 24。因此,请确保设置 minSdkVersion>= 24。此外,请确保已将 Maven 存储库包含在项目级别的 build.gradle 中。

  • 更新清单

在你的 AndroidManifest.xml 文件中添加以下行:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:name="android.hardware.camera.ar" android:required="true"/>

使用 ARCore 需要摄像头权限和支持摄像头的手机(显然)。

此外,将元数据添加到你的应用程序标签:

<meta-data
   android:name="com.google.ar.core"
   android:value="required" />

如果你的应用严格要求设备启用 ARCore,则设置 required = true 或者如果 AR 不是主要功能或者你已经处理了非兼容设备的兼容性,则可以设置 required = false。

  • 添加 ArFragment

完成所有初始设置后,现在可以开始将 ArFragment(在 Sceneform SDK 中提供)添加到我们的应用程序中。ArFragment 自动处理你的会话和应用程序工作所需的运行时检查。

如果用户的设备上没有安装 ARCore,ArFragment 会敦促用户安装 ARCore。此外,如果未授予摄像头权限,它也会请求摄像头权限。因此,ArFragment 是开始构建您的第一个 Android ARCore 应用程序的最佳方式。

但是,如果你的应用仍然需要一些扩展功能,你始终可以继承 ArFragment 并创建自己的 Fragment 来支持自定义功能。

这是布局 xml 文件的内容:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".HelloSceneformActivity">

 <fragment android:name="com.google.ar.sceneform.ux.ArFragment"
     android:id="@+id/ux_fragment"
     android:layout_width="match_parent"
     android:layout_height="match_parent" />

</FrameLayout>
  • 在运行时检查兼容性

我们将检查设备是否:

  1. 正在运行 Android API 版本 >= 24。
  2. 可以支持OpenGL 3.0版本。

上述条件是设备支持使用 ARCore 和 Sceneform SDK 的 AR 应用的必备条件。

如果不满足这些条件,我们打算完成活动。但是,你仍然可以继续支持其他功能。

  • 将 3D 模型添加到我们的应用程序

现在是时候下载并导入要渲染到我们的应用程序中的 3D 模型了。在我们的例子中,我们将在房间的一角渲染一把 3D 椅子并移动它。

你可以从任何地方下载 3D 模型,但 Google 提供了一个出色的存储库 POLY 来为您的应用程序下载 3D 模型。你可以下载 .obj 或 .gltf 格式的模型。我们将下载 .obj 文件。

在你的 android studio 项目中打开项目视图并展开 app 文件夹。你会注意到一个名为“sampledata”的文件夹。如果没有,请继续创建一个。

模型下载完成后,你需要将下载的 zip 文件解压缩到此示例数据文件夹中。

你将找到模型的 .mtl 文件、.obj 文件和 png 图像。我们将使用 sceneform 插件在我们的应用程序中导入 .obj 文件。

  • 使用 Sceneform 插件导入模型

右键单击 .obj 文件,你将找到一个选项“导入 Sceneform 资源”。单击它并将设置保留为默认值。完成导入后,gradle 将同步项目以将资产包含在您的应用程序中。

这样,你就完成了将 3D 资产导入应用程序的过程。现在是时候编写一些代码将模型包含到 AR 场景中了。

6、构建模型

在您的 java 文件中添加以下代码:

protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);

   if (!checkIsSupportedDeviceOrFinish(this)) {
       return;
   }

   setContentView(R.layout.activity_ux);
   arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);

   arFragment.setOnTapArPlaneListener(
           (HitResult hitResult, Plane plane, MotionEvent motionEvent) -> {

               Anchor anchor = hitResult.createAnchor();
               placeObject(arFragment, anchor, Uri.parse("model.sfb"));

           });
}

让我们看看这里发生了什么。

  1. 首先,我们在 supportFragmentManager 和片段 id 的帮助下获得了我们在布局文件中添加的片段。
  2. 然后我们需要将模型加载到场景中。为此,我们使用 Sceneform SDK 提供的 ModelRenderable 类。借助 ModelRenderable 的 setSource() 方法,我们可以通过传入生成的 .sfb 文件的名称来加载我们的模型。
  3. 模型是在后台线程上构建的,因此在加载模型后,它会呈现给主线程,然后将其渲染到场景中。
  4. 我们在 thenAccept 方法中接收模型。如果在构建模型时出现任何错误,则会引发异常。

我们的模型已加载,现在让我们将其放入场景中。

7、将模型添加到 AR 场景

我们的 AR 片段是场景的容器,因此需要在片段被点击时添加一个模型。因此,我们将在片段中添加一个 onTapListener。

private void placeObject(ArFragment arFragment, Anchor anchor, Uri uri) {
   ModelRenderable.builder()
           .setSource(arFragment.getContext(), uri)
           .build()
           .thenAccept(modelRenderable -> addNodeToScene(arFragment, anchor, modelRenderable))
           .exceptionally(throwable -> {
                       Toast.makeText(arFragment.getContext(), "Error:" + throwable.getMessage(), Toast.LENGTH_LONG).show();
                       return null;
                   }

           );

}

private void addNodeToScene(ArFragment arFragment, Anchor anchor, Renderable renderable) {
   AnchorNode anchorNode = new AnchorNode(anchor);
   TransformableNode node = new TransformableNode(arFragment.getTransformationSystem());
   node.setRenderable(renderable);
   node.setParent(anchorNode);
   arFragment.getArSceneView().getScene().addChild(anchorNode);
   node.select();
}


public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) {
   if (Build.VERSION.SDK_INT < VERSION_CODES.N) {
       Log.e(TAG, "Sceneform requires Android N or later");
       Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show();
       activity.finish();
       return false;
   }
   String openGlVersionString =
           ((ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE))
                   .getDeviceConfigurationInfo()
                   .getGlEsVersion();
   if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) {
       Log.e(TAG, "Sceneform requires OpenGL ES 3.0 later");
       Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG)
               .show();
       activity.finish();
       return false;
   }
   return true;
}

使用 hitResult,我们可以获得被点击的位置并创建一个锚节点,它是我们场景的根节点(将增强现实场景图像为倒置树)。

接下来,我们创建一个 TransformableNode 作为椅子并将其设置为锚节点。当用户拖动对象或使用捏合缩放时,可变形节点可以对位置变化和大小变化做出反应。

让我们看一下这里的一些术语:

  • 场景:这是我们的 3D 世界将被渲染的地方。
  • HitResult:它是来自无限远的假想光线,它与现实世界的第一个交点是点击点。
  • 锚点:现实世界中的一个固定位置。用于将本地坐标(根据用户的显示)转换为真实世界坐标。
  • TransformableNode:可以对用户交互(例如旋转、缩放和拖动)做出反应的节点。

这是最终的 java 文件的样子:

public class HelloSceneformActivity extends AppCompatActivity {
   private static final String TAG = HelloSceneformActivity.class.getSimpleName();
   private static final double MIN_OPENGL_VERSION = 3.0;

   private ArFragment arFragment;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       if (!checkIsSupportedDeviceOrFinish(this)) {
           return;
       }

       setContentView(R.layout.activity_ux);
       arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);

       arFragment.setOnTapArPlaneListener(
               (HitResult hitResult, Plane plane, MotionEvent motionEvent) -> {
                   Anchor anchor = hitResult.createAnchor();
                   placeObject(arFragment, anchor, Uri.parse("model.sfb"));

               });
   }

   private void placeObject(ArFragment arFragment, Anchor anchor, Uri uri) {
       ModelRenderable.builder()
               .setSource(arFragment.getContext(), uri)
               .build()
               .thenAccept(modelRenderable -> addNodeToScene(arFragment, anchor, modelRenderable))
               .exceptionally(throwable -> {
                           Toast.makeText(arFragment.getContext(), "Error:" + throwable.getMessage(), Toast.LENGTH_LONG).show();
                           return null;
                       }

               );

   }

   private void addNodeToScene(ArFragment arFragment, Anchor anchor, Renderable renderable) {
       AnchorNode anchorNode = new AnchorNode(anchor);
       TransformableNode node = new TransformableNode(arFragment.getTransformationSystem());
       node.setRenderable(renderable);
       node.setParent(anchorNode);
       arFragment.getArSceneView().getScene().addChild(anchorNode);
       node.select();
   }


   public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) {
       if (Build.VERSION.SDK_INT < VERSION_CODES.N) {
           Log.e(TAG, "Sceneform requires Android N or later");
           Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show();
           activity.finish();
           return false;
       }
       String openGlVersionString =
               ((ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE))
                       .getDeviceConfigurationInfo()
                       .getGlEsVersion();
       if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) {
           Log.e(TAG, "Sceneform requires OpenGL ES 3.0 later");
           Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG)
                   .show();
           activity.finish();
           return false;
       }
       return true;
   }


}

就是这样!我们已经构建了一个功能齐全的 Android AR 应用程序。你可以在github上查看整个源代码 。


原文链接:Build your first Android AR app using ARCore and Sceneform

BimAnt翻译整理,转载请标明出处