介绍
利用 Sceneform,可以轻松地在 AR 和非 AR 应用中渲染逼真的 3D 场景,主要包含内容如下:
- 一个高级场景图 API
- 逼真的基于物理的渲染器,由 Filament 提供
- 一个 Android Studio 插件,用于导入、查看和构建 3D asset
谷歌官方文档接下来把 Sceneform 和 ARCore 在项目中的配置,又讲了一遍,pass。
开始使用 Sceneform 和创建场景图的最简单方法是使用 ArFragment
,在layout中添加这个组件后即可在逻辑代码中使用。
创建可渲染对象
Renderable
是一个 3D 模型,可置于场景中的任何位置,由网格、材料和纹理组成。
- 标准 Android
ViewRenderable
,在 3D 场景中被渲染为平面的 2D 卡片,同时可以通过触摸与它们交互。 - Sceneform 提供了多种工具和插件,用于将 3D asset 文件(OBJ、FBX、glTF)转换成 Sceneform 二进制 asset (SFB),此 asset 随后可以构建到
ModelRenderable
中。 - 基本形状和材料,可通过编程方式结合,以便在运行时创建更复杂的物体。
以上3种分别对应的是:1、普通2D效果(可导入图片和组件混合);2、导入文件实现的3D立体效果;3、代码实现的3D效果(如一个大圆球)。
以第一种为例,先在 res > layout 中创建一个布局文件,然后构建 ViewRenderable
对象,代码示例如下:
// 需要引入这个类
import com.google.ar.sceneform.rendering.ViewRenderable;
// 后续是拿 testViewRenderable 放到场景中去
private ViewRenderable testViewRenderable;
ViewRenderable.builder()
.setView(this, R.layout.test_view)
.build()
.thenAccept(renderable -> controlViewRenderable = renderable);
可渲染对象创建完后,就是要把它放入场景中。
构建场景并与之互动
在不使用 AR 的情况下渲染场景,可以利用 SceneView
类渲染 3D 场景,无需使用设备的摄像头或 AR 会话。这在没有 AR 功能的应用中预览 3D 对象,或在不支持 AR 的设备上提供替代功能时十分有用。
当用户触摸屏幕时,Sceneform 会将触摸事件传递至连接到节点与场景的事件处理程序和侦听器。不过可以直接利用ArFragment
,这个已经封装了多种触摸事件的处理,ArFragment
已添加对点按(选择)、拖动(移动)、双指张合(缩放)和倾斜(旋转)手势的支持。
创建自定义节点来构建画面,个人感觉这跟浏览器的DOM节点很像,前面创建的可渲染对象就是在这里使用的。节点更新可以使用动画来呈现效果,还可以设置一个灯光的位置,达到物体有阴影的效果。
// 场景更新时执行的方法
scene.addOnUpdateListener(
(FrameTime frameTime) -> {
if (faceRegionsRenderable == null || faceMeshTexture == null) {
return;
}
Collection faceList =
sceneView.getSession().getAllTrackables(AugmentedFace.class);
// Make new AugmentedFaceNodes for any new faces.
for (AugmentedFace face : faceList) {
if (!faceNodeMap.containsKey(face)) {
AugmentedFaceNode faceNode = new AugmentedFaceNode(face);
faceNode.setParent(scene);
faceNode.setFaceRegionsRenderable(faceRegionsRenderable);
faceNode.setFaceMeshTexture(faceMeshTexture);
// controlNode 是我增加的一个节点
Node controlNode = new Node();
// 放入可渲染对象
controlNode.setRenderable(controlViewRenderable);
// 缩放节点大小
controlNode.setLocalScale(new Vector3(0.25f, 0.25f, 0.25f));
// 设置节点位置
controlNode.setLocalPosition(new Vector3(0.0f, 0.0f, 0.0f));
// 挂在faceNode下
faceNode.addChild(controlNode);
faceNodeMap.put(face, faceNode);
}
}
// Remove any AugmentedFaceNodes associated with an AugmentedFace that stopped tracking.
Iterator> iter =
faceNodeMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
AugmentedFace face = entry.getKey();
if (face.getTrackingState() == TrackingState.STOPPED) {
AugmentedFaceNode faceNode = entry.getValue();
faceNode.setParent(null);
iter.remove();
}
}
});
3D空间如何定位Node
上面讲到把可渲染对象放到 Node 里,然后把 Node 挂载在场景中就可以呈现视图效果。那在现实场景中如何定位一个 Node 呢?分析目前的示例代码,发现做法基本都是在 fragment 更新的时候做的处理:1、监听当前 fragment 在检测平面的时候;2、监听当前 fragment 更新。
// 1
arFragment.setOnTapArPlaneListener(this::onPlaneTap);
private void onPlaneTap(HitResult hitResult, Plane unusedPlane, MotionEvent unusedMotionEvent) {
if (andyRenderable == null || hatRenderable == null) {
return;
}
// Create the Anchor.
Anchor anchor = hitResult.createAnchor();
// ....
}
// 2
arFragment.getArSceneView().getScene().addOnUpdateListener(this::onFrameUpdate);
private void onFrameUpdate(FrameTime unusedframeTime) {
Frame frame = arFragment.getArSceneView().getArFrame();
if (frame == null) {
return;
}
if (frame.getCamera().getTrackingState() != TrackingState.TRACKING) {
return;
}
float[] position = { 0, 0, -1f };
float[] rotation = { 0, 0, 0, 0.1f };
Session session = arFragment.getArSceneView().getSession();
Anchor myAnchor = session.createAnchor(new Pose(position, rotation));
anchorNode = new AnchorNode(myAnchor);
anchorNode.setParent(arFragment.getArSceneView().getScene());
// ...
}
sceneform api 大全 这个链接已经两周了,还是404