用Kotlin在Android中创建一个简单的增强现实应用

493 阅读5分钟

在本教程中,我们将学习如何用Kotlin创建一个增强现实的应用程序,以跟上快速发展的科技世界。这个应用程序将允许你使用手机的摄像头为图片添加属性。

前提条件

读者应该具备以下条件才能跟上。

  • 在你的机器上安装最新版本的Android Studio
  • 你应该有Kotlin编程语言的知识。
  • 拥有一个由Google Play服务支持的AR的物理设备。

注意:不是所有的安卓设备都支持ARcore。从这里查看支持的设备列表。另外,值得注意的是,你可以在安卓模拟器上运行该应用程序。要了解如何做到这一点,请点击本指南

目标

在本教程结束时,读者将了解到以下内容。

  • 什么是增强现实(Augmented Reality)。
  • 如何为增强现实设置一个安卓工作室。
  • 如何在AR场景中放置3D物体。

什么是增强现实(AR)?

这是一个现实世界的技术增强版本。它是通过使用合成图形元素、音乐或其他感官刺激来创建的。

它是一项新技术,将数字元素与现实世界的物体融合在一起。手机用户可以使用他们的智能手机与他们的环境互动。有了AR,我们可以按照自己的意愿弯曲现实,这就像现实的延伸,或者可以说,重新评估未来。

创建一个安卓项目

启动Android Studio并创建一个新项目。

create-project

注意:最小的SDK版本应该是API 24:Android 7(Nougat)。

项目准备好后,我们需要添加Sceneform 插件。这个插件对于支持增强现实技术是必要的。

在Android Studio的菜单上,点击File ,然后点击Settings ,一个新的窗口将打开。在右边的标签上,点击Plugins ,并在市场上搜索Sceneform 。点击安装并应用。重新启动Android Studio以使变化生效。

安装Sceneform插件

install-plugin

在IDE重启后,你可能会注意到弹出的一个错误。这个错误的内容如下。

"插件错误。插件'Google Sceneform Tools(Beta)'只与IntelliJ IDEA兼容,因为它没有定义任何明确的模块依赖关系"。

你可以通过使用Sceneform SDK v1.16.0来解决这个错误。另外,你可以手动设置一切。

手动设置

  1. 这里下载Sceneform文件。将这些文件解压到你的应用程序的文件夹中,然后进入下一个步骤。

  2. 进入Gradle,打开gradle.settings ,添加以下几行。

include ':sceneform'
project(':sceneform').projectDir = new File('sceneformsrc/sceneform')
include ':sceneformux'
project(':sceneformux').projectDir = new File('sceneformux/ux')
  1. 打开build.gradle(Module:app) ,在依赖项中添加以下一行。
api project(":sceneformux")
  1. 用新的Gradle文件同步项目,并等待构建完成。

在Android Studio中设置AR 3D模型

在我们的项目中,我们将使用Sceneform SDK 1.15.0,它非常流行。有两种方法可以获得3D模型。

  1. 你可以在网上获得3D模型,并下载glb 文件。最初使用的是谷歌的Poly,但后来被废止了。你可以在网上找到其他的替代品,其中一些你可能要为模型付费。Sketchfab是一个很好的例子,但你必须购买它。

  2. 自己设计和建造模型。你可以使用Blender这样的软件来制作3D模型,你可以在你的应用程序中使用。

在本教程中,我们将使用一个现成的模型,可以从这里下载。

下载完模型后,进入Android Studio,在res 文件夹上,右键单击并创建一个新的Android Resource Directory 。将资源类型改为raw ,然后点击确定。在这个目录中,粘贴你刚刚下载的model.glb 文件。

启用权限

打开Manifest ,添加以下权限。

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

另外,在<application> 体中,添加以下元数据。

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

建立应用程序的用户界面

该应用程序将只需要一个屏幕,这将是相机屏幕。打开activity_main.xml ,并添加以下代码。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/sceneform_ar_scene_view"
        android:name="com.google.ar.sceneform.ux.ArFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

android:name="com.google.ar.sceneform.ux.ArFragment" 行可能会出现一个错误。这是因为没有找到ArFragment 类。

为了解决这个问题,打开build.gradle 应用程序级别,添加以下依赖关系并同步项目。

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

实现应用程序的主要逻辑

打开ActivityMain.kt 文件,首先创建一个函数,检查一个设备是否支持ARcore。这个函数应该是这样的。

private const val MIN_OPENGL_VERSION = 3.0

private fun isDeviceArSupported(context : Context) : Boolean {
    when {
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> {
            val openGlVersionString = (context.getSystemService(AppCompatActivity.ACTIVITY_SERVICE) as ActivityManager)
                .deviceConfigurationInfo
                .glEsVersion
            if (openGlVersionString.toDouble() < MIN_OPENGL_VERSION) {

                Toast.makeText(this, "Minimum Open GL version should be 3 or later", Toast.LENGTH_LONG)
                    .show()
                this.finish()
                return false
            }
            return true
        }
        else -> {
            Toast.makeText(this, "Android version should be 7 or later versions",
                Toast.LENGTH_LONG
            )
                .show()
            this.finish()
            return false
        }
    }
}

使用手机的Open GL版本,上述函数检查一个设备是否支持AR。对于一个完全支持AR的设备来说,它必须是安卓7或更高版本,最小的Open GL版本应该是3。

下一步是将我们的模型添加到一个场景中。这将通过创建一个将节点放入AR场景的函数来完成。该函数如下。

private fun addModelToScene(arFragment: ArFragment, anchor: Anchor, renderable: Renderable) {
    val transformableNode = TransformableNode(arFragment.transformationSystem)
    transformableNode.renderable = renderable

    val anchorNode = AnchorNode(anchor)
    transformableNode.setParent(anchorNode)
    arFragment.arSceneView.scene.addChild(anchorNode)
    transformableNode.select()
}

最后一件事是在AR场景中放置物体。这可以通过以下方式完成。

@RequiresApi(Build.VERSION_CODES.N)
private fun placeObjectOnScene(fragment: ArFragment, anchor: Anchor, uri: Uri) {
    ModelRenderable.builder()
        .setSource(fragment.context, uri)
        .build()
        .thenAccept(Consumer { renderable: ModelRenderable? ->
            addModelToScene(
                fragment, anchor, renderable!!
            )
        })
        .exceptionally { throwable: Throwable ->
            Toast.makeText(
                fragment.context, "Error:" + throwable.message,
                Toast.LENGTH_LONG
            ).show()
            null
        }
}

注意:记得用@RequiresApi(Build.VERSION_CODES.N) 注释,以确保该函数只在Android 7或更高版本上调用。

完整的MainActivity.kt 代码如下。

private const val MIN_OPENGL_VERSION = 3.0

class MainActivity : AppCompatActivity() {

    private lateinit var arFragment: ArFragment
    private lateinit var binding: ActivityMainBinding

    @RequiresApi(VERSION_CODES.N)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (isDeviceArSupported(this)) {
            binding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(binding.root)

            arFragment =
                (supportFragmentManager.findFragmentById(R.id.sceneform_ar_scene_view) as ArFragment?)!!
            this.arFragment!!.setOnTapArPlaneListener { hitResult: HitResult, plane: Plane?, motionEvent: MotionEvent? ->
                val anchor = hitResult.createAnchor()
                placeObjectOnScene(arFragment!!, anchor, Uri.parse("model.glb"))
            }
        }
    }

    private fun isDeviceArSupported(context: Context): Boolean {
        when {
            Build.VERSION.SDK_INT >= VERSION_CODES.N -> {
                val openGlVersionString =
                    (context.getSystemService(ACTIVITY_SERVICE) as ActivityManager)
                        .deviceConfigurationInfo
                        .glEsVersion
                if (openGlVersionString.toDouble() < MIN_OPENGL_VERSION) {

                    Toast.makeText(
                        this, "Minimum Open GL version should be 3 or later",
                        Toast.LENGTH_LONG
                    ).show()
                    this.finish()
                    return false
                }
                return true
            }
            else -> {
                Toast.makeText(
                    this, "Android version should be 7 or later versions",
                    Toast.LENGTH_LONG
                )
                    .show()
                this.finish()
                return false
            }
        }
    }

    private fun addModelToScene(arFragment: ArFragment, anchor: Anchor, renderable: Renderable) {
        val transformableNode = TransformableNode(arFragment.transformationSystem)
        transformableNode.renderable = renderable

        val anchorNode = AnchorNode(anchor)
        transformableNode.setParent(anchorNode)
        arFragment.arSceneView.scene.addChild(anchorNode)
        transformableNode.select()
    }

    @RequiresApi(VERSION_CODES.N)
    private fun placeObjectOnScene(fragment: ArFragment, anchor: Anchor, uri: Uri) {
        ModelRenderable.builder()
            .setSource(fragment.context, uri)
            .build()
            .thenAccept(Consumer { renderable: ModelRenderable? ->
                addModelToScene(
                    fragment, anchor, renderable!!
                )
            })
            .exceptionally { throwable: Throwable ->
                Toast.makeText(
                    fragment.context, "Error:" + throwable.message,
                    Toast.LENGTH_LONG
                )
                    .show()
                null
            }
    }
}

运行该应用程序

要运行该应用程序,首先要确保你的设备上有一个活跃的互联网连接。运行该应用程序,并将焦点放在一个表面上。谷歌AR将开始检测一个表面,在它检测到之后,点击屏幕,将我们的物体放在那里。然后,你可以在不同的表面上尝试这样做,因为你在探索。

总结

在本教程中,我们已经学会了如何创建一个增强现实的应用程序,并在场景中放置物体。AR的作用不止于此。通过阅读更多关于这个主题的文章,继续探索更多关于增强现实的内容。

编码愉快 :)