【Unity】旗がはためくシェーダー

はじめに
ShaderLabで、はためく旗をつくります。
テクスチャの表示の解説は省いていますがご了承ください。
環境
- Unity 2021.2.11f1
- Build-in Rendering Pipline
コード全文
FlagAnimation.shader
https://gist.github.com/went5/28bd105169fe62c31ef82c79b4ce81e9
Planeを両面表示する
Planeが片面しか表示されないと不都合があるため、CullOffキーワードを入れます。
このキーワードをいれるだけで、両面表示してくれます。
SubShader
{
Pass
{
Cull Off
Tags
{
"RenderType"="Opaque"
}
// ...省略
}
}
Sinを使って波のような形を作る

頂点を動かすため、頂点シェーダーを作っていきます。
sinを使って波のような形を取るには、以下の式を使います。
は周波数(Frequency)です。
を v.vertex.y 、 を v.vertex.x 、を _WaveFreq
とおくと以下のようなコードになります。
v2f vert(appdata v)
{
v2f o;
v.vertex.y = sin(v.vertex.x *_WaveFreq);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
Planeにシェーダーを貼ると上記の画像を再現できます。
カクカクしているのは、Planeの頂点数が少ないのかつ、周波数が大きいためです。
Time変数を使って動かす

Unityのビルトインのシェーダー変数 の _Time を使ってアニメーションをさせていきます。
のを増やしていくと、波を進められるので、を使って進めます。
v2f vert(appdata v)
{
v2f o;
v.vertex.y = sin(v.vertex.x*_WaveFreq + _Time.y * _TimeSpeed);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
上記のアニメーションは _TimeSpeed が1の場合のものです。
旗の動きに近づける

_WaveFreq と _TimeSpeed を調整して、旗の動きに近づけていきましょう。
筆者は以下の値にしました。
_WaveFreq: 0.27_TimeSpeed: 3.29
さらに旗の動きに近づける
このままだと、ポールに近い布の動きとポールに遠い布の動きが同じなため、ポールから布がずれてしまっています。
そのため、uv座標を使ってポールに近くところは揺れないように調整します。
fixed4 frag(v2f i): SV_Target
{
fixed4 col = fixed4(0.0, i.uv.x, 0.0, 1.0);
return col;
}

カメラの初期位置を変えていなければ、画面から見て左側のuv座標(x)の値が0に近づくことが分かります。
これを考慮したコードが以下のものになります。
v2f vert(appdata v) {
v2f o;
v.vertex.y = sin(v.vertex.x * _WaveFreq + _Time.y * _TimeSpeed) * v.uv.x;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
以上で冒頭の旗を再現できます。
参考
Table of contents