工場裏のアーカイブス

素人によるiPhoneアプリ開発の学習記 あと機械学習とかM5Stackとか

SceneKitにおけるテクスチャの利用(1)

本記事では、SceneKitにおけるテクスチャの利用法についてメモします。

SCNGeometry系の立体モデルへのテクスチャ貼付

SCNBox、SCNSphereなど、SCNGeometryより派生する汎用立体モデルに、以下の画像をテクスチャとして貼り付けてみます。

f:id:fleron:20150330220119p:plain

単純な貼り付けだけなら非常に簡単です。まずプロジェクトにテクスチャ用の画像ファイル(例えば texture.png とします)を追加します。そして以下のコードのように、立体モデルのオブジェクトを作成し、マテリアルのdiffuse.contentsプロパティにUIImageで画像を設定するだけです。

//SCNBoxにテクスチャを貼り付ける例
let box = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
box.firstMaterial?.diffuse.contents = UIImage(named: "texture")


それぞれの立体モデルには、あらかじめテクスチャ座標が設定されており、それに従いテクスチャが貼り付けられます。試しに、様々な立体モデルにテクスチャを貼って並べてみました(以下の記事のサンプルコードをベースに作成)。
Introduction To SceneKit – Part 2 - We ❤ Swift
f:id:fleron:20150323235526p:plain
左から順にSphere、Plane、Box、Pyramid、Cylinder、Cone、Torus、Tube、Capsuleです。それぞれの立体モデルでどのようにテクスチャが貼り付けられるのか、よく分かると思います。

このようにSCNGeometry系の立体モデルには手軽にテクスチャを貼れるのですが、その反面融通はきかず、あらかじめ設定されたテクスチャ座標は原則的に変更不能であるようです(特殊なテクニックを使えば変更出来る可能性もありますが、少なくとも標準的なSceneKitの範疇では無理なようです)。

そのため、例えばSCNBoxでは全ての面に同じテクスチャを貼ることしか出来ません。各面に異なるテクスチャを貼ってサイコロを作ったり、特定の面にだけテクスチャを貼ったり…ということは出来ないようです。

(2015/04/07 追記)SCNGeometry系の立体モデルでは、モデルを構成する各面(各要素)に、異なるマテリアルを設定可能であることを知りました(materials プロパティに SCNMaterial の配列を設定)。これを利用すれば、SCNBoxの各面に異なるテクスチャを貼ったり、特定の面にだけテクスチャを貼ったりすることは可能となります。誤った情報を失礼致しました。
 

各面に異なるマテリアルを設定(2019/12/31追記)

非常に今更となりますが、先述したモデルの各面に異なるマテリアルを設定する方法について、具体例も記しておきたいと思います。以下の画像をSCNBoxの一面だけに貼り付けてみます(以前作成した、周期表アプリの素材を流用しました)。
f:id:fleron:20191231150129p:plain

//SCNBoxにテクスチャを貼り付ける例
//テクスチャを貼るモデルを生成。ここではSCNBox(立方体)とする。
let box = SCNBox(width: 2.0, height: 2.0, length: 2.0, chamferRadius: 0.0)
        
//テクスチャのマテリアルを生成(テクスチャのファイル名はtexture.pngとする)
let tex = SCNMaterial()
tex.diffuse.contents = UIImage(named: "texture.png")
        
//テクスチャを貼らない面用に、色だけを設定したマテリアルを作成
let blank = SCNMaterial()
blank.diffuse.contents = UIColor.white
        
//モデルのmaterialsプロパティに、マテリアルを配列として与える
//モデルの持つ各面(立方体の場合は6個)に、配列の要素それぞれが対応する
box.materials = [tex, blank, blank, blank, blank, blank]

このようにすれば、モデルの一面だけにテクスチャを貼ることが出来ます。もちろんmaterialsプロパティに与える配列の中身を書き換えれば、二面や三面に貼ったりすることも可能です。
f:id:fleron:20191231151923p:plain

補足ですが、以下のようにテクスチャのマテリアルに対して、multiply.contentsプロパティに色を設定すると、テクスチャの色を変えることも出来ます。

//テクスチャのマテリアルを生成(テクスチャのファイル名はtexture.pngとする)
let tex = SCNMaterial()
tex.diffuse.contents = UIImage(named: "texture.png")
//演算によって、テクスチャの色を設定した色に変える
//(元のテクスチャで白色に近い部分ほど、設定色に染まるようなイメージ)
tex.multiply.contents = UIColor.magenta
        
//テクスチャを貼らない面用のマテリアルを作成
let blank = SCNMaterial()
blank.diffuse.contents = UIColor.magenta

box.materials = [tex, blank, blank, blank, blank, blank]

f:id:fleron:20191231151943p:plain
 

COLLADA(.dae)ファイルによる、外部モデルデータのインポート

SceneKitで自由にテクスチャを貼ったモデルを扱いたい場合は、外部の3Dモデリングソフトでモデルを作成してテクスチャを貼り、そのデータをSceneKitにインポートするのが確実な方法のようです。SceneKitはCOLLADAファイル(拡張子 .dae)形式のモデルデータのインポートに対応しているようです。
COLLADA - ウィキペディア

3Dモデリングソフトは、COLLADAファイルの出力に対応していれば何でも良いのですが、本記事では「SketchUp」というソフトを利用する例について紹介してみたいと思います。SketchUpは非常に直感的で分かりやすい操作によるモデリングが可能であり、非商用目的であれば無償版(SketchUp Make)を利用することが出来ます。無償版でも充分な機能が備わっており、テクスチャの貼り付けやCOLLADAファイルの出力も可能です。
SketchUp公式サイト
SketchUp - ウィキペディア

ここではSketchUpで下図のように、立方体の各面に異なるテクスチャを貼ったサイコロのモデルを作成したとします。このモデルをSceneKitにインポートする手順についてメモします。
f:id:fleron:20150331001058p:plain
 

  • まずSketchUpのメニューで「ファイル」→「エクスポート」→「3Dモデル…」を選択し、モデルデータをCOLLADAファイルとして出力します(ここでは「dice」という名前で出力)。下図のようにCOLLADAファイルと、テクスチャ画像が格納されたフォルダが生成されます。

f:id:fleron:20150331003346p:plain
 

  • 新しいフォルダを作成し、生成したCOLLADAファイルとテクスチャ画像フォルダを格納します。そしてこの新しいフォルダの名前を「〜.scnassets」という名前に変更します(ここでは「dicemodel.scnassets」とします)。下図のように確認ダイアログが表示されますが「追加」ボタンをクリックすればOKです。

f:id:fleron:20150331003832p:plain
 

  • SceneKitのプロジェクトに、メニューの「File」→「Add File to 〜」などから、先程作成した「〜.scnassets」フォルダを追加します。これでこのフォルダが、モデルデータとテクスチャをまとめたアセットとしてプロジェクトに認識されます(現在のところ、Xcode上だけで .scnassets 形式のアセットを作成することは出来ず、このように名前を変えたフォルダを外部から追加する手順が必要なようです)。

f:id:fleron:20150331005000p:plain
 

  • あとは必要なコードを書くだけです。以下にサンプルコードを示します。
//COLLADAファイルをSCNSceneとしてインポート
let diceScene = SCNScene(named: "dicemodel.scnassets/dice.dae")!

//インポートしたシーンから、サイコロのモデルをノードとして抽出
//SketchUpの出力では、デフォルトのノード名が「SketchUp」となっている。後述するエディタで編集も可能
let diceNode = diceScene.rootNode.childNodeWithName("SketchUp", recursively: true)!

//後は、普通のノードと同様に、プロパティ設定やシーンへの追加をすればOK
diceNode.position = SCNVector3(x: 0.0, y : 30.0, z : 0.0)
self.scnView?.scene?.rootNode.addChildNode(diceNode)  
diceNode.physicsBody = SCNPhysicsBody.dynamicBody()


実はCOLLADAファイルは3Dモデルだけではなく、カメラや光源などの情報も含めることが可能な形式であり、そのままSCNSceneとしてインポートして、シーンとして利用することが可能です。しかし、必要なシーンは既に準備済みであり、ただ3Dモデルのみを利用したい場合は、サンプルコードのように3Dモデルをノードとして抽出することが出来ます。

後は通常のノードと同様に、必要なプロパティを設定してシーンに追加すればOKです。もちろん物理ボディの設定なども可能です。実際にCOLLADAファイルから、サイコロのモデルを抽出して表示させてみた例を下図に示します(適当な床の上に乗せてみました)。
f:id:fleron:20150331015011p:plain
 

  • なお補足的ですが、プロジェクト上でCOLLADAファイル(〜.dae)をクリックすると、下図のようなエディタが起動します。このエディタでは3Dモデルの観察、ノードの名前変更、更には簡易的なデータ編集も行うことが出来るようです。

f:id:fleron:20150331011126p:plain