Google AR系列之Scenefrom入门

185 阅读6分钟

Scenefrom

Sceneform是Google提供的用于开发AR应用的一个SDK,通过它可以轻松地在 AR 应用和非 AR 应用中渲染逼真的 3D 场景,而无需学习 OpenGL。

不过他的命运与VR sdk一样,github( github.com/google-ar/s… )上最后的提交记录是两年前了。

在学习Sceneform前建议你先简单了解一下ARCore,了解一下相关的基础知识,具体可以见我的上一篇文章。

安装插件

首先我们需要安装Sceneform插件,在 Android Studio 中,打开 Plugins并搜索 Google Sceneform Tools 安装重启Idea即可。

image.png

目前在Android Studio上已经无法安装这个插件了。安装重启后会发现根本没有安装成功,这也说明Google已经放弃Sceneform了。

引入Sceneform

首先Sceneform要求最低api为24,所以

    defaultConfig {
        // Sceneform requires minSdkVersion >= 24.
        minSdkVersion 24
        …
    }

然后依赖Sceneform sdk,如下:

dependencies {
    …
    // Provides ARCore Session and related resources.
    implementation 'com.google.ar:core:1.15.0'

    // Provides ArFragment, and other UX resources.
    implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.15.0'

    // Alternatively, use ArSceneView without the UX dependency.
    implementation 'com.google.ar.sceneform:core:1.15.0'
}

配置AR可选或AR必备

AR可选和AR必备我在上一篇文章中详细介绍了,这里就不再啰嗦了,大家根据自己的情况来配置即可。配置如下:


<uses-permission android:name="android.permission.CAMERA" />

<!-- Sceneform requires OpenGL ES 3.0 or later. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />


<!-- 如果是AR必选则需要
<uses-feature android:name="android.hardware.camera.ar" />-->

<application>
    …
    <!-- 如果是AR必选则是required -->
    <meta-data android:name="com.google.ar.core" android:value="optional" />
</application>

这里因为Sceneform需要OpenGL ES 3.0或以上,所以相对于ARCore增加了一条。

执行检查

在上一篇文章中我们介绍过,如果要使用ARCore最好在运行的时候执行检查,一是检查设备是否支持ARCore,二是检查是否安装了兼容版本的 Google Play Services for AR,当然也需要检查是否有相机权限。

不过Scenefrom提供了ArFragment,我们就不需要手动执行检查了,因为它会在执行必要的 ARCore 运行时检查后自动处理 ARCore 会话管理。

如果ArFragment并不满足我们的需求,我们可以自定义它的子类;或者使用ArSceneView,但是这样我们就需要手动执行检查并调用 setupSession() 来手动创建会话。

ArFragment在检查后会自动创建会话,这时候可以通过getArSceneView()来获取ArSceneView;可以通过getSession()来获取ARCore的会话session。

我们在项目中直接添加一个ArFragment,打开后如果授予了相机权限并且服务正常的话,就会看到相机的预览了,而且会自动进行环境理解标记出平面。

可渲染对象

Renderable是一种 3D 模型,可放置在场景中的任何位置,由网格、材料和纹理组成。

Renderable有两个子类:ViewRenderableModelRenderable

ViewRenderable

ViewRenderable可以创建2D平面卡片,比如显示文字等,如下:

val textView = TextView(activity)
textView.text = "在这里"
textView.setBackgroundColor(Color.WHITE)
ViewRenderable.builder().setView(activity, textView).build().thenAccept { renderable ->
    testViewRenderable = renderable
}

也可以创建一个xml布局文件,通过setView(Context context, int resourceId)即可,这样我们很方便将复杂的布局绘制到场景中。

ModelRenderable

ModelRenderable可以创建3D对象,有两种方式:一是通过3D 素材资源文件(OBJ、CocoaPods、glTF);二是通过基本形状和材料通过编程组合。

ShapeFactory和MaterialFactory

这里说一下第二种方式,通过 ShapeFactory 和 MaterialFactory 可以创建立方体、球体和圆柱体等简单形状,比如下面代码就是一个红色球体:

MaterialFactory.makeOpaqueWithColor(activity, com.google.ar.sceneform.rendering.Color(Color.RED))
    .thenAccept { material: Material? ->
        testModelRenderable =
            ShapeFactory.makeSphere(0.1f, Vector3(0.0f, 0.15f, 0.0f), material)
    }

OBJ

再说一下第一种方式,本来可以通过Google Sceneform Tools这个插件来处理,但是因为这个插件已经不能使用了,所以我们只能手动来处理。

首先在app目录下创建sampledata文件夹,然后将素材资源文件拷贝到其下,比如。

6FB76AA6-CD7C-4244-AB99-03F75B405D95.png

这里我使用的是官方demo中hellosceneform项目中的素材: github.com/google-ar/s…

在项目根目录的build.gradle中添加插件com.google.ar.sceneform:plugin

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:7.0.3"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"

        classpath 'com.google.ar.sceneform:plugin:1.15.0'
    }
}

在app目录的build.gradle中使用插件,并导入资源:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'com.google.ar.sceneform.plugin'
}
...
sceneform.asset('sampledata/models/andy.obj', // 'Source Asset Path' specified during import.
        'default',                            // 'Material Path' specified during import.
        'sampledata/models/andy.sfa',         // '.sfa Output Path' specified during import.
        'src/main/res/raw/andy')              // '.sfb Output Path' specified during import.

这样当build一次后插件会自动在raw目录下创建一个andy.sfb文件,在代码中通过这个文件创建可渲染对象即可,如下:

ModelRenderable.builder().setSource(activity, R.raw.andy).build().thenAccept{
        renderable ->
    testModelRenderable = renderable
}

构建场景

上面我们创建了可渲染对象,但是如何将对象放入场景中?

ARSceneView中默认已经存在了场景Scene。场景是一种树状数据结构,其中包含要呈现的虚拟对象的 Node

SceneNode都是NodeParent的子类,Scene可以看作是整个场景的根节点,场景中的所有物体都是一个个Node,通过树状的结构组合到一起。

以上面已经创建的testViewRenderable为例,我们创建一个节点:

val node = Node()
node.setParent(parent)
node.renderable = testViewRenderable

这里parent就是父节点,我们将testViewRenderable对应的节点添加给它,这样就会形成父子关系。如果某个节点是另一个节点的子级,则该节点会随着其父级而移动、旋转和缩放。一个节点可以有多个子级,但只能有一个父级,从而形成一种树状结构。这种结构称为场景图

锚点和命中测试

上面我们构建出了节点,但是它的parent到底应该是什么?如何放置到场景中?

这就需要锚点,通过锚点可以将物体固定到场景中,在Scenefrom中锚点也是Node,AnchorNode是Node的一个子类。

那么如果获得锚点?通过命中测试,以官方入门demo为例

在官方的demo中,当检测出平面后会显示出很多白点,这些是特征点,当点击特征点的时候就会放置一个3D对象。这就涉及到命中测试。

为ArFragment添加setOnTapArPlaneListener监听,这样当点击特征点的时候就会触发回调,如下:

setOnTapArPlaneListener { hitResult, plane, motionEvent ->
    ...
}

回调中的hitResult就是命中的结果,通过它我们可以创建锚点,如下

val anchor = hitResult.createAnchor()
val anchorNode = AnchorNode(anchor)
anchorNode.setParent(arSceneView.scene)

这样就会在对应的位置放置一个锚点,将锚点放置到场景中后,就可以给它添加节点,这样物体就放置到场景中了,如下:

val node = Node()
node.setParent(anchorNode)
//node.renderable = testViewRenderable
node.renderable = testModelRenderable

完整代码如下:

setOnTapArPlaneListener { hitResult, plane, motionEvent ->
    val anchor = hitResult.createAnchor()
    val anchorNode = AnchorNode(anchor)
    anchorNode.setParent(arSceneView.scene)

    val node = Node()
    node.setParent(anchorNode)
    //node.renderable = testViewRenderable
    node.renderable = testModelRenderable
}

这样当环境理解完成后,我们点击特征点就会放置一个对应的物体。

最终效果

2D卡片的效果如下:

WechatIMG519.jpeg

基本形状和材料的效果如下:

WechatIMG518.jpeg

3D素材的效果如下:

WechatIMG520.jpeg

总结

这样我们完成了一个简单的demo,也熟悉了Scenefrom的基本功能和开发流程。下一篇文章我们继续学习Scenefrom的其他功能,看看有哪些更炫的操作。