2019年3月21日木曜日

ARKitのサンプルに影を追加しよう



ARKitのサンプルに影を追加しよう


ここまでサンプルアプリをよりリアルにする方法として、ライトを追加する方法を紹介しましたが、何か物足りないと感じてはいなかったでしょうか?

現実世界では物体に光を当てると影ができますが、サンプルアプリの飛行機には影がありませんでした。

影を付けることでさらにリアルさを演出することができますので、今回はその方法をご紹介します。


影を付ける方法


影を付ける方法は次の二つがあります。

影の画像ファイルを用意して物体の下に表示する





物体にCast Shadowを有効にした光を物体に当てる




前者は方法としてはシンプルですが、それぞれの物体に合った影の画像を用意するのは面倒です。

さらに、対象の物体が移動した場合にはそれに合ったファイルを全て用意するのは現実的ではありません。

今回は後者の方法を紹介します。


ship.scnを修正する


1. ShipMeshの位置を修正する


この後にライトと影を写す平面を追加するのですが、shipの子ノードとして追加します。

全ての子ノードがshipと同じ位置にあった方が修正がやり易いので、飛行機本体のshipMeshを親ノードのshipと同じ位置まで移動させます。



ship.scnを開き、左のペインからshipMeshを選択し、Transformsのz positionを0に変更してください。

※このまま進めると、飛行機を回転させた時に翼が地面にめり込むのでy座標も0.2に設定してください


2. 影を表示するための平面を追加する


影を表示するための平面を追加します。

影は飛行機本体の下に表示するため、平面は必ずshipMeshと比べて低い位置に配置します。


右上のオブジェクトボタンを押し、Planeを選択し、飛行機の下あたりにドラッグ&ドロップします。



次に、planeを選択し、角度と位置を調整します。

追加した状態では平面は縦向きです。横にするため、Euerのxを-90にします。

位置はPositionのx, y, zをそれぞれ、0, -0.3, 0と入力します。


次に平面の大きさを調整します。

デフォルト状態では1m × 1mなので、2m × 2mにします。

Sizeのxとyに2を入力します。



最後に、平面に影を写すため、Write To Colorの全てのチェックを外しておきます。


3. Directional Lightを追加する


ライトを追加する方法でも紹介しましたが、ライトのタイプはいくつか存在します。

影を付けるためには投光器のような一方向の光を上から当てる必要があります。

投光器のような光のタイプはDirectionalといい、それを追加します。


先ほどと同じようにオブジェクト選択ボタンからDirectional lightを選択し、編集画面上にドラッグ&ドロップします。



ライトを配置したら角度と位置を調整します。

角度はxを-90に設定し、位置は飛行機の上に来るように1を設定します。



次にshadowの設定を行います。

Casts shadowsにチェックを入れて、ModeをDeferredに設定します。

また、Castersは"Back face only"に設定をします。

この設置をしないと光が当たる上面がちらついてしまいます。



ここまで来れば影が表示されるようになりましたが、影の色が濃すぎるので透過の設定を行います。

先ほどのshadowのColorを選択しカラーパレットを表示させます。

黒を選択し、Opacityを50に変更します。


最後にライトと影をshipの子ノードに設定をしておきます。

このようにすることでshipをロードするとライトと影も一緒にロードされます。


アニメーションのコードをちょっと変更する


過去のアニメーションを追加時のコードは次のように書いていましたが、この状態だとライトと影も一緒に回転してしまいます。

if let ship = sceneView.scene.rootNode.childNode(withName: "ship", recursively: true) {
    ship.position = targetNode.position
                    
    let configuration = ARWorldTrackingConfiguration()
    sceneView.session.pause()
    sceneView.session.run(configuration)
                    
    let completion = {
         let rotationAction = SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 0, z: 1, duration: 1))
         ship.runAction(rotationAction)
    }
                    
    SCNTransaction.begin()
    SCNTransaction.animationDuration = 1.5
    SCNTransaction.completionBlock = completion
    ship.opacity = 1
    SCNTransaction.commit()
                    
    targetNode.isHidden = true
}

そのため、ライトと影の位置は変えずに飛行機だけを回転させるために次のように変更します。

if let ship = sceneView.scene.rootNode.childNode(withName: "ship", recursively: true) {
    ship.position = targetNode.position
                    
    let configuration = ARWorldTrackingConfiguration()
    sceneView.session.pause()
    sceneView.session.run(configuration)
                    
    let completion = {
        let rotationAction = SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 0, z: 1, duration: 1))
        if let shipMesh = ship.childNode(withName: "shipMesh", recursively: false) {
            shipMesh.runAction(rotationAction)
        }
    }
                    
    SCNTransaction.begin()
    SCNTransaction.animationDuration = 1.5
    SCNTransaction.completionBlock = completion
    ship.opacity = 1
    SCNTransaction.commit()
                    
    targetNode.isHidden = true
}


実際の動作




0 件のコメント:

コメントを投稿