Help us understand the problem. What is going on with this article?

[Unity] Shaderのプロパティ名を自動取得してIDに変換する

前書き

UnityでShaderを作成した後にプロパティ名とか取得するけど、間違えたりするとめんどくさいときがありまして、実行時に自動でプロパティ名を取得してくれるスクリプトを作成しました。直接プロパティ名を入力するよりはIDに変換すると早いらしいのでそちらで対応してみました。

Unityのバージョンは2019.4.1f1を使用しています。
2018ですと使用している関数が対応していないので2019のLTSを使用することをお勧めします。

ソースコード

ImageやRenderer対応にするためにスクリプト上ではStartやAwakeで書かずに使う場所で取得してもらうようにしました。(もっといい方法あると思うが)
こんな感じで作ってみました。

ShaderController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.CompilerServices;

/// <summary>
/// シェーダー管理用スクリプト
/// 使用するマテリアルがあるオブジェクトの場所につける
/// </summary>
public class ShaderController : MonoBehaviour
{
    private Material shaderMaterial = null;
    private Dictionary<string, int> shaderPropertyDic;

    /// <summary>
    /// 初期化
    /// </summary>
    public void Initialize(Renderer renderer)
    {
        if (renderer == null)
            Debug.LogError("Renderer is null");
        shaderMaterial = renderer.material;
        GetShaderProperties();
    }

    public void Initialize(Image image)
    {
        if (image == null)
            Debug.LogError("Image is null");
        shaderMaterial = image.material;
        GetShaderProperties();
    }

    private void GetShaderProperties()
    {
        shaderPropertyDic = new Dictionary<string, int>();
        //Shaderのプロパティの数を取得し、名前とIDを登録する
        for (int i = 0; i < shaderMaterial.shader.GetPropertyCount(); i++)
        {
            string name = shaderMaterial.shader.GetPropertyName(i);
            int id = shaderMaterial.shader.GetPropertyNameId(i);
            shaderPropertyDic.Add(name, id);
        }
    }

    /// <summary>
    /// シェーダーのプロパティ名が存在するかどうか
    /// 存在するならそのIDを渡し、ないなら-1を返す
    /// </summary>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    private bool CheckPropertyName(string propertyName, out int id)
    {
        //すでに登録してあるかどうか
        if (shaderPropertyDic.ContainsKey(propertyName))
        {
            id = shaderPropertyDic[propertyName];
            return true;
        }

        Debug.LogError("そのプロパティ名は存在しません。プロパティ名:" + propertyName);
        id = -1;
        return false;
    }

    #region Setter

    /// <summary>
    /// ShaderにFloat型の値を渡す
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <param name="value">渡す値</param>
    public void SetFloat(string propertyName, float value)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return;
        shaderMaterial.SetFloat(id, value);
    }

    /// <summary>
    /// ShaderにInt型の値を渡す
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <param name="value">渡す値</param>
    public void SetInt(string propertyName, int value)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return;
        shaderMaterial.SetInt(id, value);
    }

    /// <summary>
    /// ShaderにColor型の値を渡す
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <param name="value">渡す値</param>
    public void SetColor(string propertyName, Color value)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return;
        shaderMaterial.SetColor(id, value);
    }

    /// <summary>
    /// ShaderにMatrix4×4型の値を渡す
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <param name="value">渡す値</param>
    public void SetMatrix(string propertyName, Matrix4x4 value)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return;
        shaderMaterial.SetMatrix(id, value);
    }

    /// <summary>
    /// ShaderにTexture型の値を渡す
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <param name="value">渡す値</param>
    public void SetTexture(string propertyName, Texture2D value)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return;
        shaderMaterial.SetTexture(id, value);
    }

    /// <summary>
    /// ShaderにTextureのoffsetを指定する
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <param name="value">渡す値</param>
    public void SetTextureOffset(string propertyName, Vector2 value)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return;
        shaderMaterial.SetTextureOffset(id, value);
    }

    /// <summary>
    /// ShaderにTextureのScaleを指定する
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <param name="value">渡す値</param>
    public void SetTextureScale(string propertyName, Vector2 value)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return;
        shaderMaterial.SetTextureScale(id, value);
    }

    /// <summary>
    /// ShaderにVector4型の値を渡す
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <param name="value">渡す値</param>
    public void SetVector(string propertyName, Vector4 value)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return;
        shaderMaterial.SetVector(id, value);
    }
    #endregion

    #region Getter

    /// <summary>
    /// プロパティ名を指定し、Float型の値を取得
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <returns></returns>
    public float GetFloat(string propertyName)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return 0.0f;
        return shaderMaterial.GetFloat(id);
    }

    /// <summary>
    /// プロパティ名を指定し、int型の値を取得
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <returns></returns>
    public int GetInt(string propertyName)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return 0;
        return shaderMaterial.GetInt(id);
    }

    /// <summary>
    /// プロパティ名を指定し、Color型の値を取得
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <returns></returns>
    public Color GetColor(string propertyName)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return Color.white;
        return shaderMaterial.GetColor(id);
    }

    /// <summary>
    /// プロパティ名を指定し、Matrix4×4型の値を取得
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <returns></returns>
    public Matrix4x4 GetMatrix(string propertyName)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return Matrix4x4.identity;
        return shaderMaterial.GetMatrix(id);
    }

    /// <summary>
    /// プロパティ名を指定し、Texture型の値を取得
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <returns></returns>
    public Texture GetTexture(string propertyName)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return null;
        return shaderMaterial.GetTexture(id);
    }

    /// <summary>
    /// プロパティ名を指定し、Textureのoffset値を取得する
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <returns></returns>
    public Vector2 GetTextureOffset(string propertyName)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return Vector2.zero;
        return shaderMaterial.GetTextureOffset(id);
    }

    /// <summary>
    /// プロパティ名を指定し、TextureのScale値を取得
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <returns></returns>
    public Vector2 GetTextureScale(string propertyName)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return Vector2.zero;
        return shaderMaterial.GetTextureScale(id);
    }

    /// <summary>
    /// プロパティ名を指定し、Vector4型の値を取得
    /// </summary>
    /// <param name="propertyName">プロパティ名</param>
    /// <returns></returns>
    public Vector4 GetVector(string propertyName)
    {
        int id;
        if (!CheckPropertyName(propertyName, out id))
            return Vector4.zero;
        return shaderMaterial.GetVector(id);
    }
    #endregion

    /// <summary>
    /// プロパティ名一覧をログ表示
    /// </summary>
    public void LogPropertyName()
    {
        foreach (string key in shaderPropertyDic.Keys)
            Debug.Log(key);
    }
}


Dictionaryにプロパティ名をkey、そこから取得したIDをvalueとして登録します。
一通りのGet,Setがあるのでそれを使用して値を変えてあげる。

使用方法

使用するShaderとスクリプトをを用意します。
Shaderは適当にリムライトのShaderでテストしてみます。

RimLight.shader
Shader "Unlit/RimLight"
{
    Properties
    {
        _RimPower("Rim Power",Range(0.1,5.0)) = 2
        [HDR]
        _RimColor("Rim Color",Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue" = "Transparent"}
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal:NORMAL;
            };

            struct v2f
            {
                float3 normal:NORMAL;
                float3 worldPos:TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            float _RimPower;
            fixed4 _RimColor;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                o.normal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                float3 L = normalize(i.worldPos.xyz - _WorldSpaceCameraPos.xyz);
                float d = 1 - saturate(abs(dot(i.normal, L)));
                d = pow(d, _RimPower);
                return _RimColor * d;
            }
            ENDCG
        }
    }
}

NewBehaviourScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    [SerializeField]
    private ShaderController controller = null;

    // Start is called before the first frame update
    void Start()
    {
        controller.Initialize(GetComponent<Renderer>());
        //ログ表示
        controller.LogPropertyName();

        controller.SetFloat("_RimPower", 3.0f);
        //あえて間違えてみる
        controller.SetColor("Hoge", Color.red);
    }
}

とりあえず_RimPowerを0.1としてみてみます。
before / after
コメント 2020-07-01 195336.jpg

コメント 2020-07-01 195350.jpg

プロパティ名が表示され、値も反映されています。
適当に入れたプロパティ名Hogeもそんなものないんですがと怒られています。

終わりに

そもそもマテリアルやShader欲しい、取得したいってなったら随時追加したりと改造して広げてみてください。
少しでも役立ったら幸いです。

では、またどこかで・・・。

dem
Unityのことをたまにつぶやきます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした