シェーダー周りの備忘録(直面した問題と解決策まとめ)

2025 年 4 月 5 日 | カテゴリー: 中の人の戯言

ノンリニア16期のu.s.です。

ここには、私がUnity/C#のシェーダーを扱っている中で起きた問題と、それに対する自分なりの解決策を、備忘録としてまとめておきます。

状況1
「シェーダーのプロパティを Material.SetInteger や Material.SetFloat から変更したけど、変更が見た目に反映されてない!」

もしかしてそのプロパティ、Toggleとして扱ってる?

Toggleの場合、大抵プリプロセッサを用いた描画の条件分岐を行う。
例えば、”_AlphaSquare” というToggleプロパティを定義した場合、ToggleがONのときに “_ALPHASQUARE_ON” というキーワードが定義される:

Properties {
	[Toggle] _AlphaSquare ("Alpha Square", Float) = 0
}

として

#pragma shader_feature _ALPHASQUARE_ON

fixed4 frag(v2f IN) : SV_Target
{
	fixed4 c = SampleSpriteTexture(IN.texcoord) * IN.color;

	#ifdef _ALPHASQUARE_ON
	c.rgb *= (c.a * c.a);
	#else
	c.rgb *= c.a;
	#endif
	
	return c;
}

という感じで書くと、シェーダーのインスペクターにおける “Alpha Square” という名のToggleのON/OFFに応じて処理を分岐させることができる。

ところで、このプリプロセッサによる分岐は、動的ではなく静的なものである。

#ifdef … #else … #endif による分岐を仕組むと、コンパイル時に各分岐に対応するシェーダーが作成され、キーワードの有無に応じて分岐がなされるようになる。

つまり、キーワードによる分岐のパターン数だけシェーダーを出力し、ランタイムではそれらをキーワードの定義状況によって切り替える、というのを行っているっぽい。

インスペクターにおけるToggleのON/OFFは、キーワードの有効/無効が伴うみたいだが、Material.SetXxxx からToggleプロパティの値そのものを変更しても、ランタイムにおける対応するキーワードの有無には関わらない。

この場合に、ToggleのON/OFF切り替えと同じ効果をスクリプトから得たい場合は、Material.EnableKeyword / Material.DisableKeyword を用いて直接キーワードをいじることになる。

さっきの例だとこんな感じ。

[SerializeField]
private Renderer _renderer;

public void AlphaSquare(bool b)
{
    if (b)
    {
        _renderer.material.EnableKeyword("_ALPHASQUARE_ON");
    }
    else
    {
        _renderer.material.DisableKeyword("_ALPHASQUARE_ON");
    }
}

これだと何をしてるのかよく分からない!という場合は、そもそもプリプロセッサを使わずに動的なif文でシェーダーを記述すれば、従来の Material.SetXxxx を用いた描画処理の分岐もできそう。 (動的な分岐はパフォーマンスの観点から避けるべきと聞くが…実際どうなのでしょう?)

参考文献:
@up-hash. “「Shaderでif文を使ったら遅い」は正しくない🧐”. 2023-12-20.
https://qiita.com/up-hash/items/e932ccae110d687e225f

状況2
「エディターでは問題なく動いてたのに、ビルドしたら、シェーダーの効果がなくなってしまった!」

Unityでは、使われていない(と思われる)シェーダーはビルドから省かれるようになっているらしい。

このため、そのタイプのマテリアルを少なくとも一つ、アセットに用意して、 Unity にそのシェーダーバリアントを使う事を明示する必要があります。そのマテリアルは シーンで使われていなくてはなりません 。もしくは、 ランタイム時にリソースを読み込んで おくという方法もあります。そうしないと Unity からは、まだ使っていないように見えるため、ビルドから除外されてしまいます。

Unity Technologies. ” マテリアルパラメーターへのスクリプトを使ったアクセスと変更 – Unity マニュアル “. 2024-02-10. https://docs.unity3d.com/ja/2018.4/Manual/MaterialsAccessingViaScript.html

そのシェーダーをアタッチしたマテリアルがシーン上に1つでも存在すれば、それをビルドでも使うということを明示したことになるので、問題なく動作する。

しかし、例えばシーンロード後にInstantiateするプレハブのマテリアルに使っているものなど、最初からシーン上に存在しないようなシェーダーは、ビルド時に「使わない」と判断されてしまうらしい…

これを防ぐために、次のようなことを考えた。

私が使っている UnityEditor のバージョン 2022.3.23f1 には、Project Settings > Graphics に “Always Included Shaders” という項目がある。

早い話が、ビルド時に省略されたくないシェーダーをここにセットすれば、それらを全てビルド時に必ず含めてくれるようになる。

他にも対処法はあるかもしれないが、おそらくこれが一番手っ取り早くて分かりやすい方法なんじゃないかな?と思う。

ということで、もしビルド時のシェーダーの挙動がおかしいぞ?となった場合は、それがビルドから除外される対象になってないか確認し、上記の設定などを介して、除外を免れられるようにしてあげると解決するかも。

コメントはまだありません。