Apple Engine

Apple, iPhone, iOS, その周辺のことについて

iOS で SceneKit を試す(Swift 3) その53 - SceneKit の Point Of View

これまで紹介した SceneKit のカメラは、コードで設定するか、scn ファイル内のカメラで設定していた。

SceneKit の SCNView(SCNSceneRenderer)には pointOfView というプロパティがあり、 これに SCNCamera を設定しているノードを設定するとカメラに設定することができる。

わかりやすい例だと、SCNView で allowsCameraControl を true に設定した時。
画面の操作が行われると pointOfView がコントロール可能なカメラに変更される。

ちなみに iOS 11 では SCNCameraController で allowsCameraControl の振る舞いを変更できる。

 

利用用途

allowsCameraControl = true の際、ダブルタップで初期位置へスムースにアニメーションが行われる。

これと同様に複数のカメラ間での移動や回転のアニメーションを pointOfView を使用して行うことができる。

 

動作例

f:id:x67x6fx74x6f:20170807191038g:plain

 

コードを書いてみる

方法としては、異なる SCNCamera のノードを2つ作成し、pointOfView に設定したものをアニメーションさせる。

いつも通り iOS の Game テンプレートでプロジェクトを作成し、 GameViewController.swift のカメラの記述を削除。

シーン上にカメラを配置し、タッチイベントで pointOfView をアニメーションしている。

 

GameViewController.swift

import UIKit
import QuartzCore
import SceneKit

class GameViewController: UIViewController {
    
    let cameraNode1: SCNNode = SCNNode()
    let cameraNode2: SCNNode = SCNNode()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        
        // create and add a light to the scene
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light!.type = .omni
        lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
        scene.rootNode.addChildNode(lightNode)
        
        // create and add an ambient light to the scene
        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = .ambient
        ambientLightNode.light!.color = UIColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)
        
        // retrieve the ship node
        let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
        
        // animate the 3d object
        ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
        
        
        let scnView = self.view as! SCNView
        
        // set the scene to the view
        scnView.scene = scene
        
        // allows the user to manipulate the camera
        scnView.allowsCameraControl = true
        
        // show statistics such as fps and timing information
        scnView.showsStatistics = true
        
        // configure the view
        scnView.backgroundColor = UIColor.black
        
        // --- 追加分 ---
        
        cameraNode1.name = "camera1"
        cameraNode1.camera = SCNCamera()
        cameraNode1.position = SCNVector3(x: 0, y: 0, z: 15)
        
        scene.rootNode.addChildNode(cameraNode1)
        
        cameraNode2.name = "camera2"
        cameraNode2.camera = SCNCamera()
        cameraNode2.position = SCNVector3(x: 0, y: 20, z: 0)
        cameraNode2.eulerAngles = SCNVector3(x: Float.pi * -0.5, y: 0, z: 0)
        
        scene.rootNode.addChildNode(cameraNode2)
        
        scnView.pointOfView = cameraNode1
        
        // add a tap gesture recognizer
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        scnView.addGestureRecognizer(tapGesture)
    }
    
    func handleTap(_ gestureRecognize: UIGestureRecognizer) {
        // retrieve the SCNView

        let scnView = self.view as! SCNView
        
        let cameraNode:SCNNode!
        
        if scnView.pointOfView?.name == "camera1" {
            cameraNode = scnView.scene?.rootNode.childNode(withName: "camera2", recursively: true)
        } else {
            cameraNode = scnView.scene?.rootNode.childNode(withName: "camera1", recursively: true)
        }
        
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 4.0
        
        scnView.pointOfView = cameraNode

        SCNTransaction.commit()
    }
    
    override var shouldAutorotate: Bool {
        return true
    }
    
    override var prefersStatusBarHidden: Bool {
        return true
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            return .allButUpsideDown
        } else {
            return .all
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }

}

 

注意点

シーン上にカメラを配置していないとアニメーションは行われず、瞬時にカメラが変わる。

また、iOS シミュレーターではアニメーションが微妙な動きをする場合があるので注意。

 

今回はここまで

iOS で SceneKit を試す(Swift 3) その53 - SCNLight の Gobo と Category BitMask

シーン上でキャストシャドウを行うと基本的にはシャドウマップが作成される。

もし、ジオメトリの影を落とす際、細かなフォルムが必要なければ
事前にテクスチャ画像を作成したものを使用し、シャドウマップを生成させないことでライド使用時の負荷を減らすことができる。

Gobo に設定したライトは影を落とすためのもので光沢や色等のライティング効果は出ない。

 

Gobo って何?

ライブや舞台等で、ライトの前に光の当てたい部分を削ったプレートを置いて照明を照らすことで、
壁面に絵や文字を投影することができ、そのプレートや効果を指すとのこと。

 

ひとまず書いてみる

Scene Editor では設定できないためコードで書いていく。

いつも通り iOS の Game テンプレートでプロジェクトを作成。

Gobo 用の画像をプロジェクトに追加し、
Gobo 用のライトと落とした影を確認するために SCNFloor を設定する。

 

影となる画像 gabo1.png

f:id:x67x6fx74x6f:20170807180245p:plain

 

GameViewController.swift の「let scene = SCNScene(named: “art.scnassets/ship.scn”)!」より下に以下のコードを書く。

// --- 追加分 ---
let goboNode = SCNNode()
let light = SCNLight()
light.type = .directional
light.gobo?.contents = UIImage(named: "gobo1.png")
light.gobo?.intensity = 0.8
light.orthographicScale = 5
light.shadowMode = SCNShadowMode.modulated
light.castsShadow = false
goboNode.position = SCNVector3(x: 0, y: 5, z: 0)
goboNode.eulerAngles = SCNVector3(x: -Float.pi * 0.5, y: 0, z: 0)
goboNode.light = light
scene.rootNode.addChildNode(goboNode)

let floorNode = SCNNode(geometry: SCNFloor())
floorNode.position = SCNVector3(x: 0.0, y: -4.0, z: 0.0)
scene.rootNode.addChildNode(floorNode)

内容的には Y の値が 5 で下に向いた普通のライトを設定をして、 gobo の contents で画像設定、intensity で影の透過を決める。

そして、SCNLight の shadowMode を modulated にしている。

影の大きさを変更するため orthographicScale でサイズを変更している。

ちなみにドキュメントではスポットライトで使用できるとは書いてあるが、ディレクショナルライトでも動作する。

 

ビルドするとこのような感じになる。

f:id:x67x6fx74x6f:20170807180321p:plain

 

ジオメトリにも影が落ちているので落ちないようにしたい

Category BitMask を使って適応範囲を変更する。
以下のように合わせると、下の床のノード部分だけに影を落とすことができる

light.categoryBitMask = 2
floorNode.categoryBitMask = 2

f:id:x67x6fx74x6f:20170807180347p:plain

 

注意点

画像が適応できるものはモノクロが画像。
透過が含まれていると表示されない。

 

スポットライトと同等であるため、ライトの設置上限がある。
そのため、8個ライトが設定されていて Gobo のライトを追加すると
シーン内のいずれかのライトが消える。

 

Gobo はライトの向きで投影される画像が固定される。
そのため、方向の決まった影の画像を適応し、ジオメトリが回転すると問題が起きる。

画像

f:id:x67x6fx74x6f:20170807180416p:plain

 

動作

f:id:x67x6fx74x6f:20170807180524g:plain

 

今回修正した GameViewController.swift のコード

hitTest 部分が必要ないので省いている。

import UIKit
import QuartzCore
import SceneKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        
        // create and add a camera to the scene
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)
        
        // place the camera
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
        
        // create and add a light to the scene
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light!.type = .omni
        lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
        scene.rootNode.addChildNode(lightNode)
        
        // create and add an ambient light to the scene
        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = .ambient
        ambientLightNode.light!.color = UIColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)
        

        // --- 追加分 ---
        let goboNode = SCNNode()
        let light = SCNLight()
        light.type = .directional
        light.gobo?.contents = UIImage(named: "gobo1.png")
        light.gobo?.intensity = 0.8
        light.orthographicScale = 5
        light.shadowMode = .modulated
        light.castsShadow = false
        light.categoryBitMask = 2
        goboNode.position = SCNVector3(x: 0, y: 5, z: 0)
        goboNode.eulerAngles = SCNVector3(x: -Float.pi * 0.5, y: 0, z: 0)
        goboNode.light = light
        scene.rootNode.addChildNode(goboNode)
        
        let floorNode = SCNNode(geometry: SCNFloor())
        floorNode.position = SCNVector3(x: 0.0, y: -4.0, z: 0.0)
        floorNode.categoryBitMask = 2
        scene.rootNode.addChildNode(floorNode)
        
        
        // retrieve the ship node
        let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
        
        // animate the 3d object
        ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
        
        // retrieve the SCNView
        let scnView = self.view as! SCNView
        
        // set the scene to the view
        scnView.scene = scene
        
        // allows the user to manipulate the camera
        scnView.allowsCameraControl = true
        
        // show statistics such as fps and timing information
        scnView.showsStatistics = true
        
        // configure the view
        scnView.backgroundColor = UIColor.black
    }
    
    override var shouldAutorotate: Bool {
        return true
    }
    
    override var prefersStatusBarHidden: Bool {
        return true
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            return .allButUpsideDown
        } else {
            return .all
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }

}

 

今回はここまで

iOS で SceneKit を試す(Swift 3) その52 - Scene Editor の Light Probe

シーンで動かない物体にはライトマップでテクスチャに焼き付けると綺麗な見た目となるが、 キャラクターなど動くもがあった場合、光は反映されない。

Light Probe はシーンの色の変化とすべての方向から受け取る照明の強度を球体で設定し、
有効範囲にある Physically Based のライティングモデルの Emission の値を光の情報として使用する。

そのため適応させたいキャラクターなどのジオメトリが動作する周辺にいくつか設置する必要がある。

 

Light Probe の制限

  • 事前に計算させておく必要がある。
  • ライトとなるジオメトリとそれを適応させるジオメトリは Physically Based のライティングモデルである必要がある
  • 以前の記事で書いたが Light Probe は自体がライトになるわけではなく、ジオメトリの Emission の値がライトになる
  • Emission の光を反映させたいジオメトリは Movabililty を true にする必要がある
  • キャストシャドウは落とせないので他のライトと併用する。

 

シーン設定

Game (SceneKit) のテンプレートでプロジェクトを作成するか既存のファイルから ship.scn ファイルを選択。

中身はいらないので Scene Graph View か、Scene Editor 上にあるものを全て削除。

Object Library (Command + Control + Option + 3) から Plane と、Sphere、Light Probe を以下のように設定する。

 

Plane

Node Inspector (Command + Option + 3) で以下に変更。

f:id:x67x6fx74x6f:20170804182550p:plain

 

Material Inspector (Command + Option + 5) で Physically Based に変更し、 Emission の色を赤に変更する。

f:id:x67x6fx74x6f:20170804182722p:plain

 

両面描画のチェックを外す。

f:id:x67x6fx74x6f:20170804154134p:plain

 

Sphere

原点に設置。

Node Inspector (Command + Option + 3) で Movabililty の is movable チェックをオンにする。

f:id:x67x6fx74x6f:20170804182820p:plain

 

Light Probe

今回は下からしか照らされないので、球の真上に置く。

f:id:x67x6fx74x6f:20170804182522p:plain

 

Light Probe の Attrubutes Inspector

名前以外では Probe の Near Clipping と Far Clipping。 Light Probe の計算を行う Compute のボタンにのみ。

f:id:x67x6fx74x6f:20170804182107p:plain

 

Near Clipping と Far Clipping

適応範囲を Near Clipping と Far Clipping で設定する。

基本デフォルトのままでも良いと思われる。
(Apple のサンプルもデフォルト値を使用している)

 

Compute

ボタンを押すと Probe の計算が始まりジオメトリに結果が反映される。

今回のシーン設定でこのボタンを押すと以下のようになり、球の底の部分が赤く照らされているのがわかる。

f:id:x67x6fx74x6f:20170804182338p:plain

 

もし、何も問題がなく、ジオメトリに変化がない場合は Movabililty のチェックボタンをオン/オフすると反映される場合がある。

 

複数 Light Probe があって、いくつも Compute ボタンを押すのが面倒

Scene Inspector (Command + Option + 7) に、「Compute all probes」というボタンがあるのでこれを押す。 計算していない Probe 量が多いと終わるまで結構時間がかかるので注意。

 

状況によっては複数の Light Probe を設置する必要がある。

四隅に Light Probe を設置してみた。

f:id:x67x6fx74x6f:20170804182153p:plain

 

中央にも置いてみた。天井の照明が適応されている。

f:id:x67x6fx74x6f:20170804182207p:plain

 

今回はここまで

iOS で SceneKit を試す(Swift 3) その51 - Scene Editor の IES Light

IES Light はテキストデータのファイルを使用し照明のシミュレーションを行い陰や光を与える。
Directional Light とほぼ同じで、Scale のパラメーターがなく、IES ファイルの選択がある。

基本的にはライトからのシャドウマップで表現しているため、アーティファクトが出る(データのエラーやノイズの出現する)場合がある。

その場合は、Casts Shadow の チェックをオンにして Shadow のパラメーター群で調整する。

 

IES ファイル

テキスト情報で光がどのように与えるか記述されている。
試しに、以下のコードを入れて .ies の拡張子でプロジェクトに保存してみる。

TILT=NONE
1 13172.61 1
37 1
1
2
0 0 0
1 1 0
0.00 2.50 5.00 7.50 9.11 10.00 10.80 12.50 15.83 22.50 25.00 27.50 32.50 34.28 35.00 42.50 47.02 47.50 47.81 50.00 50.78 52.50
55.00 57.50 60.00 62.50 65.00 67.50 70.00 72.50 75.00 77.50 80.00 82.50 85.00 87.50 90.00
0.00
8379.00 8409.00 8528.00 8126.65 8500.00 8564.00 8301.00 8158.61 8411.98 8036.59 7865.61 7283.76 5209.38 6038.93 4533.49 3091.00
3665.38 2996.97 3625.45 2194.62 3170.85 1115.28 651.00 450.00 244.00 102.00 80.00 43.00 5.00 2.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00

 

今回コードを書いているが、基本的には自分で作成することはないと思われるので、 照明メーカーなどで配布されているものを使用した方が良いと思われる。

www.usa.lighting.philips.com

 

シーン設定

Game (SceneKit) のテンプレートでプロジェクトを作成するか既存のファイルから ship.scn ファイルを選択。
(設定が面倒である場合は、下にサンプルファイルをダウンロード)

中身はいらないので Scene Graph View か、Scene Editor 上にあるものを全て削除。

Object Library (Command + Control + Option + 3) から Plane を2つ、Sphere、IES Light を以下のように設定する。

Plane

2つの座標

f:id:x67x6fx74x6f:20170804154050p:plain

f:id:x67x6fx74x6f:20170804154059p:plain

 

Attributes Inspector (Command + Control + Option + 4) で両方とも大きさを変更。

f:id:x67x6fx74x6f:20170804154119p:plain

 

Material Inspector (Command + Control + Option + 4) で両面描画のチェックを外す。

f:id:x67x6fx74x6f:20170804154134p:plain

 

Sphere

座標値変更

f:id:x67x6fx74x6f:20170804153917p:plain

 

Attributes Inspector で大きさを変更。

f:id:x67x6fx74x6f:20170804153928p:plain

 

IES Light

f:id:x67x6fx74x6f:20170804153901p:plain

 

IES Light の Attrubutes Inspector

以前紹介した共通の設定に IES Profile の読み込みと、キャストシャドウを追加したもの。

キャストシャドウは Spot Light で紹介しているのでこちらを参照。

f:id:x67x6fx74x6f:20170804153538p:plain

 

IES Parameters

IES Profile のファイルを選択する。 Scene Editor ではフォルダのマークを選択して、上の方でつくったものか、ダウンロードした IES ファイルを選ぶ。

照明が適応されたのがわかる。

f:id:x67x6fx74x6f:20170804153708p:plain

 

試しに Casts shadows のチェックを入れると背面にアーティファクトができる。

f:id:x67x6fx74x6f:20170804153803p:plain

 

Shadow のパラメーターを調整したもの

f:id:x67x6fx74x6f:20170804153723p:plain

  

注意点

  • コードではライトの種類を選択後 IES ファイルの読み込みが呼ばれると Omni、Spot など他のライトを選択していても IES に変更される
  • モバイルの GPU で再現するため記述されている IES が意図通りに表示されない可能性がある
  • Scene Editor で IES Profile に適合していないファイル(特にバイナリ)を読み込むと Xcode が落ちたり scn が壊れる可能性があるので慎重に
  • Scene Editor で IES Lgiht を使用した場合は、必ず IES ファイルをプロジェクトの中にあるものから読み込む。プロジクト外でも Scene Editor では読み込めるが、ビルドした環境では IES ファイルが読み込めなくなるため  

サンプル

今回は scn がそこそこ複雑なのでファイルを用意。

  • IES.scn
  • iesprofile.ies

github.com

 

コード

let light = SCNLight()
light.name = "IES"
light.type = .IES
light.intensity = 1000

light.castsShadow = true

let iesPath:String = Bundle.main.path(forResource: "iesprofile", ofType: "ies")!
let fileURL:URL = URL(fileURLWithPath: iesPath)
light.iesProfileURL = fileURL

let lightNode = SCNNode()
lightNode.light = light
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)

 

今回はここまで

iOS で SceneKit を試す(Swift 3) その50 - Scene Editor の Directional Light

太陽光のように一定の方向に光を放つ Directional Light のご紹介。

設置位置に関わらず一定で光の影響を与える。 遮るジオメトリがあった場合に影を与えるキャストシャドウを設定することができる。

 

以下の画像は、ライトの位置を変更しても、光沢と影が変わらない例。

f:id:x67x6fx74x6f:20170802171700p:plain

 

ハンドラの薄い赤の矢印がライトの光の方向を表している。

f:id:x67x6fx74x6f:20170802171804p:plain

 

シーン設定

Omni Light で作成したシーンをそのまま使用する。

適当なシーンに Object Library から Directional Light をドラッグ&ドロップしても可。
Floor や Plane を下に敷いておくとわかりやすいと思われる。

Scene Editor 上で動作を見ていくが、実機で動かす場合は GameViewController.swift にあるライトの記述をすべて消す。

 

Directional Light の設定

Scene Graph View から Omni Light を選択し、Attributes Inspector (Command + Option + 4) を開く。

f:id:x67x6fx74x6f:20170802170022p:plain

 

Type が Omni になっているので Directional に変更する

f:id:x67x6fx74x6f:20170802170035p:plain

  

Directional Light の Attrubutes Inspector

以前紹介した共通の設定にキャストシャドウと Scale の項目を追加したもの。

キャストシャドウは Spot Light で紹介しているのでこちらを参照。

 

Scale

キャストシャドウの大きさを設定する。 デフォルト値は 1。

そのままだと宇宙船の影が小さい四角になっているのがわかる。

f:id:x67x6fx74x6f:20170802170112p:plain

 

Directional Light は影の大きさを指定する必要があり、試しに 20 にしてみると影が現れる。

f:id:x67x6fx74x6f:20170802170134p:plain

 

現状はこのように手動で設定しているが、
iOS 11 では自動で Scale を決める設定と、距離に応じてシャドウマップの質を決めるカスケードが追加されている。

 

コード

let light = SCNLight()
light.name = "directional"
light.type = .directional
light.intensity = 1000

light.castsShadow = true
light.orthographicScale = 20

let lightNode = SCNNode()
lightNode.light = light
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)

 

今回はここまで

iOS で SceneKit を試す(Swift 3) その49 - Scene Editor の Spot Light と Cast Shadow (Shadow Mapping)

舞台照明で使用されているスポットライトを模した Spot Light のご紹介。

設置位置から円錐状に光の影響を与え、任意で遮るジオメトリがあった場合に影を与えるキャストシャドウが使用できる。

 

その前にキャストシャドウとは?

シャドウマップというライトから影の情報を持ったテクスチャを作成し、
シーンにあるジオメトリにそれ適応させることで、物体に影が落ちている表現する。

 

さっくり説明すると、
ライトをカメラにしてジオメトリを映しベタ塗をする。
(下の画像では、ライトはジオメトリの上、X方向-90度に回転)

f:id:x67x6fx74x6f:20170802153403p:plain

 

ライトから照射される光の方向に他のジオメトリがあった場合、先ほどのベタ塗りをテクスチャをライトから影として適応するとディテールを持った影ができる。

f:id:x67x6fx74x6f:20170802174110p:plain

 

ジオメトリと他のジオメトリが遠い場合は、
ベタ塗りをテクスチャのサイズを変更したり、薄くしたり、ぼかしたりすることで遠くから影が落ちていることを表現する。

f:id:x67x6fx74x6f:20170802153423p:plain

 

影の演算処理はマシンパワーを使用するる為、リアルタイムの CG ではこのような工夫がされている。

 

シーン設定

Omni Light で作成したシーンをそのまま使用する。

適当なシーンに Object Library から Spot Light をドラッグ&ドロップしても可。
Floor や Plane を下に敷いておくとわかりやすいと思われる。

Scene Editor 上で動作を見ていくが、実機で動かす場合は GameViewController.swift にあるライトの記述をすべて消す。

 

Spot Light の設定

Scene Graph View から Omni Light を選択し、Attributes Inspector (Command + Option + 4) を開く。

Type が Omni になっているので Spot に変更する

f:id:x67x6fx74x6f:20170802151739p:plain

 

照明が宇宙船に当たるよう X 軸を -90 度傾ける。

f:id:x67x6fx74x6f:20170802152038p:plain

 

Spot Light の Attrubutes Inspector

以前紹介した共通の設定以外では以下のもの。

f:id:x67x6fx74x6f:20170802151951p:plain

 

Spot Parameters

円錐の円の大きさと光の減衰設定。

Outer angle

円錐の円の大きさを決める。
デフォルト値は 45 度。

65 度に設定したもの

f:id:x67x6fx74x6f:20170802152124p:plain

 

Inner angle

内側の円を決めることで光の減退を設定する デフォルト値は 0 度。

55 度に設定したもの。

f:id:x67x6fx74x6f:20170802152147p:plain

 

Inner angle、Outer angle の値が同じ場合は減退が起きない。

f:id:x67x6fx74x6f:20170802152206p:plain

 

Attenuation

スポットライトの始点から終点間での光の減衰を設定する。
(円錐の尖ったところから底の円の部分まで)

設定方法は Omni Light で紹介しているので割愛。

f:id:x67x6fx74x6f:20170802152250p:plain

 

Shadow

ジオメトリに影を与えるキャストシャドウの設定を行う。

Casts Shadows

チェックを入れると、スポットライトの光の進行方向にジオメトリがあった場合に影が落ちる。

デフォルト値は false でチェックが入っていない。

f:id:x67x6fx74x6f:20170802152400p:plain

 

Color

影に色をつける。
半透明も使用でき、デフォルト値は黒で、アルファは 1 の不透明になっている。

後ほど説明する Mode で「Deferred」を選択しないと設定した色は反映されず、影は黒になる。

 

Sample radius

影の輪郭のシャープさを設定する。
値を小さくするとシャープになり、大きくすると輪郭がぼやける。

デフォルト値は 3.0。

 

Near clipping, Far clipping

影の適応範囲。 デフォルト値は Near が 1.0、Far が 100.0。

例えば Far で 10 を設定するとそれ以降は影が乗り、 Near で 10 を設定するとスポットライトから 10 離れたジオメトリの影を描画し始める

 

Far: 10.0

f:id:x67x6fx74x6f:20170802152451p:plain

 

Near: 10.0

f:id:x67x6fx74x6f:20170802152508p:plain

 

Sample count

シャドウマップに重み付けを行い複数か処理しソフトエッジのシャドウを生成する。 サンプル数が多くなれば影はソフトになるが、処理が重くなる。

デフォルト値は 1。(macOS は 16) Scene Editor では表示上は 0。

 

Sample count: 1

f:id:x67x6fx74x6f:20170802152613p:plain

 

Sample count: 4

f:id:x67x6fx74x6f:20170802152642p:plain

 

Bias

複雑な形状の物体ではシャドウマップがうまく表示されない可能性がある。
それを補正するためのパラメーター。

デフォルト値は 1.0。

 

Map Size (width, height)

シャドウマップの画像の大きさ。
小さな画像にすると処理が軽くなるがそのぶん画像が荒くなる。

デフォルト値は縦横共に 0.0 で自動的サイズが調整される。

 

試しにかなり粗めの 100 x 100 px 描画したもの。

f:id:x67x6fx74x6f:20170802152721p:plain

 

Baking

Light Map 焼き込み時に、直接的な照明 Direct Light や間接的な照明 Indirect Light を使用するか設定する。

Scene Editor 上のパラメーターであるため、SCNLight のクラスでこのプロパティは存在しない。

 

Mode

影をつける処理のモード

説明
Forward デフォルト値。そのままシャドウマップを表示する。color で設定しても色は適応されないが透明度は適応される。
Deferred 最終画像をレンダリングした後に color で設定している影の色を適応する
Modulated 任意の画像の影(gobo)を使用し影を描画するモード。画像設定はコードから行う

 

コード

let light = SCNLight()
light.name = "spot"
light.type = .spot
light.intensity = 1000

light.castsShadow = true
light.shadowRadius = 3.0
light.color  = UIColor(red: 1.0, green: 0.0, blue: 0.502, alpha: 1.0)
light.zNear = 1.0
light.zFar  = 100.0
light.shadowSampleCount = 1
light.shadowBias = 1
light.shadowMapSize = CGSize(width: 0.0, height: 0.0)
light.shadowMode = .forward

let lightNode = SCNNode()
lightNode.light = light
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)

 

今回はここまで

iOS で SceneKit を試す(Swift 3) その48 - Scene Editor の Ambient Light

シーン全体に光を与える Ambient Light のご紹介。

設置位置に関係なく光の影響を与え、ジオメトリの暗部である陰の色を薄めたりできる。

 

シーン設定

iOS SceneKit のテンプレートでプロジェクト作成。

今回は床が必要ないので、ship.scn を表示するだけ。

Scene Graph View から shipMesh を選択し、マテリアルを Physically Based に変更。

一応、わかりやすくする為、
宇宙船のマテリアルの Lighting Type を Physically Based に変更しておくが今回の場合は必要はない。

 

光の影響を受けないためジオメトリが黒くなる。

f:id:x67x6fx74x6f:20170802105258p:plain

 

一旦、Omni Light を設定してみる

Object Library (Command + Option + Control + 3) から Omni Light をシーンにドラッグ。

f:id:x67x6fx74x6f:20170801183942p:plain

 

Omni Light は距離影響を受けるので Node Inspector (Command + Option + 3) を開いて y の 10 に設定。

宇宙船にライトが当たると思われる、

f:id:x67x6fx74x6f:20170802105404p:plain

 

Ambient Light の設定

Object Library から Ambient Light をシーンにドラッグ。

f:id:x67x6fx74x6f:20170802110209p:plain

 

適切な位置へ移動しなくても場所に関場所に光が適応されており、 Omni Light で陰が出ていた宇宙船の下の部分が真っ黒ではなくなっていると思う。

f:id:x67x6fx74x6f:20170802105432p:plain

 

Scene Editor 上で動作を見ていくが、実機で動かす場合は GameViewController.swift にあるライトの記述をすべて消す。

 

Ambient Light の Attrubutes Inspector

以前紹介した共通の設定のもの以外設定はない。

f:id:x67x6fx74x6f:20170802110159p:plain

 

デフォルトではカラーが灰色、intensity が 1000 になっている。

intensity が高めに設定されている為、カラーの値を白に変更すると陰の黒い部分がほとんど飛んでしまうので注意が必要。

f:id:x67x6fx74x6f:20170802105520p:plain

 

調整すると全体の光を変更することができる。

f:id:x67x6fx74x6f:20170802105646p:plain

 

Omni Light を消してみる

Ambient Light はジオメトリへ陰をつけない為、Omni Light をシーン上から削除するとジオメトリは単色の色が重ねられただけでフラットな見た目になる。

f:id:x67x6fx74x6f:20170802105803p:plain

 

以下の画像のように、宇宙船のテクスチャを取り除くとわかりやすい。

f:id:x67x6fx74x6f:20170802105905p:plain

  

使い所

全体の色の変更し、シーン印象を変えることが主な使用用途だと思われる。

また、Ambient Light はジオメトリへの陰の描画処理がされない為、演算処理が少ない。

ジオメトリにライトマップやオクルージョンのテクスチャを使用し、
影が落ちる必要のないシーンで使用することでライティングの処理が軽くなり全体の負荷を下げることができる。

 

コード

let light = SCNLight()
light.name = "ambient"
light.type = .ambient
light.intensity = 1000

let lightNode = SCNNode()
lightNode.light = light
lightNode.position = SCNVector3(x: 0, y: 10, z: 0)
scene.rootNode.addChildNode(lightNode)

 

今回はここまで