RealityKitで直方体の6面それぞれに異なるテクスチャを貼る方法

# はじめに

先月、「[iPad Pro の LiDARスキャナ を試してみた](https://qiita.com/surfinhamster/items/34001d889f514dccb705)」という記事を書きました。
その時はじめて RealityKit をいじったのですが、その後、ちょっとしたアイディアを実装してみようと思った時、RealityKit上のオブジェクトにコードのみでテクスチャを貼り付ける方法が見つからず、意外と悩んでしまいました。

ようやくその方法がわかったので、備忘録を兼ねて記事とします。

2017年に「 [ARKitで立方体の6面それぞれに異なるテクスチャを貼る方法](https://qiita.com/surfinhamster/items/1630ca14b947969a5111)」という記事を書いていますが、つまり同じことを RealityKit でやってみようという話です。

# 環境環境等

- MacBook (Retina, 12inch, Early 2015)
- macOS Catalina Version 10.15.5
- Xcode Version 11.5 (11E608c)
- iPad Pro (12.9インチ) (第4世代)/ iPhone X
- iOS 13.5 (17F75)

本記事に記載した範囲では LiDAR 搭載である必要はありませんので、普通の iPhone で試すことができます。

# 実装

## プロジェクトの作成
Xcodeで、テンプレートとして “Augmented Realit App” を選択し、Content Technology には “Reality Kit” を選択してプロジェクトを作成します。

## テクスチャの準備
「[ARKitで立方体の6面それぞれに異なるテクスチャを貼る方法](https://qiita.com/surfinhamster/items/1630ca14b947969a5111)」同様、6面分の画像を用意します。

画像は Assets に Texture として登録しておきます。
具体的には、Assets.xcassets の "+" ボタンをクリックし、"New Texture Set" を選択し、現れる枠に画像をドラッグ＆ドロップします。名前も適当につけておきます。


<img width="687" alt="スクリーンショット 2020-06-02 11.14.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56444/01ecd380-f51b-2cc9-e38c-e429ee336288.png">

## コード
まずは、ViewController.swift の viewDidLoad() に下記を追加します。
シーンにアンカーを追加し、直方体の生成と配置を行なってます。

なお、雛形のままだと、"Experience.rcproject"の読み込みとシーンへの追加なども viewDidLoad() 内に書かれていますが、それは削除してかまいません。

```swift:ViewController.swift
        // ----- ARKit3.5 ScenUnderstanding設定
        // いったん空に
        arView.environment.sceneUnderstanding.options = []
        
        // オクルージョンを有効化
        arView.environment.sceneUnderstanding.options.insert(.occlusion)

        // デバッグオプションの設定
        arView.debugOptions.insert(.showWorldOrigin)
        arView.debugOptions.insert(.showStatistics)
        
         // シーンにアンカーを追加する
         let anchor = AnchorEntity(world: SIMD3<Float>(0.0, 0.0, 0.0)) // 原点=デバイスの位置に置く
        arView.scene.anchors.append(anchor)
        
        // 直方体を生成
        let boxEntity = makeBox()
        
        // 初期位置の指定: anchorに対して、50cm画面奥、10cm上方に配置
        boxEntity.position = SIMD3<Float>(0.0, 0.1, -0.5)
        anchor.addChild(boxEntity)

```

直方体の生成〜テクスチャの貼り込みは下記のように行います。

```swift:ViewController.swift
    func makeBox() -> ModelEntity  {
        // 幅10cm、高さ20cm、奥行き30cmの立方体を作成
        let boxModel = ModelEntity(mesh: .generateBox(width: 0.1, height: 0.2, depth: 0.3, cornerRadius: 0.01, splitFaces: true))

        // 6面、別々のテクスチャを貼る
        var material1 = SimpleMaterial()
        var material2 = SimpleMaterial()
        var material3 = SimpleMaterial()
        var material4 = SimpleMaterial()
        var material5 = SimpleMaterial()
        var material6 = SimpleMaterial()
        material1.baseColor = try! .texture(.load(named: "Texture1"))
        material2.baseColor = try! .texture(.load(named: "Texture2"))
        material3.baseColor = try! .texture(.load(named: "Texture3"))
        material4.baseColor = try! .texture(.load(named: "Texture4"))
        material5.baseColor = try! .texture(.load(named: "Texture5"))
        material6.baseColor = try! .texture(.load(named: "Texture6"))
        
        boxModel.model?.materials = [material1, material2, material3, material4, material5, material6]

        return boxModel
    }
```

ポイントは下記の2つ。

### 6面に別々のテクスチャを貼る
.generateBox() で直方体の形状を定義していますが、この splitFaces: を true とすると、6面別々のテクスチャが貼られます。逆に false だと、どの面にも1番目のテクスチャが貼られます。

なお、この例では立方体の角を丸めていますが、cornerRadius: を 0 にすれば、普通の角ばった直方体になります。

### 画像ファイルからテクスチャを読み込む
SimpleMaterial() で、Material を生成し、その色(baseColor)として Assets から読み込んだテクスチャを設定します。

むむ、ここが分かりにくかったところです!

baseColor という属性名からは、赤とか青といった"色"を設定する属性としか想像できませんが、ここにテクスチャを貼れるという点が想像の範囲外。
またそこにテクスチャをセットする際に、引数を取る列挙型を利用するという点もやや技巧的なように感じます(よく考えると合理的なのですが)。

# 実行結果
まずは、スクリーンショットをご覧ください。

赤がX軸、緑がY軸、青がZ軸で、それらが交差している原点が、アプリを立ち上げたときのデバイスの初期位置で、一歩下がって、Z軸プラス方向からやや見下ろしている画像です。

<img width="375" alt="A64D3A73-A376-493C-B7A1-45C7DC8FA151.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56444/810e26a4-13f5-5f3c-43bf-d32f009e44a1.jpeg">


こちらは反対側から、つまりZ軸マイナス方向から原点方向をやや仰ぎ見る形になっている画像です。

<img width="375" alt="423CB375-CE15-41F3-ABD5-93FC0CB536FC.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56444/1b917cdb-bb07-3f9e-99dd-df3faf923bcc.jpeg">

ちょっと予想外の結果です。

「 [ARKitで立方体の6面それぞれに異なるテクスチャを貼る方法](https://qiita.com/surfinhamster/items/1630ca14b947969a5111)」の実行結果と比較してその違いを確認してください。
なんとテクスチャが裏返になっているうえに上下が逆で、各面に割り付ける順番も異なっています。SceneKitでのテクスチャ貼り付けとこんなに違う結果が得られるなんて。

こうならない設定があるか、あるいは何か合理的な理由があってこうなっているのだと思いますが、そのあたりはまだ調べがついていません。


# おわりに

RealityKit を使用して、コードだけでAR空間にオブジェクトを生成し、そこにテクスチャを貼り付けてみました。結果的に SceneKit との意外な差異に気がつくことにもなりました。

RealityKit を使うと簡単にARアプリを作成することができる反面、パーティクルが使えないなど、SceneKit に比べまだまだ不足している機能が多くあります。このあたりは今後拡充していくものと期待しています(とりあえず直近のWWWDCでの発表を楽しみにしています。)。

本稿がARアプリ開発の参考になれば幸いです。

# 参考

- [RealityKit + ARKit 3 + SwiftUI で宙に浮く Hello World テキスト in 拡張現実](https://qiita.com/niwasawa/items/3d1bd6af3ebbcadfb366)
- [ios - RealityKit / ARViewの両面素材を指定する方法は？](https://ja.coder.work/so/ios/1804399)
- [RealityKit の説明と SceneKit との違いについて考える (Xcode 11.0 Beta 1)](https://appleengine.hatenablog.com/entry/2019/06/07/052651)
- [ARKitで立方体の6面それぞれに異なるテクスチャを貼る方法](https://qiita.com/surfinhamster/items/1630ca14b947969a5111)
- [iPad Pro の LiDARスキャナ を試してみた](https://qiita.com/surfinhamster/items/34001d889f514dccb705)


# 個人的な宣伝
先月、iOS(iPad)アプリ「簡単
iOS(iPadOS)アプリ「簡単便利な階層型情報メモアプリ HiMemo」を個人開発でリリースしました。リリース時に悩んだセキュリティポリシーについての記事を近日中にまとめたいと思っています。
ご興味のある方は、こちらをご覧ください → https://apps.apple.com/jp/app/himemo/id1506694081
※ARは関係ないアプリです。