【Unity】スクリプトからShaderを変更する

2019/12/22DEVELOP

実行中にスクリプトからShaderを変更するには二通りの方法があります。Materialに割り当てられているShaderファイルを入れ替えるか、Materialごと入れ替える必要があります

目次

確認バージョン

2019.1.9f1

MaterialのShaderファイルを入れ替える

最もシンプルなShaderの入れ替え方は以下の様に書きます。

gameObject.GetComponent<Renderer>().material.shader = Shader.Find("Legacy Shaders/Diffuse");

予めビルドインシェーダーとして含まれているシェーダーであれば、Shader.Findが使えます。

Androidなど出力すると正常に表示されない(ピンク色現象)

Shader.Findできるシェーダーファイルは、ビルトインシェーダーに登録されているもののみです。そのためビルトインシェーダーに登録されていないと、アプリをビルドした際にシェーダーファイルを見つけることが出来ず表示がおかしくなってしまいます。

ビルトインシェーダーの場所

メニューの「Edit」->[Project Settings..]からGraphicsメニューのAlways Included Shadersから確認できます。任意のシェーダーファイルをこちらに追加するとShader.Findで取得できる様になります。

ビルトインシェーダーに含めない場合

予めビルトインシェーダー( Always Included Shaders )に含めない場合はResourcesにShaderファイルを含め、Resources.Loadで読み込んでしまっても良いです。

Shader shader = Shader.Find("Shaderファイルへのパス");
gameObject.GetComponent<Renderer>().material.shader = shader;

Materialごと入れ替える

Material ファイルには Shader が設定されています。となれば Material ごと入れ替えてしまっても良いです。経験としてはこちらの実装が多いです。

Material mat = Resources.Load<Material>( "Materialファイルへのパス" );
gameObject.GetComponent<Renderer>().material = new Material(mat); // コピーを使う。

Material はコピーしたものを使用します。コピーしておかないと、複数の GameObject に同じ Material を割り当てた場合、プロパティの値を共有してしまいます。

Material mat = Resources.Load<Material>( "Materialファイルへのパス" );
// クラスAのGameObject
gameObject.GetComponent<Renderer>().material = mat;
// クラスBのGameObject
gameObject.GetComponent<Renderer>().material = mat;
gameObject.GetComponent<Renderer>().material.SetFloat("_Color", Color.red)

サンプルの様にコピーせずに使用した場合、クラスBの_Colorプロパティの値を変更したいだけなのにクラスAの_Colorプロパティの値まで変わってしまいます。これは Resources.Load した Material ファイルがコピーではなく実態を伴っているためです。そのためコピーせず書き換えて UnityEditor を閉じたりするとファイル自体が書き換わってしまうので超危険です。必ずコピーしたものを使う様にしましょう。

後片付けは忘れずに

Material に限らず new してインスタンスをクローンしたものは破棄する癖をつけましょう。メモリリークします。C#はメモリリークしない言語というわけではありません。時々勘違いする人がいますが、そこまでお世話してくれません。

private Material m_material;
void OnDestroy()
{
    GameObkect.Destroy(m_material);
}
void Start()
{
    var mat = Resources.Load<Material>( "Materialファイルへのパス" );
    m_material = new Material(mat);
    gameObject.GetComponent<Renderer>().material = m_material;
}

まとめ

Unity で Shader を変更するためには Material内の Shader を差し替えるか、Material そのものを入れ替えるかすると良い。というお話でした。

Posted by kazupon