Skip to content

第10回 シェーダー連携1

前回: 第09回 UIエフェクト2 / 次回: 第11回 描画バッファとポストエフェクト

注意

この授業には提出課題があります

提出方法は 授業内で説明します

前回の振り返り

ゲージ UI を作りました

  • 第09回では、ゲージ UI と World Space Canvas を扱いました
  • 値の変化を視覚的に伝えるために、 マスクや fillAmount を使いました
  • 今回は自作のシェーダーで 3D 空間上の斬撃エフェクトを作ります

今回作るもの

メッシュ上を走る斬撃

画像

  • リング状のメッシュを用意する
  • シェーダーの計算で斬撃効果を移動させる
  • マスクで斬撃の形を加工する
  • ノイズを流して、内部の密度を変える
  • 光る色と透明度を調整して仕上げる

今日のゴール

計算で形状を作り、パラメーターで動かす

  • シェーダー側で、マスクやノイズを組み合わせて 斬撃が走ったように見せます
  • 表示に使うメッシュの形は固定です 動いて見える効果は、シェーダー側の計算で作ります

メッシュを準備する

UV を斬撃の時間軸にする

画像
画像
リング状のメッシュにUVを割り当てた例 UVのUを動かせば斬撃が進むように見える

  • メッシュを斬撃の通り道とします
  • UV.x を、斬撃が進む方向として使います
  • シェーダー側でUVを加工することで 「横長の画像の上を斬撃が進む」ように 扱います

最初の一歩

テクスチャーをスクロールさせる シェーダーを用意する

画像
設定の例 作成した ShaderGraph のマテリアルを 3D メッシュに貼って テクスチャーが動いて見えることを確認します

  • URP Unlit Shader Graph をベースに ShaderGraph を作成します
  • プロパティに Texture2D を1つ追加 float を1つ追加します
  • Sample Texture 2D ノードでテクスチャーを サンプリングして、UV を float でスクロール させます

このままテクスチャーで描いても構わないが

手続き的に絵を作る

  • 斬撃の形をテクスチャーで描くのも悪くありませんが シェーダー側でマスクやノイズを組み合わせて 表現する方法もあります
  • 計算で形状を操作できれば調整しやすく またアニメーション操作の幅が広がります
  • こうした手続き的に表現を作る手法を プロシージャルと呼びます

計算でグラデーションを作る

UV の値からグラデーションを得る

画像
画像ShaderGraphと適用例

  • UV の値は 0 から 1 の範囲で変化します
  • UV.x をそのまま使うと、左から右へ UV.y を使うと、上下方向のグラデーションが 得られます
  • UV.y から UV.x を引くと、左上から右下への グラデーションが得られます 今回はこれを斬撃の基本形状として使います

斬撃を動かす際の注意

計算の値は自動でラップしない

画像
Modulo の使用例 1で割った余りを返すので、1を超えると0に戻る 負の値は今回の目的には使えないので注意

  • UV の値は自動でラップしません 0 から 1 の範囲を超えると、グラデーションは 消えたり真っ白になってしまったりします
  • 斬撃の位置を動かす場合は、UV の値を 0 から 1 の範囲に収めるように計算する必要があります
  • ここでは Modulo ノードで値を折り返しています Modulo は剰余(余り)を計算するノードで 値を 0 から 1 の範囲でラップするのに便利です

UV 計算のサブグラフ化

計算が複雑になってきたら

画像
画像
UV計算をサブグラフ化した例 ノイズ計算など複数のブロックにUVを引き回す際には サブグラフ化で整理すると便利

  • UV に Phase を加えてModulo で折り返す計算は ShaderGraph 上では複雑な表示になります
  • こうした計算は、サブグラフとしてまとめると 扱いやすくなります
  • ある程度まとまった計算は、サブグラフにして 再利用しやすくしてみましょう

グラデーションに色を乗せる

SampleGradient の利用

画像
画像
SampleGradient の適用例 アルファも設定して、半透明描画で斬撃の端を 自然に消している

  • Sample Gradient ノードを使うと、値を色に変換できます
  • UV の計算で得たグラデーションを Sample Gradient に入れるとことで、好みの着色が可能です
  • 透明度も同時に指定できます 斬撃の端を自然に消すために、アルファも設定してみましょう

注意

Sample Gradient は シェーダーのコードで 作られるので、 変更の際はShader Graph の 保存が必要です

パラメーターじゃなくて プログラムなのよ

さらに情報を載せる

ノイズの適用

  • この状態でも最低限の斬撃表現にはなっていますが、内部に動きや密度の変化がないため、少し物足りない印象です
  • ノイズを適用して、斬撃の内部にムラや流れを加えると、より勢いのある表現になります
  • ノイズはテクスチャーを用意しても構いませんが、ShaderGraph の Noise ノードを使って得ることも可能です ここでは Noise ノードを使う方法を紹介します

ノイズを適用する

シンプルな乗算から

画像
画像
ノイズの適用例 単なる乗算でも一程度の効果が得られる

  • Noise ノードは、UV の値を元に ノイズの値を出力します
  • 斬撃のグラデーションにノイズを乗算するだけ でも最低限の効果は得られます
  • ノイズ関数には Gradient, Simple, Voronoi などがあります 好みのものを選び、パラメーターを操作して みましょう

ノイズをスクロールさせる

斬撃の流れを作る

画像

  • ノイズのUVをスクロールさせると、斬撃の内部に流れが生まれます
  • Time ノードを使って、ノイズに与えるUVを動かしてみましょう
  • プロパティに float の値を追加して、 TimeMultiply ノードで乗算すれば ノイズの流れの速さを調整できます

濃度を制御する

Intensity プロパティを作る

画像

  • 斬撃エフェクトは出現してから消えるまでの 間に強さが変化するのが一般的です
  • 斬撃の強さを制御するために Intensity プロパティを作りグラデーションや ノイズに乗算してみましょう
  • Intensity の値を操作することで、 斬撃の強さを時間変化させることができます

アニメーションさせる

スクリプトで制御する

  • PhaseSpeedIntensity などのプロパティを スクリプトで操作してみましょう
  • AnimationCurve を使って斬撃の出現から消滅までの強さを変化させると、より自然な表現になります

コード例 (1/2)

csharp
using UnityEngine;

[RequireComponent(typeof(MeshRenderer))]
public class CrescentSlash : MonoBehaviour
{
    [Tooltip("自動でアニメーションを開始")]
    [SerializeField] private bool playOnEnable = true;

    [Tooltip("アニメーションの再生時間(秒)")]
    [SerializeField, Min(0f)] private float lifeTime = 1f;

    [Tooltip("ループ再生するかどうか")]
    [SerializeField] private bool loop;

    [Tooltip("シェーダー内部の Time に掛ける Speed 値")]
    [SerializeField] private float speed = 1f;

    [Tooltip("Phase の変化カーブ")]
    [SerializeField] private AnimationCurve phaseCurve =
        AnimationCurve.Linear(0f, 0f, 1f, 1f);

    [Tooltip("Intensity の変化カーブ")]
    [SerializeField] private AnimationCurve intensityCurve =
        AnimationCurve.Linear(0f, 1f, 1f, 1f);
csharp
    private static readonly int SpeedPropertyId =
        Shader.PropertyToID("_Speed");
    private static readonly int PhasePropertyId =
        Shader.PropertyToID("_Phase");
    private static readonly int IntensityPropertyId =
        Shader.PropertyToID("_Intensity");

    private MeshRenderer _meshRenderer;
    private MaterialPropertyBlock _propertyBlock;
    private float _elapsedTime;
    private bool _playing;

    private void Awake()
    {
        _meshRenderer = GetComponent<MeshRenderer>();
        _propertyBlock = new MaterialPropertyBlock();
    }

    private void OnEnable()
    {
        if (playOnEnable)
            Play();
        else
            Apply(0f);
    }

コード例 (2/2)

csharp
    private void Update()
    {
        if (!_playing)
            return;

        _elapsedTime += Time.deltaTime;

        var normalizedTime = lifeTime <= 0f ?
            1f :
            Mathf.Clamp01(_elapsedTime / lifeTime);

        Apply(normalizedTime);

        if (normalizedTime < 1f)
            return;

        if (loop && lifeTime > 0f)
        {
            _elapsedTime %= lifeTime;
            return;
        }

        _playing = false;
    }
csharp
    public void Play()
    {
        _elapsedTime = 0f;
        _playing = true;
        Apply(0f);
    }

    public void Stop()
    {
        _playing = false;
    }

    private void Apply(float normalizedTime)
    {
        _meshRenderer.GetPropertyBlock(_propertyBlock);
        _propertyBlock.SetFloat(SpeedPropertyId, speed);
        _propertyBlock.SetFloat(PhasePropertyId,
            phaseCurve.Evaluate(normalizedTime));
        _propertyBlock.SetFloat(IntensityPropertyId,
            intensityCurve.Evaluate(normalizedTime));
        _meshRenderer.SetPropertyBlock(_propertyBlock);
    }
}

自由に作る

斬撃の形や色を調整する

  • 斬撃の長さや速さ、ノイズの密度や流れの速さ、 色などはすべてノードで作られています 調整して、好みのエフェクトを作成しましょう
  • ノイズも乗算するだけでなく、加算や Lerp などで組み合わせると さらに多様な表現が可能になります
  • 同時に複数のノイズやカラーを組み合わせるのも面白いでしょう 手元で自由に作成してみましょう

実習

斬撃を一つ完成させる

まずはやってみて

実習1:メッシュ上でテクスチャーをスクロール

まずはスクロールさせる

  1. リング状または三日月状のメッシュをシーンに配置する
  2. URP Unlit Shader Graph を作成する
  3. Texture2DPhase プロパティを作る
  4. UV.x + Phase を使ってテクスチャーをスクロールさせる
  5. マテリアルをメッシュに貼り、Phase を変更して動くか確認する
  • ここで動かない場合は、UV の向きやメッシュの UV 展開を確認します

実習2:グラデーションで形を作る

斬撃の基本形状を作る

  1. UV.y - UV.x または UV.y + UV.x を使って斜めの値を作る
  2. Modulo で 0 から 1 の範囲に折り返す
  3. Sample Gradient で色と透明度を設定する
  4. Phase を動かして、斜めの光が流れるか確認する
  5. 端が不自然な場合は、Gradient の Alpha を調整する
  • 斬撃の形は画像だけでなく、UV 計算でも作れます

実習3:ノイズを加える

斬撃の内部に情報量を足す

  1. Noise ノードを追加する
  2. ノイズ用の UV に Time * Speed を足す
  3. グラデーション結果とノイズを乗算する
  4. Speed をプロパティ化して流れの速さを調整する
  5. Intensity を掛けて明るさを調整する
  • ノイズは強すぎると斬撃の形に影響が出ます 最初は弱めにして、徐々に強くしてみましょう

実習4:スクリプトで再生する

Phase と Intensity を時間変化させる

  1. CrescentSlash を斬撃メッシュに追加する
  2. Phase の AnimationCurve を設定する
  3. Intensity の AnimationCurve を設定する
  4. lifeTime を 0.2 から 0.6 秒程度にする
  5. 再生して、出現から消滅までの流れを確認する
  • 斬撃は長く残りすぎると重く見えます 短時間で出て、少し余韻を残して消してみましょう

課題の条件

以下を満たしてください

  • メッシュに Shader Graph のマテリアルを適用している
  • Phase などの値で、斬撃が移動して見える
  • Gradient またはマスクで透明度を調整している
  • ノイズを使って内部の密度や流れを表現している
  • Intensity などで出現から消滅までの強さを変化させている
  • スクリーンショットまたは動画で提出する

余裕があれば挑戦

さらに斬撃らしくする

  • 外側に薄い発光用のメッシュを重ねる
  • 火花や小さな粒子を追加する
  • 色を属性ごとに変える 炎、氷、雷、闇など
  • Add Subtract Multiply Lerp などで複数のノイズを混ぜる
  • 剣のモーションに合わせて斬撃の位置や角度を調整する

提出時の説明

調整の意図を添えてください

  • 何の斬撃を表現したか
  • メッシュの形をどう決めたか
  • PhaseSpeed をどう調整したか
  • ノイズをどの程度加えたか
  • Intensity の変化をどう設定したか
  • 改善したい点

今回のまとめ

斬撃はメッシュとシェーダーの組み合わせ

  • メッシュは斬撃の通り道を決めます
  • UV を加工すると、固定されたメッシュ上で 光が移動しているように見せられます
  • Sample Gradient を使うと、色と透明度をまとめて調整できます
  • ノイズを加えると、斬撃内部に流れや密度を作れます
  • PhaseSpeedIntensity を時間変化させることで ゲーム中で使える斬撃エフェクトになります

おつかれさまでした!

次回予告 第11回 描画バッファとポストエフェクト

描画済みの画面色と 高輝度の再利用を扱います