UnityでURPのLit shaderのモデルのmaterialのOffsetをスクリプトから変更する - yousanのメモ

UnityでURPのLit shaderのモデルのmaterialのOffsetをスクリプトから変更する

初めに

テクスチャの位置を変更した実装を行う際に スクリプトから任意のタイミングで materialのoffsetを変更したい場合があります。その際の実装でURPのlit shaderの場合に少し手こずったので記録しておきます。

実際にmaterialのoffsetを変更して キャラクターの目のテクスチャーの位置の変更にして 瞬きの実装は動画のようになります

開発環境

  • Unity 2022.3.42f1
  • URP 14.0.11

実装

スクリプトからshaderのテクスチャーのプロパティについて

以下のように URP/Litを使用した materialのOffsetを取得をするために、まずは shader側にテクスチャーの値にアクセスをする必要があります。

2022.3.42f1の時点では、URPのLitシェーダーでは、アルベド(ベースカラー)テクスチャのプロパティ名として _BaseMap が使用されています。
_MainTex は古い互換性のために存在しますが、シェーダーコードでは使用されていません。

MaterialPropertyBlockを使ってマテリアルのプロパティの取得・更新

マテリアルのプロパティにアクセスする際に 今回は MaterialPropertyBlock をします。

docs.unity3d.com

まずは shaderで使われているプロパティ名を以下のように定義します

// シェーダープロパティのIDを取得
private static readonly int BaseMapSt = Shader.PropertyToID("_BaseMap_ST");

プロパティブロックの取得と適用は以下のように行うことができます。

// プロパティブロックを取得(特定のマテリアルインデックスを指定)
facialRenderer.GetPropertyBlock(_propertyBlock, _materialIndex);

// プロパティブロックに新しい値を設定
_propertyBlock.SetVector(BaseMapSt, baseMapST);

// プロパティブロックを適用
facialRenderer.SetPropertyBlock(_propertyBlock, _materialIndex);

プロパティの存在確認は以下のように行うことができます。

var baseMapSt = _propertyBlock.HasVector(BaseMapSt) ? _propertyBlock.GetVector(BaseMapSt) : _targetMaterial.GetVector(BaseMapSt);

上記の関数を使い、以下のように取得及び反映をすることができます

        private void UpdateFacial(float facialValue)
        {
            if (_materialIndex == -1)
            {
                return;
            }

            // プロパティブロックを取得(特定のマテリアルインデックスを指定)
            facialRenderer.GetPropertyBlock(_propertyBlock, _materialIndex);

            // 現在の _BaseMap_ST を取得

            var baseMapSt =
                // プロパティブロックから取得
                _propertyBlock.HasVector(BaseMapSt) ? _propertyBlock.GetVector(BaseMapSt) :
                // マテリアルから取得
                _targetMaterial.GetVector(BaseMapSt);

            // 現在のオフセットYを取得
            float currentOffsetY = baseMapSt.w;

            // オフセットが変更されているか確認
            if (Mathf.Approximately(currentOffsetY, facialValue))
            {
                // 変更がない場合は終了
                return;
            }

            // オフセットのY値を更新
            baseMapSt.w = facialValue;

            // プロパティブロックに新しい値を設定
            _propertyBlock.SetVector(BaseMapSt, baseMapSt);

            // プロパティブロックを適用
            facialRenderer.SetPropertyBlock(_propertyBlock, _materialIndex);
        }

備考

shaderからテクスチャーにアクセスする際のプロパティーは以下のように定義されています

  • X(mainTex_ST.x):U軸方向(横方向)のタイルリング(スケール)
  • Y(mainTex_ST.y):V軸方向(縦方向)のタイルリング(スケール)
  • Z(mainTex_ST.z):U軸方向(横方向)のオフセット
  • W(mainTex_ST.w):V軸方向(縦方向)のオフセット