Nember ArtのNFTを3D/VR空間に展示してみる

:::note warn
Nember Artはサービスを終了しており、本記事の内容は投稿時点での内容となります。
:::

# はじめに

Nember Artとは、Symbolブロックチェーンのテストネットを使ってNFTアートを扱うことができるプラットフォームです。（私の解釈）

https://nember.art/

作成されたNFTは、ブロックチェーンに乗っかっているデータなので、他のサイトでも見れたりするのが自然かなと思いました。

なので、3D空間に表示してみようと思います。あと、VRで入れるようにしてみようと思います。

# Unityで3Dワールドを作る

Unity

https://unity.com/ja

Unity Personalなら条件付き無料なので、ぜひ試してみてください。

https://store.unity.com/ja/products/unity-personal

## 建物

3Dモデルは買います。

https://assetstore.unity.com/packages/3d/environments/showroom-environment-73740

導入した図。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/78890103-a5c9-02d8-4bca-738c1942fb13.png)

これにNFTのメディアを表示するオブジェクトを追加します。

## アート表示

建物にアートを表示していきます。メディアデータはHTTPで取得できるようにしておき、実行時にダウンロードするようにします。

3D表示だけのやつと、VRのやつでやり方が異なります。

3D表示はブラウザで見れるように、WebGLでビルドすることを想定しています。VR用はVRChatのワールドとしてアップロードすることを想定しています。

### WebGL用

VRしない場合です。

展示パネルに合わせてQuadを置きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/22d96ea3-4d51-5177-18de-5760129eaa08.png)

MyPanoramaというスクリプトでメディアをダウンロードして表示させるようにします。

```C#
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class MyPanorama : MonoBehaviour
{
    [SerializeField] private string url = default;

    void Start() {
        StartCoroutine(GetTexture());
    }
 
    IEnumerator GetTexture() {
        UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
        yield return www.SendWebRequest();

        if(www.isNetworkError || www.isHttpError) {
            Debug.Log(www.error);
        }
        else
        {
            var tex2d = DownloadHandlerTexture.GetContent(www);
            GetComponent<MeshRenderer>().material.mainTexture = tex2d;
        }
    }
}
```

次に、視点移動ができるように、キーボードやマウスの入力をもとに、カメラを動かしていきます。

これもすでにあるものを使用します。

https://assetstore.unity.com/packages/tools/input-management/mini-first-person-controller-174710

Prefabを配置するだけで完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/3c8c1ba0-aab3-d979-d26c-4943249a22ea.png)

あとはPlayするとキーボードのWASDやカーソル操作やマウスでカメラを操作できます。

### VRChat用

VRChatのSDKを導入し、外部URLから画像をダウンロードできるコンポーネントを付けていきます。

SDKをダウンロード。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/232e0fdd-0281-2f52-2621-e3825daf08dc.png)

展示パネルに合わせてQuadを置き、VRCPanoramaコンポーネントを付けます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/4bf38aaf-a343-ce9e-13fb-e39b054aa042.png)

[ドキュメント](https://docs.vrchat.com/docs/creating-your-first-world)に従って必要なオブジェクトを配置します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/e0030025-7072-3d28-5f1f-3f55707307c3.png)

最後にアップロードです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/4afd33b2-40ec-145d-4f4b-4e1b02dd9fd3.png)

あとはVRChatにログインして、ワールドに入るだけです。

# バックエンド

バックエンド側には、2つの機能が必要です。1つはメディアデータを提供するHTTPエンドポイントで、もう1つはメディアデータを取得しておいてリサイズしたりキャッシュしたりする機能です。

これらはAzureの関数アプリで作りました。

## メディアのキャッシュ

こちらを参考にします。

https://qiita.com/planethouki/items/38a6e61d73e73141f843

以下のような関数を作成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/caa39379-00e1-1b34-5449-c186e55cc869.png)

AddQueueは定期実行しており、新しいブロックがないかを確認し、あったらQueueに登録します。

そのQueueをトリガーに、FetchTransactionsが起動し、対象のブロックからトランザクションを取得して、NFTが作られたトランザクションをフィルターしてQueueに登録します。

最後に、トランザクションのメタデータから、IPFSから画像を取得して、リサイズなどを行ったうえでBlobに書き込みます。

こんな感じでBlobに溜めていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172833/6f14efad-79c4-4304-732c-f8838b063a85.png)

## 画像のAPI

HTTPエンドポイントを作ります。リクエストが来たらBlobに保存された画像を読み取り、レスポンスに入れて返します。

URLは、クエリ文字列で`number=1`という風に番号を指定する形にします。

クライアント側はNFTのメディアのIPFSハッシュを知らないのと、（VRChatでは）動的なURL生成ができないからです。

関数アプリの前にはAzure CDNを配置しました。


# できたもの

https://youtu.be/dolCPfe1CS8

https://twitter.com/planethouki/status/1389816039513358337?s=20

https://twitter.com/planethouki/status/1383796606655340550?s=20

# おわりに

かなり端折って説明しました。

是非ともいろんな人にいろんなものを作って欲しいと思います。