ARKit で 3D モデルのテクスチャの色をコード上から変更して表示する方法

# はじめに

こちらは [xRギルド Advent Calendar 2024](https://adventar.org/calendars/10320) の 18 日目の記事です。
私の他にも xR に関する記事がたくさん更新されていますので、読んでいただけると幸いです。

# 環境

macOS Sonoma 14.7.1
Xcode 16.1.0

# 本題

今回は ARKit において、取り込んだ dae 形式の 3D モデルのテクスチャの色をコード上から変更して表示する方法について解説していこうと思います。

## モデルを scn ファイルに変換する

まずはコード上から取り扱いやすく、マテリアルなどノード内の関係性もわかりやすくするために dae 形式から scn 形式にモデルを変換します

1. dae 形式の 3D モデル（[サンプル]((https://free3d.com/3d-model/low-poly-car-40967.html))）をプロジェクトに追加します

2. 次にモデルを選択した状態でツールバーから Editor > 「Convert to SceneKit file format (.scn)」を選択し、scn 形式に変換します

![スクリーンショット 2024-12-17 15.49.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46227/1c38c207-20b8-bd6e-7bd3-51fe197a50a0.png)

## 変換前後のモデルのマテリアルの対応を知る

変換前後でエディタ上のマテリアルがどう対応しているかは以下のように確認することでわかります。
モデルの構造によってはより深い位置のノードにマテリアルが適用されている場合もあります。

| dae 形式でのマテリアルの一覧 | scn 形式でのマテリアルの一覧 |
|---|---|
| ![スクリーンショット 2024-12-17 15.49.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46227/d917b324-fff5-ad2b-c3c3-56d04935518d.png) | ![スクリーンショット 2024-12-17 15.49.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46227/7d946447-1c3e-3324-a64e-88bba0e37435.png) |

## モデルのテクスチャの色を切り替える

1. まずはマテリアルを取得したいノードの名前を確認しておきます

![スクリーンショット 2024-12-17 16.28.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46227/5d613e7c-87ad-e50b-0902-ad0fac01cbe9.png)

2. 次に改めて scn 形式のモデルで変更したいマテリアルの名前を確認しておきます。今回は「Body」と「Lights」の色を変更していきます

![スクリーンショット 2024-12-17 15.49.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46227/7d946447-1c3e-3324-a64e-88bba0e37435.png)

3. 次に以下のようにすることでコード上から「Body」と「Lights」のマテリアルの色をそれぞれ変更できます

```swift
// Car.scn のシーンファイルを読み込む
let scene = SCNScene(named: "Car.scn")!

// シーンから Car ノードを取得してくる
if let carNode = scene.rootNode.childNode(withName: "Car", recursively: true) {

    // Body のマテリアルを取得
    if let body = carNode.geometry?.materials.filter({ $0.name == "Body" }).first {
        // マテリアルの色を変更
        body.diffuse.contents = UIColor.green
    }

    // Lights のマテリアルを取得
    if let lights = carNode.geometry?.materials.filter({ $0.name == "Lights" }).first {
        // マテリアルの色を変更
        lights.diffuse.contents = UIColor.yellow
    }
}

// sceneView にセットする
sceneView.scene = scene
```

### 実行結果

![IMG_1638.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46227/6d1fe34c-699e-8f0c-17e5-000bf1e93ba8.jpeg)


# おわりに

今回は色を変えるだけにしましたが、[SCNMaterialProperty の contents](https://developer.apple.com/documentation/scenekit/scnmaterialproperty/contents) は Any 型で、UIImage を設定すれば色だけでなく、画像テクスチャの切り替えなどにも応用することが可能です。

最近案件で実装した [競馬AR](https://www.youtube.com/live/GyHWlYh1SGM?si=oxwPd2t3EjNmHTKN&t=2725) における各馬の 3D モデルのゼッケンのテクスチャ画像の切り替えもこの仕組みで実装しました。

最後まで読んでくださりありがとうございました。
来年も xR 界隈盛り上げていけるように色々と挑戦していきたいです。

# 参考リンク

- [Change color of SCNNode/Geometry/Material - Stack Overflow](https://stackoverflow.com/questions/33828572/change-color-of-scnnode-geometry-material)
- [contents | Apple Developer Documentation](https://developer.apple.com/documentation/scenekit/scnmaterialproperty/contents)
- [Low-Poly Car 無料3Dモデル - .3ds .obj .dae .blend .fbx .mtl - Free3D](https://free3d.com/3d-model/low-poly-car-40967.html)