このページでは、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));
ユーザーの操作
ユーザー タップの処理
ユーザーが画面にタッチすると、シーンとノードおよびシーンにアタッチされたイベント ハンドラとリスナーにタッチイベントが伝播されます。この動作は、タップイベントが Android のビューやビューグループに伝播する仕組みに似ています。伝播の順番は次のとおりです。
イベントは、
scene.addOnPeekTouchListener()
に追加されたリスナーに送信されます。これは、ピークタップ リスナーのシーンがイベントを利用できない点を除いて、
viewGroup.intercept()
と似ています。イベントは、レイが交差する最初のノードに渡されます。
- ノードは、
true
を返すonTouchEvent()
メソッドセットを定義することで、イベントを使用できます。 onTouchEvent()
メソッドがfalse
を返す場合、またはリスナーが定義されていない場合、イベントはノードの親に伝播されます。このプロセスは、イベントが使用されるかシーンに達するまで続きます。
- ノードは、
最後に、どのリスナーもイベントを使用していない場合は、イベントは
scene.onTouchListener()
に渡されます。
操作の検出
ArFragment
には、タップ(選択)、ドラッグ(移動)、ピンチ(拡大)、回転(回転)のサポートが組み込まれています。
例については、HelloSceneform サンプルアプリの HelloSceneformActivity.java
をご覧ください。
カスタムノードを作成する
カスタム Android ビューの作成と同様に、Node
をサブクラス化することでカスタムノードを作成できます。次のような場合はカスタムノードを作成できます。
- ノードのライフサイクルで
onUpdate()
、onActivate
、onDeactivate()
などのイベントにアクセスする場合。 - ノードのグループで構成されるノードを作成する。
- 多くのコードを複製するため、サブクラスに分解できます。
例については、ソーラーシステム サンプルアプリの Planet.java
をご覧ください。
ノードのアニメーション化
ノードをアニメーション化するには、次の 2 つの方法があります。
- 標準の 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()
をオーバーライドして、フレームごとにアニメーション化します。次の例では、Solar System サンプルアプリの 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
は、シーン内の任意のノードにアタッチできます。デフォルトでは、すべてのシーンシーンに Sun
ノードが含まれており、このノードにディレクショナル ライトが接続されています。
太陽光を変更したり、シーンに独自の照明を追加したりできます。次の例では、スポットライトが追加されます。
Light spotLightYellow =
Light.builder(this, Light.Type.FOCUSED_SPOTLIGHT)
.setColor(new Color(android.graphics.Color.YELLOW))
.setShadowCastingEnabled(true)
.build();
次に、setLight()
を呼び出して、ノードに接続します。
プレーン ビジュアリゼーションのカスタマイズ
デフォルトでは、シーンに ARCore で検出された PlaneRenderer
がハイライト表示されます。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));
});
シャドウ
シャドウを使用すると、レンダリング可能なものが現実世界に埋もれて見えるようになり、ユーザーが奥行きや空間を感じることができます。
シーンには、シャドウをキャストできるオブジェクトと、シャドウを受信できるオブジェクトがあります。
Lights
とRenderables
はシャドウをキャストできます。影のキャストは、デフォルトでは太陽では有効になっていますが、照明では有効になっていません。
setShadowCastingEnabled()
を呼び出してオンにします。Renderables
とPlaneRenderer
はシャドウを受信できます。デフォルトでは、シャドウの受信が有効になっています。オフにするには、
setShadowReceiver()
を呼び出します。
レンダリング可能なものがシャドウをキャストして受け取る場合、シャドウ自体もキャストできます。