Appearance
第12回 頂点アニメーション
前回: 第11回 PBRマテリアル
今回の授業内容
動的な変形を加えたグリッドメッシュ
- 頂点シェーダーとフラグメントシェーダーの役割を区別する
- 頂点位置を時間で変化させ、メッシュを動かす
- UVまたは頂点カラーで動かす範囲を制御する
- Object SpaceとWorld Spaceを目的に応じて使い分ける
- 風で揺れる旗または草のマテリアルを制作する
頂点シェーダーによる変形
頂点シェーダーとフラグメントシェーダー
第1回で、シェーダーには頂点シェーダーとフラグメントシェーダーがあることに触れました
ポリゴンの描画は大きく分けて2段階で行われます
頂点を画面上のピクセルへ変換する処理を担当するのが頂点シェーダー、ピクセルごとの情報を計算する処理を担当するのがフラグメントシェーダーです
この2段階の処理で、三角形の集合体であるポリゴンモデルが画面上に描画されます
本来のシェーダープログラミングでは頂点処理とフラグメント処理は明確に分かれていますが、Shader Graph は頂点シェーダーとフラグメントシェーダーの違いを意識せずに作業できるよう設計されています
この授業でも両者の使い分けを扱ってきませんでした
実はここまでの授業で扱っていたのは、スキンメッシュを除いて主にフラグメントシェーダーです
今回は頂点シェーダーを扱いた頂点操作で、動的にモデルを変形させる方法を学びます
頂点シェーダーの主な役割は、メッシュを構成する頂点の位置、法線、接線を計算して確定することです
この計算中に三角関数で作った波やノイズで作った揺れを頂点位置に干渉させれば、メッシュの形を動的に変化させることができます
旗、草、波、呼吸、膨張、振動など、規則的な動きの表現に適しています
変形に必要な頂点数
分割の例
ただし細かくするほど処理が重くなるため、適切な分割数を選ぶ必要がある
頂点シェーダーが操作できるのは、メッシュに存在する頂点だけです
4頂点しかない平面では、中央部分を曲げられません
縦横に分割された平面なら、各頂点を少しずつ異なる位置へ動かせます
頂点数を増やせば結果も滑らかになりますが、それだけ処理とメモリの負荷も増えます
必要な品質を満たす分割数を選択する必要があります
INFO
動的にポリゴンを分割して頂点数を増やすテッセレーションという手法もあります
テッセレーションを利用できれば、元のメッシュを変更せずに頂点密度を高め、より滑らかな変形を表現できます
ただ、今回使用しているURPのShader Graphでは利用できません
今回はメッシュを細分化せずにそのまま使用します
PositionブロックへObject Spaceの座標を渡す
元の頂点座標に変位を加えるのが頂点アニメーションの基本です
Position (Object)
元の頂点位置を渡す
Vertex Position
Offset (Object)
変位を加える
Vertex Position
URP Lit Shader GraphのVertex ContextにあるPositionブロックは、Object Spaceの頂点位置を受け取ります
World SpaceやView Spaceの座標ではありませんので注意してください
周期的な揺れを作る
Sineで往復運動を作る
Sineは、サインカーブを計算するノードです
入力に応じて-1から1の間を繰り返します
このSineにTimeを入力すると、時間とともに正負へ往復する値になります
| プロパティ | 役割 |
|---|---|
Speed | 揺れが繰り返す速さ |
Amplitude | 頂点が移動する最大量 |
Direction | 頂点を動かす方向 |
Time × Speed
周期的な値へ変換する
Sine
Sine
揺れ幅を調整する
Sine × Amplitude
Sine × Amplitude
移動方向を与える
× Direction
× Direction
頂点へ加える変位にする
Offset
このままでは全頂点が同じ量だけ動くため、メッシュ全体が平行移動してしまいます
「曲がる」動きにするには、場所ごとに変位量を変えます
UVで根元を固定する
例えば旗や草を揺らす際は、取り付け部分や根元は固定し、先端にいくほど大きく動かすと自然な表現になります
UVが根元で0、先端で1になるメッシュなら、その値を変位のマスクとして利用できます
text
揺れ × UV.y
根元 UV.y = 0 -> 動かない
中央 UV.y = 0.5 -> 半分動く
先端 UV.y = 1 -> 最大まで動く例えば横方向を固定したい場合は、UVの縦成分を使います
あるいは逆に、縦方向を固定したい場合は、UVの横成分を使います
使う値はUV以外でも構いません
地面から生える草ならば、ObjectのY座標を使って根元を固定するのも悪くない選択です
より細かく調整するのであれば、頂点カラーに固定度を塗ってマスクにしたり、テクスチャーを使って揺れ具合を塗り分けることもできます
商用ゲームの樹木などでは、根元は固定、枝は少し揺れ、葉は大きく揺れるように頂点カラーで塗り分けられていることがあります
場所ごとに位相をずらす
全頂点が同じタイミングで動くと、板のように見えます
頂点位置をSineの入力へ加えると、場所ごとに波のタイミングをずらせます
Frequencyを上げると、短い間隔で波が繰り返されます
高すぎる値は細かく震えて見え、メッシュの分割数が足りない場合は角ばります
Time × Speed
時間による位相を渡す
Sineの入力
Position.x × Frequency
場所による位相を加える
Sineの入力
Sineの入力
周期的な値へ変換する
Sine
Sine
揺れ幅を調整する
× Amplitude
× Amplitude
根元の変位を抑える
× RootMask
揺れの方向の制御
Object SpaceとWorld Space
Object Spaceは、モデル自身の原点と軸を基準にした座標系です
World Spaceは、シーン全体の原点と軸を基準にした座標系です
風の方向や揺れの位相はWorld Spaceで指定できた方が扱いやすいですが、Shader GraphはPositionブロックにObject Spaceの座標を要求します
このため、Shader Graph内の計算はObject Spaceで行うか、World Spaceで計算した結果をObject Spaceに変換する必要があります
INFO
どちらでも結果は変わりませんが、Object Space→World Space→Object Spaceの変換は計算量が増えてしまいます
可能であればObject Spaceで計算する方が効率的です
Wind Direction (World)
World to ObjectのDirection変換
Transformノード
Transformノード
Object Spaceの方向を得る
Offset Direction (Object)
Offset Direction (Object)
頂点位置へ加算する
Position (Object)
方向を変換するためにはTransformノードのTypeはDirectionを選びますPositionとして変換すると平行移動の成分まで含まれて意図しない値になってしまうので注意してください
ワールド位置で風の位相を作る
草原全体へ風が伝わる表現では、各オブジェクトのWorld PositionをSineの位相へ使います
オブジェクトごとに同じローカル座標を持っていても、ワールド上の場所が違えば異なるタイミングで揺れます
dot(PositionWS.xz, WaveDirection.xz) × Frequency
場所による位相を渡す
Sineの入力
Time × Speed
時間による位相を加える
Sineの入力
Sineの入力
風の波を作る
Sine
この処理により、風の波が地面を移動するように見せられます
頂点の移動方向と、波が伝わる方向は別々のプロパティとして持たせると調整しやすくなります
応用:ノイズで規則性を崩す
Sineだけでは動きが規則的になってしまいます
第6回で扱ったNoiseを弱く混ぜると、風の強さやタイミングに変化を加えられます
ノイズを加える際は、UV座標とTimeを使って得た乱数をSineの位相へ加える方法が簡単です
Noiseを強くしすぎると細かい振動になり、草や布の重さを感じにくくなります
まずSineで大まかな動きを作り、弱いNoiseを加えて調整するのがおすすめです
法線方向に変形させる
先の例では風の方向を基準に頂点を動かしました
草がたなびくような表現には好適な方法です
続いてメッシュの法線方向を基準に動かすことで波の表現を実装します
変形後の描画を確認する
法線とライティング
法線を更新しない場合
変形後の法線を計算した場合
頂点位置を動かしても法線は変化しません
メッシュ形状を変形させても法線が元の平面のままではライティングが不自然になります
変形後の法線は、変形後の面に沿う2つの方向から求めます
法線と求める座標から見て縦と横にずらした位置の変化を使い、外積を取ることで近似できます
外積は第7回で扱っています
Deformed Tangent
変形面の横方向を渡す
Cross Product
Deformed Bitangent
変形面の縦方向を渡す
Cross Product
Cross Product
外積を単位ベクトルにする
Normalize
Normalize
変形後の法線として使う
Normal
たとえば横方向へ波を作る場合は、横方向へ少し進んだ変位の変化を使って、変形後の横方向を近似できます
縦方向も同じように求め、2つの方向の外積を取ると、波で傾いた面に近い法線になります
text
元の位置 + 変位 = 変形後の位置
変形後の位置の横方向の変化 -> Deformed Tangent
変形後の位置の縦方向の変化 -> Deformed Bitangent
Cross Productで変形後の法線を作る近似の細かさは実装によります
小さな揺れでは元の法線との差が目立ちにくい場合もありますが、たなびく旗や波打つ水面のように面の向きが大きく変わる表現では法線の計算を避けることはできません
また、メッシュの分割と粒度がそろっていない場合は変形後の法線が不自然に見えることがあります
Boundsとカリング
UnityはメッシュのBoundsを使い、カメラに映る可能性があるか判定します
頂点シェーダーで元のBoundsより外へ大きく動かすと、画面内にあるはずのメッシュが描画されないことがあります
問題が起きた場合は、次を確認します
- 変位量が必要以上に大きくないか
- 元メッシュのBoundsが変形後の範囲を含むか
- 大量のオブジェクトでBoundsを広げすぎていないか
表示されないからといってBoundsを無制限に広げるのも問題です
実際に表示の必要がないのにカメラの描画範囲を超えて描画されることになり、パフォーマンスに悪影響を与えます
実習:風による頂点アニメーションを作る
実習1:草を風向きへ揺らす
- 根元から先端へUVが
0から1になる草メッシュを用意する - UVを使って根元を固定する
Wind DirectionをWorld Spaceのプロパティとして用意するTransformノードでWorld SpaceからObject Spaceの方向へ変換する- 変換した方向へ頂点を動かす
- 複数の草を回転させて配置し、同じ風向きへ揺れることを確認する
実習2:旗を揺らす
シェーダーの例
- 縦横に分割されたPlaneを用意する
URP Lit Shader Graphを作成する- Vertex Contextへ
Positionブロックを追加する Time、Multiply、Sineで往復する値を作る- UVの横方向をマスクにし、固定側を
0にする - 揺れをObject Spaceの一定方向へ加える
Speed、Amplitude、Frequencyをプロパティにする
GameObjectを回転し、揺れの方向がモデルと一緒に回転することを確認します
実習3:風の波を移動させる
PositionノードをWorld Spaceに設定する- XZ成分と
Wave DirectionのDot Productを計算する Frequencyを掛け、Time × Speedへ加える- 結果を
Sineへ入力する - 複数の草の間を波が移動して見えるか確認する
- 必要に応じて低周波のNoiseを加える
授業内課題
旗または草を題材に、根元を固定した頂点アニメーションを作成してください
小さな揺れで仕組みを確認する場合は、元の法線との差が目立たない範囲に変位を抑えます
大きく曲げる旗や布として見せる場合は、変形後の法線計算も完成条件に含めます
完成条件
- Vertex Contextの
Positionを変更している - 元の頂点位置へ変位を加えている
TimeとSineを使って往復運動を作っている- UVまたは頂点カラーで固定部分と可動部分を分けている
Speed、Amplitude、Frequencyをマテリアルから調整できる- Object SpaceまたはWorld Spaceを選んだ理由を説明できる
- 大きく変形するLit表現では、変形後の法線を計算して
Normalへ反映している - 静止状態と動作状態を比較できる動画を提出する
発展条件
基本課題が完成した場合は、次のいずれかを追加します
- World Spaceで複数オブジェクトの風向きを統一する
- World Positionを使って風の波を伝える
- 頂点カラーで揺れやすさを塗り分ける
- Noiseで風の強弱を変化させる
- 変形後の接線方向と従法線方向から、より安定した法線を近似する
- C#または
MaterialPropertyBlockから風向きと強さを変更する
よくある問題
| 問題 | 確認する場所 |
|---|---|
| 全頂点が原点へ集まる | 元のPositionへOffsetを加えているか |
| メッシュ全体が平行移動する | UVまたは頂点カラーのマスク |
| 曲がらず板のまま動く | メッシュの分割数、位相に頂点位置を使っているか |
| 固定したい側が動く | UVの向き、One Minusの要否 |
| 回転すると風向きが変わる | World方向をObject方向へ変換しているか |
| 一部の角度で突然消える | Boundsと最大変位量 |
| 光の当たり方が不自然 | 変位量、法線、非一様スケール |
今回のまとめ
- Vertex ContextのPositionを変更すると、GPU上でメッシュを変形できます
- SineとTimeを使うと、繰り返す往復運動を作れます
- UVや頂点カラーは、動かす範囲を制御するマスクとして利用できます
- 頂点位置を位相へ加えると、メッシュに沿って波が伝わります
- Object Spaceはモデルに固定された動き、World Spaceはシーンで共有する風に適しています
- 頂点密度、変形後の法線、Boundsも変形品質と描画結果に影響します
発展資料
座標空間と行列を詳しく確認する場合は、次の資料を参照してください