PICO4で動作しないUnityの機能とその対応策の紹介

これは [STYLY Advent Calendar 2023](https://qiita.com/advent-calendar/2023/styly) の6日目の記事です。

# PICO4で動作しないUnityの機能

2023年は、PICO4を利用したMRコンテンツ制作・イベント運営をたくさんやりました！

| <blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">今日はスタッフの皆さんとPICO4Eをセットアップしました！<br>42台並ぶと壮観！<a href="https://twitter.com/hashtag/STYLY?src=hash&amp;ref_src=twsrc%5Etfw">#STYLY</a> <a href="https://twitter.com/hashtag/PICO?src=hash&amp;ref_src=twsrc%5Etfw">#PICO</a> <a href="https://twitter.com/hashtag/%E3%82%A8%E3%82%A2%E3%83%AC%E3%83%BC%E3%82%B9?src=hash&amp;ref_src=twsrc%5Etfw">#エアレース</a> <a href="https://t.co/CgqVDJoaj7">pic.twitter.com/CgqVDJoaj7</a></p>&mdash; segur(せぎゅ)@都市ARエンジニア (@segur_vita) <a href="https://twitter.com/segur_vita/status/1713206926098780540?ref_src=twsrc%5Etfw">October 14, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> |
|:---:|
| 大量のPICO4でイベントを開催した様子 |

コンテンツ制作にあたり、他のデバイスでは動くのに、PICO4（のパススルーモード）では動かない現象にいくつか悩まされたので、今後のために記事にまとめてみました！

PICO4版STYLYで動作確認をしているので、もしかしたらSTYLY特有の現象かもしれませんが、ご了承ください！（その場合はPICOさん。ごめんなさい。）

# 加算ブレンド

残念ながら `加算ブレンド` 方式のシェーダーは、PICO4では動きません！
（公式ドキュメントにそういった記述は見当たりませんが、試してみて動作しませんでした。）

加算ブレンドとは、2つの色を合成するときに、背景色 `B` と前景色 `F` をそのまま足し算する手法です。

別の手法に `アルファブレンド` があります。
こちらは前景色 `F` に不透明度の `α` をかける手法です。
背景色 `B` には `(1-α)` をかけます。

2023年現在、アルファブレンドの方が主流です。

```mermaid
graph TD
    subgraph アルファブレンド
        B2[背景色B]
        F2[前景色F]
        C2[合成色C]

        B2 -->|1-α| C2
        F2 -->|α| C2
    end
    subgraph 加算ブレンド
        B1[背景色B]
        F1[前景色F]
        C1[合成色C]

        B1 -->|1| C1
        F1 -->|1| C1
    end
```

|| 加算ブレンド | アルファブレンド |
|---|---|---|
| 数式 | `C = F + B` | `C = F・α + B・(1-α) ` |
| shaderファイルの記述 | `Blend One One` | `Blend SrcAlpha OneMinusSrcAlpha` |
| αチャンネル | 不要 | 必要 |
| PICO4 | 非対応 | 対応 |

## 対応策

加算ブレンドのシェーダーがあれば、アルファブレンド方式に切り替えてください。

独自シェーダーで直接編集できる場合は、フラグメントシェーダーの戻り値にαチャンネルを設定してください。

以下のように、RGBからグレースケールを計算して、αチャンネルに代入すればOKです。

```c
fixed4 frag(v2f i) : SV_Target
{
    // RGB値が float3 color に格納されているものとします。

    // テレビのグレースケール変換の公式を使用
    float grayScale = dot(color, float3(0.299, 0.587, 0.114));

    // αチャンネルにグレースケールを代入
    float4 outputColor = float4(float3(color), grayScale);
    return outputColor;
}
```


# Custom Render Texture

残念ながら `Custom Render Texture` がPICO4では動きません！
（公式ドキュメントにそういった記述は見当たりませんが、試してみて動作しませんでした。）

Custom Render Textureとは、テクスチャの絵を動的にシェーダーで描画できる仕組みのことです。
イメージとしてはこんな感じ！

```mermaid
graph TD
    subgraph ShaderファイルA
        CRTShader[計算結果をテクスチャへ出力する処理A]
    end
    subgraph ShaderファイルB
        RenderShader[テクスチャをもとにサーフェスを描画する処理B]
    end

    CRT[Custom Render Textureファイル]
    Mesh[ディスプレイ]

    CRTShader -->|動的に描画する| CRT
    CRT -->|普通のテクスチャと同じようにサーフェスに貼り付ける| RenderShader
    RenderShader --> Mesh
```

詳しくは以下のページがわかりやすいです！

https://light11.hatenadiary.com/entry/2018/06/12/004133

上記のページに記載されているようなシンプルなシェーダーでも動作しないことを確認しました。

`Color Format` を `R8_UNORM` にしたり、いろいろ試しましたがダメでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221845/70fc798f-ee78-da51-cb21-76c5636e1f69.png)


## 対応策

もともと2つあったShaderファイルを1つのShaderファイルに統合しましょう！

以下のようなイメージです。

```mermaid
graph TD
    subgraph ShaderファイルC
        CRTShader[計算結果を変数へ出力する処理A]
        RenderShader[変数をもとにサーフェスを描画する処理B]
    end

    Mesh[ディスプレイ]

    CRTShader -->RenderShader
    RenderShader --> Mesh
```

処理Aと処理Bをそれぞれ関数化して、1つのShaderファイルに収めます。

変数名が衝突した場合は、リネームして回避してください。

これにより、CRTを使わなくて済み、PICO4でも描画できるようになりました！


# 最後に

本記事作成にあたり、以下のページを参考にさせていただきました。
ありがとうございました。

- https://scrapbox.io/rn-works/%E6%AF%94%E8%BC%83_:_%E3%82%A2%E3%83%AB%E3%83%95%E3%82%A1%E3%83%96%E3%83%AC%E3%83%B3%E3%83%89_vs_%E5%8A%A0%E7%AE%97%E3%83%96%E3%83%AC%E3%83%B3%E3%83%89
- https://light11.hatenadiary.com/entry/2018/06/12/004133
- https://forum.unity.com/threads/why-does-rendertexture-never-work-on-android-build-oculus-go.630559/

# 宣伝

Psychic VR LabではUnityエンジニア・サーバーサイドエンジニアを募集しています！！ご応募お待ちしています！！

https://www.wantedly.com/projects/1360135

https://www.wantedly.com/projects/1360123