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即可。
目前在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
有两个子类:ViewRenderable
和ModelRenderable
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文件夹,然后将素材资源文件拷贝到其下,比如。
这里我使用的是官方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
。
Scene
和Node
都是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卡片的效果如下:
基本形状和材料的效果如下:
3D素材的效果如下:
总结
这样我们完成了一个简单的demo,也熟悉了Scenefrom的基本功能和开发流程。下一篇文章我们继续学习Scenefrom的其他功能,看看有哪些更炫的操作。