【Unity】【シェーダ】MaterialPropertyBlockの使い方

MaterialPropertyBlockの使い方です。

Unity2018.3.1f1

MaterialPropertyBlock?

いま、こんな感じで色を変更できるシェーダがMaterialに設定されているとします。

f:id:halya_11:20190120170859p:plain

そしてこのMaterialを使うGameObjectが二つあります。

f:id:halya_11:20190120170951p:plain

ここでそれぞれのオブジェクトの色を別の色に変えたいとすると、
Materialをもう一個作って別々のMaterialをアサインしなければいけません。

二つくらいだったらいいのですが、このような色違いのGameObjectが100個あるケースを考えると、
少しプロパティを変更するだけでMaterialを100個インスタンス化するのは効率が悪そうです。

こんな時にMaterialPropertyBlockを使えば、Materialのインスタンスは変えずに一部のプロパティだけ変更できます。

シェーダを書く

検証のためにプロパティで色を変えられるシェーダを書きます。

Shader "Example"
{
    Properties{
        _Color ("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM

           #pragma vertex vert
           #pragma fragment frag
           #include "UnityCG.cginc"
            
            float4 _Color;

            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return UnityObjectToClipPos(vertex);
            }

            fixed4 frag () : SV_Target
            {
                return _Color;
            }

            ENDCG
        }
    }
}

シンプルなので特に説明は必要ないかと思います。
普通にInspectorから色を設定できます。

f:id:halya_11:20190120170859p:plain

スクリプトから色を変える

次にスクリプトからMaterialPropertyBlockを使って色を変更します。

using UnityEngine;

public class Example : MonoBehaviour
{
    [SerializeField]
    private Color _color;

    private MeshRenderer _renderer;
    private MaterialPropertyBlock _materialPropertyBlock;
    
    private void Start()
    {
        _renderer = GetComponent<MeshRenderer>();
        _materialPropertyBlock = new MaterialPropertyBlock();
    }

    private void Update()
    {
        // この時点ですでにほかのスクリプトなどからMaterialPropertyBlockが
        // セットされているかもしれないので、まずは取得する
        _renderer.GetPropertyBlock(_materialPropertyBlock);

        // MaterialPropertyBlockに対して色をセットする
        _materialPropertyBlock.SetColor("_Color", _color);
        
        // MaterialPropertyBlockをセットする
        _renderer.SetPropertyBlock(_materialPropertyBlock);
    }
}

説明はコメントに書いた通りです。

MaterialPropertyBlockを使うと、MateiralのInspectorから指定した色は無視され、
MaterialPropertyBlockで設定した色で上書きされます。

結果

このスクリプトをそれぞれのGameObjectにアタッチして再生、色を変えてみます。

f:id:halya_11:20190120172321p:plain

Materialのインスタンスは同一のまま、色を変更することができました。

プロパティは定義しなくてもいい

MateiralPropertyBlockでプロパティを変更する場合はシェーダのPropetiesを書く必要はないので消してもOKです。

Shader "Example"
{
    // Propetiesは消した

    SubShader
    {
        Pass
        {
            CGPROGRAM

           #pragma vertex vert
           #pragma fragment frag
           #include "UnityCG.cginc"
            
            // これは必要
            float4 _Color;

            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return UnityObjectToClipPos(vertex);
            }

            fixed4 frag () : SV_Target
            {
                return _Color;
            }

            ENDCG
        }
    }
}

ドローコールとかが減るわけじゃない

下記の記事にも書きましたが、MaterialPropertyBlockを使ったからと言ってDraw CallやSetPass Callの削減には繋がりません。

light11.hatenadiary.com

あくまでMaterialのインスタンスを無駄に増やさなくて済む機能になります。

関連

light11.hatenadiary.com