构建场景并与之互动

本页包含有关构建 Scene 并与之互动的常见提示

在不使用 AR 的情况下渲染场景

您可以利用 SceneView 类渲染 3D 场景,无需使用设备的摄像头或 AR 会话。 这在没有 AR 功能的应用中预览 3D 对象,或在不支持 AR 的设备上提供替代功能时十分有用。

默认情况下,SceneView 不会显示 AR 摄像头中的图像,将使用黑色背景。 要更背景色,您可以调用 view.setBackgroundColor() 或按如下所示定义布局中的背景色:

<com.google.ar.sceneform.SceneView
    android:id="@+id/scene_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/deep_teal"/>

场景的 Camera 节点位于原点(位置 0,0,0),并朝前(方向 0,0,-1)。 因为摄像头的位置和旋转与 AR 运动跟踪没有关联,您可以重新调整它的位置,或者像呈现任何其他节点一样以动画形式呈现它。

Camera camera = sceneView.getScene().getCamera();
camera.setLocalRotation(Quaternion.axisAngle(Vector3.right(), -30.0f));

互动

处理用户触摸

当用户触摸屏幕时,Sceneform 会将触摸事件传递至连接到节点与场景的事件处理程序和侦听器。 此行为类似于触摸事件传递至 Android 中视图和视图组的方式。 下面是传递顺序:

  1. 事件发送至 scene.setOnPeekTouchListener()(如果设置)。

    这类似于 viewGroup.intercept(),不过,场景的快速触摸侦听器无法消费事件。

  2. 事件传递至与射线相交的第一个节点。

    • 通过定义一个能够返回 trueonTouchEvent() 函数集,节点可以消费事件。
    • 如果 onTouchEvent() 函数返回 false,或者未定义侦听器,事件将传递至节点的父级。 在事件被消费或者到达场景之前,这个过程将持续进行。
  3. 最后,如果没有任何侦听器消费事件,事件将传递至 scene.onTouchListener()

检测手势

ArFragment 已添加对点按(选择)、拖动(移动)、双指张合(缩放)和倾斜(旋转)手势的支持。

如需了解示例,请参见 HelloSceneform 示例应用中的 HelloSceneformActivity.java

创建自定义节点

与创建自定义 Android 视图类似,您可以通过将 Node 子类化的方式创建自定义节点。 下面是一些您可能需要创建自定义节点的情况:

  • 您希望在节点生命周期中访问事件,例如 onUpdate()onActivateonDeactivate()
  • 您希望创建一个由一组节点组成的节点。
  • 您正在重复许多代码,并且可以将节点分解成子类。

如需了解示例,请参见 solarsystem 示例应用中的 Planet.java

以动画形式呈现节点

可以通过两种方式以动画形式呈现节点:

通过 ObjectAnimator 以动画形式呈现

下面是一个以动画形式呈现聚光灯强度的示例:

final int durationInMilliseconds = 1000;
final float minimumIntensity = 1000.0f;
final float maximumIntensity = 3000.0f;
ValueAnimator intensityAnimator =
    ObjectAnimator.ofFloat(
        spotlightNode.getLight(), "intensity", minimumIntensity, maximumIntensity);
intensityAnimator.setDuration(durationInMilliseconds);
intensityAnimator.setRepeatCount(ValueAnimator.INFINITE);
intensityAnimator.setRepeatMode(ValueAnimator.REVERSE);
intensityAnimator.start();

如需了解更多信息,请参阅通过 ObjectAnimator 以动画形式呈现

在 onUpdate 中以动画形式呈现

替换节点的 onUpdate(),在帧与帧之间将其以动画形式呈现。 以下示例(来自 solarsystem 示例应用中的 Planet.java)可以在每一帧中调整信息卡以朝向用户,即使行星旋转。

@Override
public void onUpdate(FrameTime frameTime) {
  Vector3 cameraPosition = getScene().getCamera().getWorldPosition();
  Vector3 cardPosition = infoCard.getWorldPosition();
  Vector3 direction = Vector3.subtract(cameraPosition, cardPosition);
  Quaternion lookRotation = Quaternion.lookRotation(direction, Vector3.up());
  infoCard.setWorldRotation(lookRotation);
}

添加灯

可以将 Lights 连接至场景中的任何节点。 默认情况下,每一个 Sceneform 场景都包含一个 Sun 节点,该节点具有一个方位灯。

您可以修改太阳,或者向场景添加自己的灯。 以下示例可以添加一个聚光灯:

Light spotLightYellow =
    Light.builder(this, Light.Type.FOCUSED_SPOTLIGHT)
        .setColor(new Color(android.graphics.Color.YELLOW))
        .setShadowCastingEnabled(true)
        .build();

然后调用 setLight(),将聚光灯连接到节点。

自定义平面可视化

默认情况下,场景有一个 PlaneRenderer,它可以在 ARCore 检测到 Planes 时将其突出显示。 如下所示:

您可以修改用于渲染检测到的平面的默认材料和纹理。 下面这段代码可以更改纹理:

Texture.Sampler sampler =
    Texture.Sampler.builder()
        .setMinMagFilter(Texture.Sampler.MagFilter.LINEAR)
        .setWrapMode(Texture.Sampler.WrapMode.REPEAT)
        .build();

Texture.builder()
    .setSource(this, R.drawable.custom_texture)
    .setSampler(sampler)
    .build()
    .thenAccept(texture -> {
         arSceneView
             .getPlanerRenderer()
             .getMaterial()
             .setTexture(PlaneRenderer.MATERIAL_TEXTURE, customTexture);
});

阴影

阴影可以让可渲染对象看起来贴近现实世界,并为用户提供一种层次和空间感。

Sceneform 中存在可以投射阴影的物体和可以接收阴影的物体。

如果可渲染对象可以同时投射和接收阴影,则它可以在自己身上投射阴影。