Sceneform 概览笔记

介绍

利用 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

你可能感兴趣的:(Sceneform 概览笔记)