マグマが広がるような表現を作る方法を解説します。
サンプル
ざっと解説
座標(X, Y, Z)のXZ成分を取り出し、座標(0, 0)からの距離をとります。
この距離を使って波を作っています。
シェーダーグラフを作る
STEP0 : 板オブジェクトとUnlitシェーダーグラフの準備
Unity標準のPlaneオブジェクトを作り、これにUnlitシェーダーグラフから作ったマテリアルを適用しておきます。
STEP1 : Distanceノードを使って原点(0,0)からの距離をとる
以下のようにノードをつなぎます。
原点(0,0)からの距離が板の色に反映されるようになりました。
STEP2: 距離にSineノードを適用する
距離をSineノードにつなぎます。
波紋っぽい色になりました。
STEP3: Sineの範囲を(-1,1)から(0,1)に変換する
Sineノードの出力は(-1,1)の範囲になっているため、Remapノードを使って範囲(-1, 1)を(0, 1)へ変換します。
後に頂点座標へ加算する時、負の値が含まれていると板が下へ凹んでしまうためこのような補正を入れています。
STEP4: 波紋を時間で動かす
SubtractノードとTimeノードを使い、距離から時間を減算して波紋が広がるようにします。
STEP5: 波紋を使って頂点を動かす
波紋を頂点座標のY成分へ加算して頂点を動かします。
板がY方向に動くようになりました。
STEP6: smoothstepで波紋をとがらせる
smoothstepノードを使って波紋をとがらせます。
補足 : smoothstepの挙動について
smoothstepが返す値
smoothstepノードは以下のような値を返します。
- In<Edge1の場合は0.0を返す
- In>Edge2の場合は1.0を返す
- Edge1 < In < Edge2の場合は0.0~1.0の間を滑らかに補間するような値を返す
グラフの描画にはiq氏のGraphToyを使わせていただきました。(http://www.iquilezles.org/apps/graphtoy/)
smoothstepをsinに対して使った場合
smoothstepをsin(x)に対して使用することで、sinカーブをとがらせることができます。
今回はsin(x)が(0,1)の範囲に収まるように補正しているため、実際は以下のようなグラフを描きます。
STEP7: 波の高さを調整する
Multiplyノードを使って波の高さを1.4倍にします。
なんだか良い感じの形の波になってきました。
STEP8: SampleGradientノードで色をつける
波紋(Vector1型の数値)をSampleGradientでカラーグラデーションに変換して色を付けます。
マグマっぽくなりました。
STEP9 : Smoothstepノードで色を絞る
波が立っていないところにも色がついてしまっていて残念な印象なので、SmoothStepノードでくっきりとさせたいと思います。
以下のようにSmoothstepノードをつなぎます。
色がくっきりとしました。
変化が分かりにくいので、変更前と変更後を並べてみました。
Step10 : ノイズで質感を加える
原点(0,0)からの距離を利用して波紋を作っていましたが、この距離にノイズを加えてみます。
今回はNoiseScaleに45.0を指定してみました。
Step11 : 板オブジェクトを差し替える(完成)
25x25の頂点からなるサイズ10の板オブジェクトに差し替えると以下のような見た目になります。
using UnityEngine; /// <summary> /// 板状のメッシュを作成C#スクリプト /// </summary> [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] public class GeneratePlane : MonoBehaviour { [SerializeField] bool generateMeshOnUpdate = false; // 毎フレームメッシュ更新 [SerializeField] int xCount = 25; // x方向の頂点数 [SerializeField] int zCount = 25; // y方向の頂点数 [SerializeField] float planeSize = 10f; // 板のサイズ Mesh mesh; void Start() { mesh = new Mesh(); GetComponent<MeshFilter>().mesh = mesh; Create(); } private void Update() { if (generateMeshOnUpdate) { Create(); } } private void Create() { if (xCount < 2) xCount = 2; if (zCount < 2) zCount = 2; // 頂点座標(vertices)と法線(normals)作成 Vector3[] vertices = new Vector3[xCount * zCount]; Vector3[] normals = new Vector3[vertices.Length]; int vi = 0; for (int z = 0; z < zCount; z++) { for (int x = 0; x < xCount; x++) { vertices[vi++] = new Vector3( (float)x / (xCount - 1) - 0.5f, 0f, (float)z / (zCount - 1) - 0.5f ) * planeSize; } } for (int i = 0; i < vertices.Length; i++) { normals[i] = new Vector3(0f, 1f, 0f); } // 頂点インデックス(triangles)作成 int[] triangles = new int[(xCount - 1) * (zCount - 1) * 6]; int ti = 0; int triangleOffset = 0; for (int z = 0; z < zCount - 1; z++) { for (int x = 0; x < xCount - 1; x++) { triangles[ti++] = triangleOffset + xCount; // 2 triangles[ti++] = triangleOffset + 1; // 1 triangles[ti++] = triangleOffset + 0; // 0 triangles[ti++] = triangleOffset + xCount + 1; // 3 triangles[ti++] = triangleOffset + 1; // 1 triangles[ti++] = triangleOffset + xCount; // 2 triangleOffset++; } triangleOffset++; } // メッシュ更新 mesh.triangles = null; mesh.vertices = vertices; mesh.normals = normals; mesh.triangles = triangles; } }