本页包含关于构建 Scene
并与之互动的常见提示。
不使用 AR 渲染场景
借助 SceneView
类,您无需使用设备的相机或 AR 会话就能渲染 3D 场景。对于没有 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 中传播到视图和视图组。传播顺序如下:
系统会将事件发送到在
scene.addOnPeekTouchListener()
中添加的任何监听器。这与
viewGroup.intercept()
类似,不同之处在于滑点触控监听器上的场景不能使用事件。该事件会传递到光线与第一个相交节点。
- 节点可以通过定义返回
true
的onTouchEvent()
方法集来使用事件。 - 如果
onTouchEvent()
方法返回false
,或未定义监听器,则事件将传播到节点的父级。此过程会一直持续到事件被消耗或到达场景后。
- 节点可以通过定义返回
最后,如果没有任何监听器使用了该事件,则系统会将该事件传递给
scene.onTouchListener()
。
检测手势
ArFragment
已内置对点按(选择)、拖动(移动)、双指张合(缩放)和扭转(旋转)手势的支持。
有关示例,请参阅 HelloSceneform 示例应用中的 HelloSceneformActivity.java
。
创建自定义节点
与创建自定义 Android 视图类似,您可以通过为 Node
创建子类来创建自定义节点。在以下情况下,您可能需要创建自定义节点:
- 您希望访问节点生命周期中的事件,例如
onUpdate()
、onActivate
和onDeactivate()
。 - 您想要创建一个由一组节点组成的节点。
- 您复制了许多代码,可以将其子类化。
有关示例,请参阅太阳能示例应用中的 Planet.java
。
为节点添加动画效果
为节点添加动画效果的方法有两种:
- 使用标准 Android Animation API 中的
ObjectAnimator
。 - 创建自定义节点类并替换
onUpdate()
使用 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()
,使其在帧间添加动画效果。以下示例来自太阳系统示例应用中的 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
节点,该节点附有方向灯。
您可以修改阳光或将自己的场景添加到场景中。以下示例添加了一个 Spotlight:
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()
.setMinFilter(Texture.Sampler.MinFilter.LINEAR)
.setWrapMode(Texture.Sampler.WrapMode.REPEAT)
.build();
// R.drawable.custom_texture is a .png file in src/main/res/drawable
Texture.builder()
.setSource(this, R.drawable.custom_texture)
.setSampler(sampler)
.build()
.thenAccept(texture -> {
arSceneView.getPlaneRenderer()
.getMaterial().thenAccept(material ->
material.setTexture(PlaneRenderer.MATERIAL_TEXTURE, texture));
});
阴影
阴影可以让可渲染对象看起来贴近现实世界,并为用户提供深度和空间感。
Sceneform 中有可以投射阴影的对象,以及可以接收阴影的对象。
Lights
和Renderables
可以投射阴影默认情况下,太阳已启用阴影投射,但未启用光线投射。调用
setShadowCastingEnabled()
即可将其开启。Renderables
和PlaneRenderer
可以接收阴影。默认情况下,阴影接收已启用。调用
setShadowReceiver()
即可将其关闭。
如果可渲染对象同时投射和接收阴影,那么它可以对自身投射阴影。