实战教程:如何从零开始构建一个极简的 Android 增强现实(AR)应用

你是否曾梦想过像钢铁侠托尼·斯塔克那样,在现实的空气中操作全息数据?增强现实(AR)技术正在将这种科幻构想变为触手可及的科学现实。通过将数字信息——如声音、文本、3D 模型——叠加到我们的物理世界中,AR 打破了虚拟与现实的边界。想想风靡全球的“Pokémon GO”,你就能感受到 AR 的魅力。

在这篇文章中,我们将作为一名探索者,一起深入 Android AR 开发的世界。我们将不依赖复杂的 3D 引擎知识,使用 Java 语言在 Android Studio 中构建一个功能完整、能够识别平面并展示 3D 模型的 AR 应用。准备好你的手机,让我们开始这段神奇的代码之旅吧!

为什么选择 ARCore 与 Sceneform?

在开始敲代码之前,我们需要理解背后的核心工具。Google 的 ARCore 是 Android AR 体验的基石,而 Sceneform 则是我们手中的画笔。

#### 深入了解 ARCore

ARCore 是一个平台,它让我们的智能手机能够“感知”周围的环境。要在手机上实现稳定的 AR 体验,ARCore 主要依赖三个核心原则:

  • 运动追踪:利用手机的摄像头和惯性传感器,ARCore 能在用户移动时追踪手机的位置和姿态。这确保了虚拟物体在屏幕中看起来是“钉”在现实世界中的,而不是随着手机晃动而乱飞。
  • 环境理解:这是 AR 的关键。ARCore 会检测水平或垂直的平面(如地板、桌子或墙壁)。通过识别这些特征点,手机能知道哪里有“地面”,从而让虚拟物体有地方放置。
  • 光照估算:为了逼真,虚拟物体必须有阴影。ARCore 会分析环境的光线条件,让我们的 3D 模型看起来真的像是处于当前的灯光下。

> 注意:ARCore 仅在支持它的设备上运行。虽然现在大部分新手机都支持,但开发前最好查阅 Google 的设备支持列表。

#### Sceneform 的魅力

以前,在 Android 上写 AR 需要懂 OpenGL,这对许多应用开发者来说是一道高墙。Sceneform 是一个由 Google 开发的 3D 框架,它的存在就是为了打破这道墙。它让我们可以在不懂 OpenGL 的情况下,像操作普通 Android 视图一样处理 3D 资产。它甚至自带了处理 UI 手势(如点击旋转、缩放)的功能。

第一步:搭建项目地基

首先,我们需要一个干净的起点。打开 Android Studio,创建一个新项目(请参考标准的新建项目流程)。但在配置时,请注意以下几点:

  • 语言选择:选择 Java。虽然 Kotlin 现在很流行,但为了教程的通用性和稳定性,我们沿用 Java。
  • Minimum SDK:设置为 API 24: Android 7.0 (Nougat)。这是保证 ARCore 兼容性的基础。
  • 记住路径:记下你的项目保存路径,稍后我们需要在那里放入一些特殊的文件。

第二步:准备 3D 资产(.glb 文件)

AR 应用需要内容。在这个极简示例中,我们需要一个 3D 模型。

#### 关于文件格式:glTF 与 .glb

Sceneform 1.16.0 主要使用 glTF(GL Transmission Format)格式。它是 3D 界的“JPEG”。而 .glb 文件则是 glTF 的二进制版本,它将纹理、模型数据打包在一个文件中,非常适合移动端传输和加载。

#### 如何获取模型?

你有两个选择:下载现成的或者自己制作。

  • 下载资源:有很多优秀的 3D 模型库(Sketchfab 等以前提到的 Poly 平台虽已整合,但社区资源依然丰富)。搜索 .glb 格式的模型即可。
  • 自己动手:如果你喜欢创造,可以使用 Blender(一款免费且强大的开源 3D 软件)制作模型,然后导出为 .glb 格式。

#### 导入项目

这是一个关键步骤,请务必仔细操作:

  • 命名规范:确保你的文件名仅包含小写字母、数字或下划线。例如 my_model.glb。避免使用大写字母,这可能会在资源编译中引发错误。
  • 创建 INLINECODE5be8890d 目录:在 Android Studio 左侧项目视图中,右键点击 INLINECODE67e97f74 目录,选择 New > Android Resource Directory。在弹出的窗口中,资源类型选择 Raw,点击 OK。
  • 放置文件:将你下载或制作的 INLINECODE26afdf2a 文件复制到 INLINECODE2879c88a 文件夹下。

第三步:集成 Sceneform SDK(手动配置)

这里我们可能会遇到一些挑战。早期的 Sceneform 插件在最新版 Android Studio 中可能会出现兼容性问题。为了保证我们的项目能跑起来,我们将采用更稳妥的方式:手动集成 Sceneform 1.16.0 SDK。

#### 1. 获取 SDK 文件

我们需要从 GitHub 的 Sceneform 仓库中下载源码包(下载链接指向 v1.16.0 版本的 Release)。解压 ZIP 文件,找到其中的 INLINECODE521e154b 和 INLINECODE43d54390 文件夹。

#### 2. 配置 Settings.gradle

将解压后的 INLINECODEa2b9de25 和 INLINECODE90a7bc06 文件夹直接移动到你的项目根目录下(与 INLINECODEbc876d26 文件夹同级)。然后,打开 INLINECODEeeec9433 文件,添加以下配置。这将告诉 Gradle 去哪里找这两个库。

// 将 sceneformsrc 文件夹作为模块引入
include ‘:sceneform‘
project(‘:sceneform‘).projectDir = new File(‘sceneformsrc/sceneform‘)

// 将 sceneformux 文件夹作为模块引入
include ‘:sceneformux‘
project(‘:sceneformux‘).projectDir = new File(‘sceneformsrc/sceneformux‘)

#### 3. 配置 Build.gradle

接下来,我们需要在 INLINECODEb30baf83 模块中引用这些库。打开 INLINECODE958c8c9e。

dependencies 闭包中添加依赖:

dependencies {
    ...
    // 依赖我们刚才导入的 sceneformux 库
    implementation project(‘:sceneformux‘)
    ...
}

> 实用见解:由于 Java 8 的特性在 AR 开发中非常常用,建议在同一文件的 android 闭包中,确保开启了 Java 8 支持(虽然现代 AS 版本通常默认开启,但检查一下总是好的)。

第四步:编写核心代码——从布局到逻辑

现在,地基已经打好,是时候建造大楼了。我们将分三步来完成代码编写。

#### 1. 设计用户界面

我们需要一个全屏的视图来显示摄像头画面。打开 INLINECODEcee6d931,并将代码修改如下。这里我们使用了 INLINECODEae704518,它是 Sceneform 提供的一个现成的 Fragment,帮我们处理了权限请求、相机开启和背景渲染等繁琐工作。





    
    


#### 2. 实现主逻辑

接下来是 MainActivity.java。这是我们将一切串联起来的地方。我们要做的是:

  • 获取 ArFragment 的引用。
  • 添加一个监听器,等待 ARCore 检测到平面。
  • 当检测到平面后,添加另一个监听器,等待用户点击屏幕。
  • 当用户点击屏幕上的平面时,显示 3D 模型。
// MainActivity.java
package com.example.simplearapp; // 替换为你的包名

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.view.MotionEvent;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.google.ar.core.HitResult;
import com.google.ar.core.Plane;
import com.google.ar.sceneform.AnchorNode;
import com.google.ar.sceneform.rendering.ModelRenderable;
import com.google.ar.sceneform.ux.ArFragment;
import com.google.ar.sceneform.ux.TransformableNode;

public class MainActivity extends AppCompatActivity {

    private ArFragment arFragment;
    // 使用 URI 格式引用我们在 res/raw 中放入的模型文件
    // 注意:格式固定为 android.resource://包名/raw/文件名(不含扩展名)
    private Uri modelUri = Uri.parse("android.resource://" + getPackageName() + "/raw/your_model_filename");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化 ArFragment
        arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.arFragment);

        // 开始构建我们的 AR 场景
        setUpModel();
        setUpPlane();
    }

    // 辅助方法:动态获取包名(因为 onCreate 中 getPackageName() 可能还未完全初始化)
    @Override
    protected String getPackageName() {
        return super.getPackageName();
    }

    // 步骤 1: 异步加载 3D 模型
    private void setUpModel() {
        // 动态修正 URI,确保在运行时获取正确的包名
        modelUri = Uri.parse("android.resource://" + getPackageName() + "/raw/your_model_filename"); 
        // 请确保将 ‘your_model_filename‘ 替换为你真实的文件名,例如 ‘gfg_model‘

        ModelRenderable.builder()
                .setSource(this, modelUri)
                .setIsFilamentGltf(true) // Sceneform 1.16+ 使用 Filament 引擎加载 glTF
                .build()
                .thenAccept(renderable -> {
                    // 加载成功,将 renderable 存起来(这里简化处理,直接在点击事件中引用)
                    // 在实际开发中,建议将其设为成员变量以便复用
                    Toast.makeText(this, "模型加载成功", Toast.LENGTH_SHORT).show();
                })
                .exceptionally(throwable -> {
                    Toast.makeText(this, "无法加载 3D 模型,请检查文件名和格式", Toast.LENGTH_LONG).show();
                    return null;
                });
    }

    // 步骤 2: 设置平面检测和点击监听
    private void setUpPlane() {
        arFragment.setOnTapArPlaneListener((HitResult hitResult, Plane plane, MotionEvent motionEvent) -> {
            
            // 检查点击的是否是水平面(地板或桌子)
            if (plane.getType() != Plane.Type.HORIZONTAL_UPWARD_FACING) {
                return;
            }

            // 创建锚点
            AnchorNode anchorNode = new AnchorNode(hitResult.createAnchor());
            anchorNode.setParent(arFragment.getArSceneView().getScene());

            // 再次加载模型(为了代码逻辑清晰,这里简化,实际应复用已加载的对象)
            // 注意:真实项目中应避免在点击时重复加载,这里仅为演示流程
            ModelRenderable.builder()
                    .setSource(this, modelUri)
                    .setIsFilamentGltf(true)
                    .build()
                    .thenAccept(renderable -> {
                        createNode(anchorNode, renderable);
                    });
        });
    }

    // 步骤 3: 创建可交互的节点
    private void createNode(AnchorNode anchorNode, ModelRenderable renderable) {
        // TransformableNode 让我们可以通过手指拖拽、旋转、缩放模型
        TransformableNode node = new TransformableNode(arFragment.getTransformationSystem());
        node.setParent(anchorNode);
        node.setRenderable(renderable);
        node.select(); // 激活节点,显示控制手柄
    }
}

> 重要提示:在代码中,请务必将 INLINECODEa48e8808 替换为你放在 INLINECODE5d47309d 文件夹下的实际文件名(不需要 INLINECODEc2e15154 后缀)。例如,如果你的文件是 INLINECODE30cde459,这里就写 teddy

#### 3. 配置权限

AR 应用必须使用摄像头。虽然 ArFragment 会自动请求权限,但我们必须在 INLINECODEc927099d 中声明它。打开该文件,在 INLINECODE47c56dc6 标签外添加:




常见陷阱与优化建议

在实际开发中,你可能会遇到一些坑。这里分享几点实战经验:

  • 模型不显示?:最常见的原因是模型尺寸不合适。现实世界非常大,如果你的模型太小(例如 0.001 单位),它可能就在你眼皮底下,你却看不见。反之,如果太大(100 单位),它可能包围了整个相机。在 Blender 中导出时,注意调整模型的缩放比例。
  • APK 体积过大:3D 模型文件通常很重。如果 App 启动慢,请检查 .glb 文件的大小。优化模型的纹理和面数是减小 APK 体积的关键。
  • 灯光与环境:为了让效果更逼真,我们还可以在代码中开启光照估计。Sceneform 支持根据环境光调整模型的亮度。只需一行代码:arFragment.getArSceneView().getSession().setLightEstimationEnabled(true); (需在 Session 创建后调用)。

结语

恭喜你!你已经完成了从 0 到 1 的突破。通过这篇教程,我们不仅构建了一个简单的 AR 应用,更重要的是,我们掌握了 ARCore 的基本原理(环境感知、锚点建立)和 Sceneform 的核心用法(模型加载、节点交互)。

但这只是冰山一角。接下来,你可以尝试探索更多功能:比如让 3D 模型动起来(动画)、支持识别垂直平面(像海报一样的墙面)、或者使用 Cloud Anchors 让不同用户看到同一个虚拟物体。AR 的未来充满无限可能,而你已经拿到了开启这扇大门的钥匙。快去创建属于你的增强现实世界吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/53473.html
点赞
0.00 平均评分 (0% 分数) - 0