Azure Remote Rendering REST API を使って Azure Blob Storage の3Dモデルを変換する方法

# 概要
Azure Remote Rendering - REST API を使って Azure Blob Storage にアップロードされている3Dモデル (glTF) を Azure Remote Rendering 専用のバイナリ形式 ( ArrAsset ) に変換する方法を調査したので、以下調査した内容を記事に残しておきます。

# 前提知識 : Azure Remote Rendering とは？
Azure Remote Rendering は Azure Mixed Reality サービスの１つで、Azure 上のハイエンドな GPU マシン ( Azure VM ) を使ってハイポリゴン ( 高品質・高精細 ) な 3D コンテンツをレンダリング、リアルタイムでローカル ( PC, HoloLens 2 ) へのレンダリング結果をストリームしてれくれるサービスです。サービスの概要とサンプルについては、[過去の登壇資料](https://speakerdeck.com/futo23/azure-remote-rendering-gong-shi-sanpuruche-di-jie-shuo) でまとめているので、気になる方はご覧ください。

<img width="600" alt="image.png (959.0 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/53343800-0abe-498c-a7b6-d3c41032c62a.png">


Azure Remote Rendering ( ARR ) の レンダリングエンジン は、``FBX`` や ``GLTF`` などの
ファイルフォーマット (拡張子) を直接処理することができないため、3Dモデルデータを
ARR 専用のバイナリ形式 ( ArrAsset ) に変換する必要があります。(※ 月 100 トランザクションまで無料で変換することが可能です。詳しい費用は [価格表](https://azure.microsoft.com/ja-jp/pricing/details/remote-rendering/) をご覧ください。  )

モデルデータの変換は、以下3種類の方法が提供されています。

(1) Azure Remote Rendering Asset Tool (ARRT) での変換方法
(2) PowerShell スクリプトを用いた変換
(3) Azure Remote Rendering REST API を用いた変換

今回は **(3) Azure Remote Rendering - REST API を用いた変換** をやってみたいと思います。

# Remote Rendering - REST API

Azure Remote Rendering では、以下8種類の API が提供されています。

| 項目 | 概要 |
| --- | --- |
| API Version | 2021-01-01 |
| API Docs | https://docs.microsoft.com/en-us/rest/api/mixedreality/2021-01-01/remote-rendering |
| **API 一覧** | 
| Create Conversion | Azure Blob Storage アカウントに保存されたアセットを ArrAsset に変換する API |
| Create Session | 新しいレンダリングセッションを作成可能な API |
| Get Conversion | 特定のコンバージョン (モデル変換) スタータスを取得可能な API |
| Get Session | 特定のセッションのプロパティを取得する API |
| List Conversions | コンバージョン ( モデル変換 ) 一覧を取得する API |
| List Sessions | リモートレンダリングセッション一覧を取得する API |
| Stop Session | 特定のリモートレンダリングセッションを停止する API |
| Update Session | 特定のリモートレンダリングセッションの接続時間 (最大値) を更新する API  |

今回は ``Create Conversion`` API を使用して、Azure Blob Storage の 3Dデータを変換します。

# Azure 構成

Azure サービスの構成は以下の通りです。

<img width="397.91999999999996" alt="image.png (48.7 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/04/10862/5b4a92cb-b08a-4938-ae84-2351d1f6b140.png">

※ Azure Remote Rendering アカウントの [マネージドID (システム割り当て)](https://docs.microsoft.com/ja-jp/azure/active-directory/managed-identities-azure-resources/overview#managed-identity-types) を有効にして、
　ロールの割り当てを行う必要があります。


# 手順
## Azure Remote Rendering アカウントを作成する

Azure Portal へアクセスして 「Remote Rendering Accounts」を検索します。

<img width="959.9999999999999" alt="image.png (162.0 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/e38ff3cf-b890-4065-b770-cae0f2be34a9.png">

Remote Rendering Accounts ページ遷移後、**[+作成]** ボタンを押して
Azure Remote Rendering アカウントを新規作成します。


<img width="959.9999999999999" alt="image.png (126.4 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/4edce4a8-09e6-45f4-a14f-4a27593847cb.png">

リソースグループ、インスタンス詳細 ( 名前、 リージョン ) を指定して
 **[確認と作業]** ボタンを押します。

<img width="666.24" alt="image.png (212.5 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/c552d90e-004c-48e3-acea-04fe3c536b10.png">

検証に成功した後 **[作成]** ボタンを押します。

<img width="614.4" alt="image.png (121.9 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/485775f2-7f9c-4cbf-bc74-68da432b7cf2.png">

Remote Rendering の作成が正常終了したら、以下3つをメモしておきます。

- **Account ID**
- **Account Domain**
- **Access Keys ( Primary Key )**

「Overview」ページ
> <img width="793.92" alt="image.png (240.6 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/c719a467-0e67-4d14-8224-7a396d44dd65.png">

「Settings」> 「Access Keys」 ページ
> <img width="961.4399999999999" alt="image.png (120.6 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/5d96b97d-ad76-48bd-96f4-600e820aaf72.png">



## Azure Storage コンテナー  を作成する

Azure Portal を開き、3Dデータ保存用の Azure Storage アカウント を新規作成します。

<img width="959.9999999999999" alt="image.png (192.9 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/f0dcfb99-0084-462d-8a0b-d0ce2131ec70.png">

ストレージアカウントページに遷移後、**[+作成]** ボタンを押します。

<img width="959.9999999999999" alt="image.png (117.1 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/a2181113-b8ed-477b-9cce-d556829dee6f.png">

プロジェクト詳細 (サブスクリプション、リソースグループ)、インスタンス詳細 (ストレージアカウント名、地域、パフォーマンス) を入力して、**[確認および作成]** ボタンを押します。

<img width="659.52" alt="image.png (223.3 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/4fbd5c28-d8e8-487a-a715-c5563a495f9d.png">

検証成功後、**[作成]** ボタンを押します。

<img width="665.28" alt="image.png (163.8 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/db525a42-93b0-4876-88c4-18514059ff09.png">

リソースのデプロイが終わったら、ナビゲーションメニューから
 **[データストレージ] > [コンテナー]** を選択します。

<img width="196.32" alt="image.png (98.1 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/fa831b7d-08d1-4a2d-9bac-3d8c36c296f0.png">

**[+コンテナー]** ボタンを押して、以下コンテナーを作成します。

<img width="959.9999999999999" alt="image.png (139.0 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/95ed72db-93de-43b8-8e8d-b846e01c70eb.png">

**作成するコンテナー**
- arrinput ( 変換前の3Dモデルデータを保存 )
- arroutput ( 変換後の3Dモデルデータを保存 ) 

<img width="959.9999999999999" alt="image.png (159.6 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/fc7d0516-0ca6-4baa-b914-3e2fed99aab7.png">

以上で、3D データを保存するための Azure Storage コンテナーを準備できました。

## ARR インスタンス を Azure Storage にリンクする
Azure Remote Rendering インスタンス から Azure Storage アカウント へアクセスするためには、Azure Storage Account の アクセス制御 (IAM) から Azure Remote Rendering インスタンス の マネージドID へ ロールの割り当てを行う必要があります。( ※ この手順を行わないと REST API を実行した際に、権限周りのエラーが発生するので注意してください。)

まず、Azure Remote Rendering アカウントの マネージドID を有効にします。
( ※ デフォルト設定では、無効の状態 )

**[Settings] > [Identity]** を選択、システム割り当て済みの状態を **オン** に変更後、
**[保存]** ボタンを押します。

<img width="961.4399999999999" alt="image.png (228.2 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/ce3982e8-c350-459d-8526-c5886063390b.png">

システム割り当て マネージドID を有効にするか聞かれるので、**[はい]** と答えます。

<img width="961.4399999999999" alt="image.png (217.5 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/af5e368e-28c9-4db4-a2fd-7293b4cc13df.png">

オブジェクト ( プリンシパル ) ID が割り当てられました。

<img width="959.9999999999999" alt="image.png (320.2 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/38ea5730-20be-4386-b4cb-ed3e40e33276.png">

次に Azure Storage アカウント へ移動し、
ナビゲーションメニューより **[アクセス制御 (IAM)]** を選択します。

<img width="321.12" alt="image.png (141.0 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/cf39e1d3-b3fe-43df-88fb-affc1d136664.png">

アクセス制御 (IAM) ページに遷移した後、
**[このリソースへのアクセス権の付与] > [ロールの割り当ての追加]** ボタンを押します。

<img width="959.9999999999999" alt="image.png (324.2 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/b387b3b4-3d78-4823-baf6-5ad8674afbbf.png">

**[ストレージ BLOB データ共同作成者]** を選択後、[次へ] ボタンを押します。

<img width="694.0799999999999" alt="image.png (447.2 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/6dca26de-9c85-45cf-b2c4-4c6cd9d273d3.png">

メンバー設定へ遷移後、 **アクセスの割り当て先] の [マネージドID] にチェックを入れ**、**[＋メンバーを選択する]** ボタンを押して、Azure Remote Rendering インスタンスの マネージドID を登録します。

<img width="676.8" alt="image.png (127.2 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/9e2d1468-e4f8-4bc9-bbf7-57bed3809ddf.png">

メンバー追加が終わると、レビューを行い、ロールの割り当てを実行します。

<img width="959.9999999999999" alt="image.png (174.4 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/05/10862/7da44071-3e44-4c97-94c2-a39d152d28f3.png">

これで Azure Remote Rendering の マネージドID にロールの割当を行うことができました。
※ Azure Blob Storage の アクセス制御 (IAM) よりロールの割当を行うより詳しい方法は
[ストレージ アカウントをリンクする](https://docs.microsoft.com/ja-jp/azure/remote-rendering/how-tos/create-an-account#link-storage-accounts) をご覧ください。


## サンプルモデルの取得 と アップロード
今回は [KhronosGroup/glTF-Sample-Models ( GitHub )](https://github.com/KhronosGroup/glTF-Sample-Models) のサンプルを使用して検証を進めたいと思います。

サンプルリポジトリをクローンします。

```shell
git clone https://github.com/KhronosGroup/glTF-Sample-Models
```

今回は FOX の 3Dデータ (glTF) を ArrAsset に変換したいと思います。

> <img width="1660.5" alt="image.png (113.6 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/ef8782cb-3dd9-443e-8cbc-242fdb57dc7d.png">
> 
> [glTF-Sample-Models/2.0/Fox/](https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Fox)

Azure Portal を開き、Azure Blob Storage へ移動します。
ナビゲーションメニューより **[データストレージ] > [コンテナー]** を選択します。

<img width="279.35999999999996" alt="image.png (131.7 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/10d81ec4-0036-4eaf-b1db-aee7fea71023.png">

コンテナー一覧より、**[arrinput]** を選択します。

<img width="959.9999999999999" alt="image.png (72.6 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/490f600a-b109-4d58-8230-edd2781ff0fe.png">

**[アップロード]** ボタンを押して、さきほどのサンプルデータ (FOX) をアップロードします。

<img width="959.9999999999999" alt="image.png (132.5 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/2a2caaeb-8b4c-459c-a3d0-a54facf590bb.png">

Azure Portal 画面からだとフォルダごとアップロードができないみたいなので、
詳細設定でアップロード先フォルダーを指定してアップロードを行います。
(配置するデータは ``glTF-Sample-Models/2.0/Fox/glTF/`` 配下のもの全てになります。)

<img width="234.23999999999998" alt="image.png (97.1 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/d2036825-5830-444a-a71a-f09d83117fe8.png">

arrinput / Fox 配下に以下3つのファイルがアップロードされていることが確認できます。

<img width="850.5" alt="image.png (77.0 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/5b2b1f28-c0e2-41aa-9bc1-374bf1565f98.png">

- Fox.bin
- Fox.gltf
- Texture.png

以上で、サンプルモデルの準備が整いました。

## Remote Rendering REST API でモデルを変換する

それでは Azure Blob Storage にアップロードした  3Dデータ を変換していきたいと思います。

今回は Postman を使用して、Remote Rendering REST API を実行します。
REST API を実行するためには、``MRAccessToken (MRトークン)`` が必要となります。

この ``MRAccessToken`` は、Azure Mixed Reality Security Token Service (STS) から取得することができます。Azure Remote Rendering リージョンごとに、STS のエンドポイントが異なるので、注意してください。

### 1. MRAccessToken の取得 

[ GET ] :  ``https://sts.japaneast.mixedreality.azure.com/accounts/{Account ID}/token``

**リクエストヘッダー**

```json
{
  "Autorization": "Bear {Account ID}:{Access Key}"
}
```

もしくは、

```json
{
  "Authorization": "Bearer {Azure_AD_token}"
}
```

セキュリティの観点では後者が推奨されています。この REST API 呼び出しで返ってくるトークンが、``MRAccessToken ( MR トークン )`` です。

**レスポンス**

```json
{
  "AccessToken": "{MRAccessToken}"
}
```

<img width="790.0799999999999" alt="image.png (255.6 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/2f5dc49f-c126-4d38-88bb-91b24570e399.png">

取得したアクセストークン (赤枠部分) を元に、``Create Conversion`` API を実行します。

### 2. 3Dモデルの変換をリクエストする

[ PUT ] :  ``https://remoterendering.japaneast.mixedreality.azure.com/accounts/{Account ID}/conversions/{Conversion Id}/?api-version=2021-01-01``

**リクエストヘッダー**

```json
{
  "Autorization": "Bearer {Access Token}"
}
```

**リクエストボディ**

```json
{
  "settings": {
    "inputLocation": {
      "storageContainerUri": "https://demo20220203.blob.core.windows.net/arrinput",
      "blobPrefix": "Fox/",
      "relativeInputAssetPath": "Fox.gltf"
    },
    "outputLocation": {
      "storageContainerUri": "https://demo20220203.blob.core.windows.net/arroutput",
      "blobPrefix": "Fox/",
      "outputAssetFilename": "Fox.arrAsset"
    }
  }
}
```

Postman で実際に API リクエストを行うと、**200 Created** が返ってきました。

<img width="694.0799999999999" alt="image.png (297.4 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/0f2208dd-a77e-4aea-8ab3-a73ccca6775e.png">

続いて、``List Conversions`` API を実行してみると、
コンバージョン中のデータ一覧が返ってきました。
(※ 2~3分は ``status`` が ``NotStarted`` のままでした。)

<img width="526.0799999999999" alt="image.png (183.2 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/56cb3686-46a2-437c-be41-789166b605f2.png">

3 ~ 5 分経過後、再度同様の API を実行すると、
``Status`` が ``Succeeded`` と表示されました。

<img width="503.99999999999994" alt="image.png (181.9 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/06083b3d-9473-4dc3-a801-65d267e17895.png">

Azure Portal で ``arroutput`` コンテナーを開くと、
変換後のバイナリデータ ( ``ArrAsset`` ) が保存されていることが確認できました。

<img width="959.9999999999999" alt="image.png (326.4 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/1fe60148-7d70-4fb7-8f05-0e13a5c1850a.png">

試しに、ARRT ( Azure Remote Rendering Asset Tool ) を使って、
変換したモデルデータをレンダリングしてみたいと思います。

<img width="1365" alt="image.png (122.0 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/08/10862/f5c3cea1-dccc-475e-8e46-f5383ab44c9e.png">

``FOX`` データを Azure Remote Rendering で処理することができました！
Azure Remote Rendering Asset Tool (ARRT) の使用方法は、[Azure Remote Rendering Asset Tool (ARRT) の使い方 (2022.02 最新版)](https://qiita.com/Futo_Horio/items/31c42e458f4dbc1aa0c2) にまとめたので、興味がある方はこちらも合わせてご覧ください。

# 終わりに
無事 API で 3Dモデルを変換することができました。マネージドID や ロール割り当てはまだまだ理解できていない部分もあるので、今後はそのあたりも深堀りできると良いなと思います。最後までお読みいただき、ありがとうございました！

# 参考文献 / 資料

- [Remote Rendering - Create Conversation | MS Docs](https://docs.microsoft.com/en-us/rest/api/mixedreality/2021-01-01preview/remote-rendering/create-conversion)
- [ストレージ アカウントをリンクする](https://docs.microsoft.com/ja-jp/azure/remote-rendering/how-tos/create-an-account#link-storage-accounts)
- [認証を構成する](https://docs.microsoft.com/ja-jp/azure/remote-rendering/how-tos/authentication) ← Azure Remote Rendering API 認証の仕組み

過去にコミュニティイベントで登壇した際の資料も参考になるかと思います。
- [Azure Remote Rendering 公式サンプル徹底解説](https://speakerdeck.com/futo23/azure-remote-rendering-gong-shi-sanpuruche-di-jie-shuo)
- [Azure Remote Rendering Recap - サービス概要と活用事例](https://speakerdeck.com/futo23/azure-remote-rendering-recap-sabisugai-yao-tohuo-yong-shi-li)

## トラブルシューティング

Azure Remote Rendering アカウントに適切なロールが割り当てられていない場合、以下エラーが発生します。( <font color="red">Error accessing connected storage account due to insufficient permission.</font> )

<img width="906.2399999999999" alt="image.png (266.4 kB)" src="https://img.esa.io/uploads/production/attachments/3505/2022/02/03/10862/77477a21-254f-48a0-9fa6-02fa8e5c306d.png">