我在 Android 上做了一个 1 : 1 高达

简介: 我在 Android 上做了一个 1 : 1 高达

最近看到一个新闻,一个 1:1 的自由高达落户在上海金桥。

作为高达爱好者,我一直想去现场感受一下真实高达的压迫感,无奈一直没机会成行。不过这难不倒我,我决定自己动手做一个 1:1 高达来体验一番。

借助 AR 技术我实现了这个效果, 怎么样,不比上海金桥的差吧 ~

image.png

什么是 AR (Augemented Reality)

AR(增强现实)是近几年新兴的技术,他可以将3D模型等虚拟元素模拟仿真后应用到现实世界中,虚拟与现实,两种信息互为补充,从而实现对真实世界的“增强”。

不少人会将 AR(增强现实) 与 VR(虚拟现实)相混淆,两者的区别在于虚拟元素的占比:

  • VR:看到的场景和人物全是假的,是把你的意识代入一个虚拟的世界。
  • AR:看到的场景和人物一部分是真一部分是假,是把虚拟的信息带入到现实世界中。A
  • image.png

VR 技术的研究已经有30多年的历史了,而 AR 则年轻得多,随着智能手机以及智能穿戴设备的普及而逐渐被人们熟知。相对于 VR,AR 的开发门槛低得多,只要有一台智能手机,借助 Google 提供的 ARCore,人人都可以开发出自己的 AR 应用。

image.png

ARCore 是 Google 提供的 AR 解决方案,为开发者提供 API,可以通过 Android , iOS 等手机平台感知周边环境,打造沉浸式的 AR 体验。

ARCore 为开发者提供了三大能力:

image.png

ARCore 为 AR 提供了周边环境的感知能力,但一个完整的 AR 应用还需要处理 3D 模型的渲染,这要借助 OpenGL ES 来完成,学习成本很高。官方意识到这个问题,在 2017 年推出 ARCore 之后,紧跟着 2018 年的 IO 大会上推出了 Sceneform 这个在 Android 上的 3D 图像渲染库。

.obj , .fbx.gltf 等常见的 3D 模型文件格式,虽然可以在主流的 3D 软件中通用,但在 Android 中,我们只能通过 OpenGL 代码对其进行渲染。而 Sceneform 可以将这些格式的模型文件,连同所依赖的资源文件(.mtl, .bin, .png 等) 转换为 .sfa.sfb 格式。 后者是可供 Sceneform 渲染的二进制模型文件, 前者是具有可读性的摘要文件,用来描述后者。

相比于 OpenGL , Sceneform 的使用要简单得多,而且 sfb 还可以通过 Sceneform 提供的 AS 插件在 IDE 中进行模型预览。

image.png

接下来,通过 Sceneform 和 ARCore 来实现我的 1:1 高达

1. Gradle 添加依赖

新建 AndroidStudio 工程,在 root 的 build.gradle 中添加 Sceneform 插件

dependencies { 
    classpath 'com.google.ar.sceneform:plugin:1.17.1' 
}

接着在 app 的 build.gradle 中依赖 ARCore 和 Sceneform 的 aar

dependencies {
    ...
    implementation 'com.google.ar:core:1.26.0'
    implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.17.1'
    implementation 'com.google.ar.sceneform:core:1.17.1'
}

2. Manifest 申请权限

<uses-permission android:name="android.permission.CAMERA"/>
<!-- 此 App 在 GooglePlay 中只会对支持 ARCore 的设备可见 -->
<uses-feature android:name="android.hardware.camera.ar" android:required="true"/>
<application …>
  <!-- 当安装 App 时,如果设备没有安装 ARCore,GooglePlay 会自动为其安装 -->
  <meta-data android:name="com.google.ar.core" android:value="required" />
</application>

3. 布局文件

ARFragment 可以用来承载 AR 场景、响应用户行为,Android 中显示虚拟元素的最简答的方法就是在布局中添加一个 ARFRagment :

<?xml version="1.0" encoding="utf-8"?>
<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=".MainActivity">
<fragment
        android:id="@+id/ux_fragment"
        android:name="com.google.ar.sceneform.ux.ArFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="9" />
</FrameLayout>

4. 制作 sfb 模型文件

3D 模型一般是通过 Maya 或 3D Max 等专业软件制作的,不少 3D 建模爱好者会将自己的作品上传到一些设计网站供大家免费或有偿下载。

image.png

我们可以在网站上下载常见格式的 3D 模型文件。以 .obj 为例,obj 文件中描述了多边形的顶点和片段信息等, 此外还有颜色、材质等信息存储在配套的 .mtl 文件中 , 我们将下载的 obj/mtl/png 等模型文件拷贝到非 assets 目录下,这样可以避免打入 apk。

例如 app/sampledata

image.png

我们在 build.gtadle 通过 sceneform.asset(...) 添加 obj > sfb 的配置如下

sceneform.asset('sampledata/msz-006_zeta_gundam/scene.obj',
        'default',
        'sampledata/msz-006_zeta_gundam/scene.sfa',
        'src/main/assets/scene')

sampledata/msz-006_zeta_gundam/scene.obj 是 obj 源文件位置, src/main/assets/scene 是生成的 sfb 目标路径,我们将目标文件生成在 assets/ 中,打入 apk ,便于在运行时加载。

gradle 配置完后,sync 并 build 工程,build 过程中,会在 assets/ 中生成同名 sfb 文件

5. 加载、渲染模型

//MainActivity.kt
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        arFragment = supportFragmentManager.findFragmentById(R.id.ux_fragment) as ArFragment
        arFragment.setOnTapArPlaneListener { hitResult, plane, motionEvent ->
            if (plane.type != Plane.Type.HORIZONTAL_UPWARD_FACING ||
                    state != AnchorState.NONE) {
                return@setOnTapArPlaneListener
            }
            val anchor = hitResult.createAnchor()
            placeObject(ux_fragment, anchor, Uri.parse("scene.sfb"))
        }
    }

ARFragment 能够响应在 AR 场景中的用户点击行为,在点击的位置中添加虚拟元素, Uri.parse("scene.sfb") 用来获取 assets 中生成的模型文件。

    private fun placeObject(fragment: ArFragment, anchor: Anchor, model: Uri) {
        ModelRenderable.builder()
                .setSource(fragment.context, model)
                .build()
                .thenAccept {
                    addNodeToScene(fragment, anchor, it)
                }
                .exceptionally { throwable : Throwable ->
                   Toast.makeText(arFragment.getContext(), "Error:$throwable.message", Toast.LENGTH_LONG).show();
                   return@exceptionally null
                }
    }

Sceneform 提供 ModelRenderable 用于模型渲染。 通过 setSource 加载 sfb 模型文件

   private fun addNodeToScene(fragment: ArFragment, anchor: Anchor, renderable: Renderable) {
        val anchorNode = AnchorNode(anchor)
        val node = TransformableNode(fragment.transformationSystem)
        node.renderable = renderable
        node.setParent(anchorNode)
        fragment.arSceneView.scene.addChild(anchorNode)
        node.select()
    }

ARSceneView 持有一个 Scene, Scene 是一个树形数据结构,作为 AR 场景的根节点,各种虚拟元素将作为其子节点被添加到场景中进行渲染

val node = TransformableNode(fragment.transformationSystem)
node.renderable = renderable
node.setParent(anchorNode)

所以,渲染 3D 模型,其实就是添加一个 Node 并为其设置 Renderable 的过程。

hitResult 是用户点击的位置信息,Anchor基于 hitResult 创建锚点,这个锚点作为子节点被添加到 Scene 根节点中,同时又作为 TransformableNode 的父节点。 TransformableNode 用来承载 3D 模型, 它可以接受手势进行拖拽或者放大缩小, 添加到 Archor 就相当于把 3D 模型放置到点击的位置上。

6. 完整代码

class MainActivity : AppCompatActivity() {
    lateinit var arFragment: ArFragment
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (!checkIsSupportedDeviceOrFinish(this)) return
        setContentView(R.layout.activity_main)
        arFragment = supportFragmentManager.findFragmentById(R.id.ux_fragment) as ArFragment
        arFragment.setOnTapArPlaneListener { hitresult: HitResult, plane: Plane, motionevent: MotionEvent? ->
            if (plane.type != Plane.Type.HORIZONTAL_UPWARD_FACING)
                return@setOnTapArPlaneListener
            val anchor = hitresult.createAnchor()
            placeObject(arFragment, anchor, R.raw.cube)
        }
    }
    private fun placeObject(arFragment: ArFragment, anchor: Anchor, uri: Int) {
        ModelRenderable.builder()
                .setSource(arFragment.context, uri)
                .build()
                .thenAccept { modelRenderable: ModelRenderable -> addNodeToScene(arFragment, anchor, modelRenderable) }
                .exceptionally { throwable: Throwable ->
                   Toast.makeText(arFragment.getContext(), "Error:$throwable.message", Toast.LENGTH_LONG).show();
                    return@exceptionally null
                }
    }
    private fun addNodeToScene(arFragment: ArFragment, anchor: Anchor, renderable: Renderable) {
        val anchorNode = AnchorNode(anchor)
        val node = TransformableNode(arFragment.transformationSystem)
        node.renderable = renderable
        node.setParent(anchorNode)
        arFragment.arSceneView.scene.addChild(anchorNode)
        node.select()
    }
    private fun checkIsSupportedDeviceOrFinish(activity: Activity): Boolean {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show()
            activity.finish()
            return false
        }
        val openGlVersionString = (activity.getSystemService<Any>(Context.ACTIVITY_SERVICE) as ActivityManager)
                .deviceConfigurationInfo
                .glEsVersion
        if (openGlVersionString.toDouble() < MIN_OPENGL_VERSION) {
            Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG)
                    .show()
            activity.finish()
            return false
        }
        return true
    }
    companion object {
        private const val MIN_OPENGL_VERSION = 3.0
    }
}

checkIsSupportedDeviceOrFinish 用来检测可运行环境,通过实现可知, Sceneform 的运行条件是 AndroidN 以及 OpenGL 3.0 以上。

以上就是全部代码了,虽然代码很少,效果很哇塞

image.png

最后

Sceneform 配合 ARCore 可以快速搭建 AR 应用,除了加载静态的 3D 模型以外,Sceneform 还可以加载带动画的模型。

随着 “元宇宙” 概念的兴起,Google,Facebook 等巨头必将加大在 AR 乃至 VR 技术上的研究投入,虚拟现实技术或将成为移动互联网之后的新一代社交、娱乐场景,想象空间巨大。

今天就写到这里吧, 我要和刚认识的小姐姐玩耍去了 🙈

image.png

最后推荐一个网站,大家可以在那里下载一些有趣的 3D 模型 ~

sketchfab.com/

(完)

目录
相关文章
|
存储 IDE API
我在 Android 上做了一个 1 : 1 高达
借助 Sceneform 和 ARCore,仅仅数行代码可以在 Android 上实现一个 1:1 的高达, 效果惊艳!
386 0
|
1月前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
239 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
1月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
206 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
1月前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
524 4
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
2月前
|
开发工具 Android开发
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
489 11
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
|
1月前
|
移动开发 Android开发
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
122 0
|
2月前
|
Java 开发工具 Maven
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
199 6
|
4月前
|
安全 数据库 Android开发
在Android开发中实现两个Intent跳转及数据交换的方法
总结上述内容,在Android开发中,Intent不仅是活动跳转的桥梁,也是两个活动之间进行数据交换的媒介。运用Intent传递数据时需注意数据类型、传输大小限制以及安全性问题的处理,以确保应用的健壯性和安全性。
355 11
|
4月前
|
移动开发 Java 编译器
Kotlin与Jetpack Compose:Android开发生态的演进与架构思考
本文从资深Android工程师视角深入分析Kotlin与Jetpack Compose在Android系统中的技术定位。Kotlin通过空安全、协程等特性解决了Java在移动开发中的痛点,成为Android官方首选语言。Jetpack Compose则引入声明式UI范式,通过重组机制实现高效UI更新。两者结合不仅提升开发效率,更为跨平台战略和现代架构模式提供技术基础,代表了Android开发生态的根本性演进。
181 0
|
8月前
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
2011 77
下一篇
oss云网关配置