【PlayCanvas】動的にCubemapをロードしてSkyboxを変更する

### はじめに

PlayCanvasで動的にCubemapのテクスチャを読み込みSkyboxを変更するための手順となります。


#### 準備
- PlayCanvasアカウント ( https://playcanvas.jp/ )
- Skybox用のテクスチャ 本記事では、「[Poly Haven](https://polyhaven.com/)」を使います。

### PolyHavenからテクスチャをダウンロード
![PolyHaven.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/669028b2-e805-7059-3e0d-fb6cadb6129d.jpeg)
ページにアクセスし[Burnt warehouse](https://polyhaven.com/a/burnt_warehouse)のHDR画像をダウンロードします。


### Texture Toolsで6面のテクスチャを作成

次に、Cubemapの画像を作成します。Webで完結するツールががあるのでこちらを利用します。
GitHubにて「[playcanvas/playcanvas-tools](https://github.com/playcanvas/texture-tool)」、OSSで公開されています。

1. Texture Tools ( [https://playcanvas.com/texture-tool](https://playcanvas.com/texture-tool) ) にアクセス

![TextureTools.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/56f9fd92-db17-902d-b7e7-a97552624daa.jpeg)
2.ダウンロードしたテクスチャをドラッグアンドドロップでアップロードし、「Reproject」 をクリックします。

![ExportToPng.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/d5956769-5543-15a6-fea5-26b29132d768.jpeg)
3.「Export TO HDR」をクリックして6面のテクスチャ画像をダウンロードします。


### PlayCanvasにインポート & Cubemapアセット作成

次に、PlayCanvasにアップロードをし、Cubemapのアセットを作成します。


![PrefilterCubemap.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/2c0a2989-ae69-56ff-6bd0-8db4819911ae.jpeg)

PlayCanvasエディタでCubemapアセットを作成し、「Prefilter Cubemap」をクリックします

### PlayCanvas エディタからCubemapのアセットをダウンロード

![DonwloadCubemap.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/b4026219-19b3-f585-68f5-76e8f3ca084d.jpeg)
インスペクター上から「DOWNLOAD」をクリックします。


![Files.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/5e6309a9-e5ce-996e-3928-601c243618b9.jpeg)
ダウンロードされたファイルはこのような形式でダウンロードされます。`dds`ファイル、`png`ファイル6つ含まれたZipファイルがダウンロードされます。



![RenamedFiles.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/8955eb58-6947-62e5-9271-cef30724d4ab.jpeg)
ダウンロードしたファイルを管理しやすいように名前の変更をします。

### 外部のホスティングサービスにCubemapをホスト

![VSCode.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/82e99dac-da6a-1d20-d4e5-19295c091b6b.jpeg)

先ほどダウンロードした`.ddsファイル` / `pngファイル`をホスティングサービスにアップロードします。
アップロードが完了したら、CubemapのURLをコピーしておきます。

今回アップロードしたURLはこちらとなります。

- `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burnt-warehouse.dds`
- `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_posx.png`
- `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_negx.png`
- `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_posy.png`
- `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_negy.png`
- `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_posz.png`
- `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_negz.png`





### スクリプトの設定

最後に、プロジェクトのCubemapはこちらをPlayCanvasで読み込むスクリプトを作成します。

```load-cubemap.js
var LoadCubemap = pc.createScript('loadCubemap');
LoadCubemap.prototype.initialize = function () {
    const cubemap = new pc.Asset('Cubemap', 'cubemap', {
        url: "https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burnt-warehouse.dds"
    }, {
        magFilter: pc.FILTER_LINEAR,
        minFilter: pc.FILTER_LINEAR_MIPMAP_LINEAR,
        anisotropy: 1,
        type: pc.TEXTURETYPE_RGBM,
        textures: [
            `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_posx.png`,
            `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_negx.png`,
            `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_posy.png`,
            `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_negy.png`,
            `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_posz.png`,
            `https://playcanvas-cubemap-test.vercel.app/cubemaps/burnt-warehouse/burunt-warehouse_negz.png`,
        ]
    });
    cubemap.loadFaces = true;
    cubemap.on('load', () => {
        this.app.scene.setSkybox(cubemap.resources);
    });
    this.app.assets.add(cubemap);
    this.app.assets.load(cubemap);
};

```
PlayCanvasエディタで、新しいスクリプトを作成します、このURLに先ほどコピーしたCubemapのURLを指定します。

![root.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/5c5ab314-0e1a-395d-f799-bd3757143069.png)
このスクリプトを「Root」エンティティに設定をします


### スクリプトの入ったPlayCanvasのアプリを実行


![Capture.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/e74a2b30-1bc4-ad6e-3fbd-daec9893cb93.jpeg)


「Launch」をクリックして、プロジェクトを実行します。
正しく実行されると、生成したCubemapが表示されます。

### ボタンをクリックで複数のSkyboxを切り替える

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/e07bbb72-ee2a-9ea3-036b-976a956bf153.png)
この方法使って複数のSkyboxを切り替えることもできます。上記の画像の[デモ](https://playcanv.as/b/7d7b06ec)では、複数のCubemapアセットを読み込んでいます。プロジェクトはパブリックのプロジェクトとなっておりますのでこちらからプロジェクトをご確認下さい。
- プロジェクト[https://playcanvas.com/project/1055420](https://playcanvas.com/project/1055420)

### スクリプトを設定
```load-cubemap-button.js
var LoadCubemapButton = pc.createScript("loadCubemapbutton");
LoadCubemapButton.attributes.add("assetBaseUrl", { type: "string", default: "https://playcanvas-cubemap-test.vercel.app/cubemaps/helipad/" });
LoadCubemapButton.attributes.add("dds", { type: "string", default: "Helipad.dds" });
LoadCubemapButton.attributes.add("face", { type: "string", default: "Helipad" });
LoadCubemapButton.attributes.add("anisotropy", { type: "number", default: 1 });
LoadCubemapButton.attributes.add("skyboxMip", { type: "number", default: 1 });
LoadCubemapButton.attributes.add("skyboxIntensity", { type: "number", default: 1 });
LoadCubemapButton.attributes.add("skyboxLuminance", { type: "number", default: 0 });
LoadCubemapButton.attributes.add("textureType", {
    type: "string",
    enum: [{ TEXTURETYPE_DEFAULT: "default" }, { TEXTURETYPE_RGBE: "rgbe" }, { TEXTURE_TYPE_RGBM: "rgbm" }, { TEXTURETYPE_RGBP: "rgbp" }, { TEXTURETYPE_SWIZZLEGGGR: "swizzleGGGR" }],
    default: "rgbm",
});
LoadCubemapButton.prototype.initialize = function () {
    // ボタンのクリックイベントをリッスン
    this.entity.button.on("click", this.loadCubemapFromUrl, this);
};

LoadCubemapButton.prototype.loadCubemapFromUrl = function () {
    // Cubemapアセットを作成
    const cubemap = new pc.Asset(
        this.dds,
        "cubemap",
        {
            url: `${this.assetBaseUrl}${this.dds}`,
        },
        {
            magFilter: pc.FILTER_LINEAR,
            minFilter: pc.FILTER_LINEAR_MIPMAP_LINEAR,
            anisotropy: this.anisotropy,
            type: this.textureType,
            textures: [
                `${this.assetBaseUrl}${this.face}_posx.png`,
                `${this.assetBaseUrl}${this.face}_negx.png`,
                `${this.assetBaseUrl}${this.face}_posy.png`,
                `${this.assetBaseUrl}${this.face}_negy.png`,
                `${this.assetBaseUrl}${this.face}_posz.png`,
                `${this.assetBaseUrl}${this.face}_negz.png`,
            ],
        }
    );
    cubemap.loadFaces = true;
    // Cubemapアセットのが読み込まれたら、スカイボックスに設定
    cubemap.on("load", () => {
        // 読み込まれたCubemapをシーンのスカイボックスに設定
        this.app.scene.setSkybox(cubemap.resources);
        // スカイボックスプロパティを設定
        this.app.scene.skyboxMip = this.skyboxMip;
        this.app.scene.skyboxIntensity = this.skyboxIntensity;
        this.app.scene.skyboxLuminance = this.skyboxLuminance;
    });
    // アセットレジストリにCubemapアセットを追加
    this.app.assets.add(cubemap);
    // Cubemapアセットをロード
    this.app.assets.load(cubemap);
};

```

### エンティティにスクリプトを設定

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/69735d92-0f6a-5eb5-e9a7-6f281f0e3d4d.png)
作成したボタンに対して`loadCubemapButton`のスクリプトを設定します。



### まとめ

PlayCanvasをして動的にCubemapをロードしてSkyboxを変更しました。これで複数のプロジェクトから利用ができるようになります。