Appearance
第10回 シェーダー連携1
前回: 第09回 UIエフェクト2 / 次回: 第11回 描画バッファとポストエフェクト
今回の授業内容
- リング状メッシュのUVを斬撃の時間軸として利用する
- Shader Graphでテクスチャーとノイズをスクロールさせる
- Gradientとマスクで斬撃の形と透明度を作る
Phase、Speed、Intensityを公開して調整する- スクリプトから値を時間変化させて斬撃を再生する
表示に使うメッシュの形は固定です
斬撃が走る動きは、Shader Graphの計算とスクリプトによるパラメーター操作で作ります
メッシュを準備する: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を保存する
Sample Gradientはシェーダーコードへ変換されるため、Gradientを変更した場合はShader Graphを保存して反映します
さらに情報を載せる:ノイズの適用
この状態でも最低限の斬撃表現にはなっていますが、内部に動きや密度の変化がないため、少し物足りない印象です
ノイズを適用して、斬撃の内部にムラや流れを加えると、より勢いのある表現になります
ノイズはテクスチャーを用意しても構いませんが、ShaderGraph の Noise ノードを使って得ることも可能です ここでは Noise ノードを使う方法を紹介します
ノイズを適用する:シンプルな乗算から


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

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

斬撃エフェクトは出現してから消えるまでの 間に強さが変化するのが一般的です
斬撃の強さを制御するために Intensity プロパティを作りグラデーションや ノイズに乗算してみましょう
Intensity の値を操作することで、 斬撃の強さを時間変化させることができます
アニメーションさせる:スクリプトで制御する
Phase や Speed、Intensity などのプロパティを スクリプトで操作してみましょう
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:メッシュ上でテクスチャーをスクロールする
- リング状または三日月状のメッシュをシーンに配置する
URP Unlit Shader Graphを作成するTexture2DとPhaseプロパティを作るUV.x + Phaseを使ってテクスチャーをスクロールさせる- マテリアルをメッシュに貼り、
Phaseを変更して動くか確認する
- ここで動かない場合は、UV の向きやメッシュの UV 展開を確認します
手順2:グラデーションで斬撃の形を作る
UV.y - UV.xまたはUV.y + UV.xを使って斜めの値を作るModuloで 0 から 1 の範囲に折り返すSample Gradientで色と透明度を設定するPhaseを動かして、斜めの光が流れるか確認する- 端が不自然な場合は、Gradient の Alpha を調整する
- 斬撃の形は画像だけでなく、UV 計算でも作れます
手順3:ノイズで内部に情報量を加える
Noiseノードを追加する- ノイズ用の UV に
Time * Speedを足す - グラデーション結果とノイズを乗算する
Speedをプロパティ化して流れの速さを調整するIntensityを掛けて明るさを調整する
- ノイズは強すぎると斬撃の形に影響が出ます 最初は弱めにして、徐々に強くしてみましょう
手順4:スクリプトでPhaseとIntensityを変化させる
CrescentSlashを斬撃メッシュに追加するPhaseの AnimationCurve を設定するIntensityの AnimationCurve を設定するlifeTimeを 0.2 から 0.6 秒程度にする- 再生して、出現から消滅までの流れを確認する
- 斬撃は長く残りすぎると重く見えます 短時間で出て、少し余韻を残して消してみましょう
完成条件
- メッシュに Shader Graph のマテリアルを適用している
Phaseなどの値で、斬撃が移動して見える- Gradient またはマスクで透明度を調整している
- ノイズを使って内部の密度や流れを表現している
Intensityなどで出現から消滅までの強さを変化させている- スクリーンショットまたは動画で提出する
余裕があれば挑戦:さらに斬撃らしくする
- 外側に薄い発光用のメッシュを重ねる
- 火花や小さな粒子を追加する
- 色を属性ごとに変える 炎、氷、雷、闇など
AddSubtractMultiplyLerpなどで複数のノイズを混ぜる- 剣のモーションに合わせて斬撃の位置や角度を調整する
提出時の説明:調整の意図を添えてください
- 何の斬撃を表現したか
- メッシュの形をどう決めたか
PhaseとSpeedをどう調整したか- ノイズをどの程度加えたか
Intensityの変化をどう設定したか- 改善したい点
今回のまとめ:斬撃はメッシュとシェーダーの組み合わせ
- メッシュは斬撃の通り道を決めます
- UV を加工すると、固定されたメッシュ上で 光が移動しているように見せられます
Sample Gradientを使うと、色と透明度をまとめて調整できます- ノイズを加えると、斬撃内部に流れや密度を作れます
Phase、Speed、Intensityを時間変化させることで ゲーム中で使える斬撃エフェクトになります