Dual Heightfield Trailによるインタラクティブな草揺れ表現について

![blog_2022_programmer_tech19.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565926/b0a438ce-0a9a-4242-df7e-bdf730dfa8e1.jpeg)

トイロジックでGraphics関連を担当しているプログラマのTHです。
今回は **大量の草をインタラクティブに揺らす仕組みの一つ**について簡単にご紹介します。

自分が操作するキャラクターに反応して草が揺れると嬉しいですよね。
**私は嬉しいです:relaxed:**。 新作ゲームを買ったらまず草が揺れるか確認します。

しかし草というのは大量に配置される可能性があり、リアルタイムなゲームで揺らすには処理負荷に注意が必要です。また、倒れた草がゆっくり戻るだけでは単調なので揺れながら戻るような動きをさせたかったりします。

このような要件を満たす手法の一つとして、今回は **Dual Heightfield Trail**のご紹介をします。

# 投影テクスチャと頂点シェーダによる草揺れ
本題に入る前に、よく利用される（と思われる） **投影テクスチャと頂点シェーダ**による手法を簡単に説明します。

これは真上から地面に投影したテクスチャに足跡などを描画し、草モデルの頂点シェーダでそれを読み取って動かすことで揺れを表現する手法です。
まず投影テクスチャに毎フレームキャラクターの足跡スプライトを描画します。

![blog_2022_programmer_tech19_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565926/285d058b-fa41-1b46-87ba-6685af39f3dd.jpeg)

そして草モデルを描画するときに頂点シェーダでこの投影テクスチャを参照します。投影テクスチャ上で草モデルがどこに位置するか計算し、足跡が描画されていれば頂点シェーダで草を曲げるといったイメージです。

![blog_2022_programmer_tech19_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565926/1540d377-622f-fddd-cf02-225ff8880fdd.jpeg)

前回フレームの足跡を薄めて現在フレームにブレンドすることで、一度踏んだ草が徐々に戻るような表現も可能です。

この手法はキャラクターと草のヒット判定を単純なテクスチャ読取りに置き換えていると言えます。
そのためキャラクターや草が増えても負荷が増加しにくい、草を揺らす処理負荷をGPU(頂点シェーダ)にオフロードできるといったメリットがあります。
一方で高さの情報が無いために、キャラクターが空中にいるときに触れていないはずの草も揺れてしまうという問題があります。
キャラクターがジャンプしたり、地形が上下に重なっているようなゲームではおかしな位置の草が揺れるといった現象が起きてしまいます。

![blog_2022_programmer_tech19_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565926/d3527f39-7d1f-4576-1212-0e4f5fb2681f.jpeg)

# Dual Heightfield Trail
Dual Heightfield Trail は GDC2019の Interactive Wind and Vegetation in ‘God of War’ の中で発表された**草を揺らす手法の一種**です。

これは前述の投影テクスチャ手法の一種で、高さ情報をテクスチャに格納することで投影テクスチャ手法の問題を解決しようというものになります。

<p><iframe width="560" height="315" src="https://www.youtube.com/embed/MKX45_riWQA?si=uUsffdGMW9COY20E" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe></p>

この手法では**2チャンネルのテクスチャに2種類のHeightfieldを格納し、毎フレーム更新していきます。**

このテクスチャを利用することでキャラクターの位置の高さの考慮して草を揺らすことができ、さらには踏みつけられた後に一定時間揺れてから元に戻る表現ができるようになります。

# 2種のHeightfield
テクスチャに格納される値は以下のようになっています。

+ Rチャンネル : 足跡の高さを示すHeightfield
+ Gチャンネル : Rチャンネル値を一定速度で上昇(フェードアウト)させたHeightfield
```
"疑似コード部"
Gチャンネルにフェードアウト係数を加算して更新 
if キャラクターの足跡を描画する場合
    if 足跡の高さがGチャンネルよりも低い場合
        RチャンネルとGチャンネルを足跡の高さに書き換え
```
この更新処理によるテクスチャの変化を簡単に示した模式図が次の図右です。
キャラクターが等速で右方向へ移動している状況を横から見たものになります。
キャラクターが今いる場所はRとGが同じ高さになっていますが、キャラクターが移動した跡はGだけが上昇しています。

![blog_2022_programmer_tech19_04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565926/63554785-47c5-4c86-fe24-7ce9bfacc7a4.jpeg)

そしてゲーム中に実際に更新しているテクスチャが次の図です。
左からRGチャンネル、Rチャンネルのみ、Gチャンネルのみ となります。
![blog_2022_programmer_tech19_08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565926/b55c6680-69ed-02e5-d867-4e46dc17064d.jpeg)

Rチャンネルにはキャラクターが歩いた軌跡が黒く描かれています。平らな場所を歩いたため、模式図のRと同じようにどこも同じ高さ(黒さ)です。
Gチャンネルも同様に歩いた軌跡が描かれていますが、通過してから時間が経った場所はフェードアウトして白くなっています。

この辺りについてはGDC講演動画の**56:50 付近からのアニメーションがわかりやすい**と思います。
<p><iframe width="560" height="315" src="https://www.youtube.com/embed/MKX45_riWQA?si=LzeE8LW4Yr3FEQl_&amp;start=3410" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe></p>

# 草の揺れへの利用
まず、草自体の高さとRチャンネルを比較することで草が踏まれているか(又は踏まれていたか)を判断できます。
更に、RチャンネルとGチャンネルの差をフェードアウト速度で割ることで 踏まれてからの経過時間 が計算できます。
```
t = (G - R) / FadeSpeed
```
![blog_2022_programmer_tech19_05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565926/b5f03c5f-f49e-3009-15cc-cb4e102c669e.jpeg)
この経過時間を適当な減衰振動関数に入力することで、徐々に揺れ幅が小さくなりながら元に戻るカーブを得ることができます。
減衰振動関数は例えば以下が考えられます。
```
y = exp(-t * a) * cos(t * b) 
```
![blog_2022_programmer_tech19_06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565926/1005f272-85c4-568f-7f90-7786a03523d6.jpeg)
草の揺れ幅にこの曲線の値を利用することで, 踏まれた後に揺れながら元にもどる表現が実現できるというものです。
![blog_2022_programmer_tech19_07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565926/dd89c530-c332-9705-522a-3c35f13d9dbc.jpeg)


# 弊社タイトルでの利用例
弊社のタイトルである[Warlander](https://playwarlander.com/ "Warlander")では背の低い植物のほとんどがこの仕組みでインタラクティブに揺れるようになっています。

触れた草が揺れながら元に戻る様子や、ジャンプ中のキャラクターが不自然に草を揺らさないことがわかると思います。




<p><iframe width="560" height="315" src="https://www.youtube.com/embed/J44ZKvBTr7o?si=L7Wf7V4hBppWXc0-" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe></p>




# 最後に
今回は**Dual Heightfield Trail**による草の揺れの仕組みと弊社タイトルでの利用例の紹介でした。
実際には下記のような様々な工夫が必要になります。
```
・ 投影テクスチャをプレイヤーに追従させる
・ 「草むら」として1モデルになっている場合に、草パーツを個別に揺らすための特殊な情報をモデルにベイク
```
もし次の機会があればご紹介したいと思います。