【UnityShader】風で揺れているような表現 #20
前回の成果
カリングがわかった。
今回やること
旗が風で揺れているような見た目を制作します。
事前準備
SceneにPlaneを用意して、カメラから見ていい感じに斜めになるようにしてください。
ソースコード
Shader "Unlit/shake" { Properties { _MainTex("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert vertex:vert #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void vert(inout appdata_base v) { float amp = 0.5f * sin(_Time * 100 + v.vertex.x * 100); v.vertex.xyz = float3(v.vertex.x, v.vertex.y + amp, v.vertex.z); } void surf(Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
揺らすための考え方
Planeのオブジェクトを横からみると頂点が一直線にならんでいます。
この頂点を上下に移動させることによって、Planeの形を波のようにさせます。
それぞれの頂点は単に上下移動しているだけですが、隣の頂点とは少しタイミングをずらしています。
これで風で揺れているようにします。
#pragma surface surf Lambert vertex:vert
これで、Vertex Shaderをフックすることができます。
フック (hook)とは
プログラムの中に独自の処理を割りこませるために用意されている仕組み。
もしくは
プログラムにおいて、本来の処理を横取りして独自の処理を割りこませること
です。
これにより、Surface ShaderでVertex Shaderを使用することができます。
今回はライティングを特に使用しないため、Lambertにしています。
ライティングに関してはこちらを参照してください。
void vert(inout appdata_base v)
これがVertex Shaderです。
今回はvertex:POSITIONしか使用しないので、appdata_baseを使用しています。
appdata一覧はこちら
ちなみに、Input構造体のデータを受け取りたい場合は、
void vert(inout appdata_full v, out Input o)
代わりにこちらを定義すれば、使用することができます。
その際に
UNITY_INITIALIZE_OUTPUT(Input, o);
を忘れないようにしてください。
UNITY_INITIALIZE_OUTPUTはInputを初期化する関数です。
float amp = 0.5f * sin(_Time * 100 + v.vertex.x * 100);
この計算式がPlaneを揺らしている本体となります。
sin関数を使用することによって、-1~1の値を往復させて、揺れを表現しています。
そして、隣の頂点とは少しズラすために頂点のx座標を足しています。
結果
テクスチャは適当なものを貼り付けています。
ソースコードにコメントを付与
Shader "Unlit/shake" { // プロパティ Properties { // テクスチャ _MainTex("Texture", 2D) = "white" {} } // Shaderの中身を記述 SubShader { // 一般的なShaderを使用 Tags { "RenderType" = "Opaque" } // しきい値 LOD 200 // cg言語記述 CGPROGRAM // vertex shaderをフックする #pragma surface surf Lambert vertex:vert // Shader Model #pragma target 3.0 // テクスチャ sampler2D _MainTex; // Input構造体 struct Input { float2 uv_MainTex; }; // vert関数 void vert(inout appdata_base v) { // 波の揺れをsin関数を用いて表現 float amp = 0.5f * sin(_Time * 100 + v.vertex.x * 100); v.vertex.xyz = float3(v.vertex.x, v.vertex.y + amp, v.vertex.z); } // surf関数 void surf(Input IN, inout SurfaceOutput o) { // テクスチャのピクセルの色を返す fixed4 c = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
今回はこれで終わりです。
ここまでご視聴ありがとうございました!
+α
せっかくなので、Vertex Shaderと Flagment Shaderでも書いてみた。
Shader "Unlit/shakeVert" { Properties{ _MainTex("Texture", 2D) = "white" {} } SubShader{ Tags{ "Queue" = "Geometry" "RenderType" = "Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; float4 _MainTex_ST; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert(appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); float amp = 0.5f * sin(_Time * 100 + v.vertex.x * 100); o.vertex.xyz = float3(o.vertex.x, o.vertex.y + amp, o.vertex.z); return o; } fixed4 frag(v2f i) :SV_Target { fixed4 c = tex2D(_MainTex, i.uv); return c; } ENDCG } } }
結果
左がvertex to fragmentで右がvertex to surfaceです。
渡している値は同じなのに右のオブジェクトの方が激しく揺れています。
また、明度も左右によって違ってきます。
なぜ変わるのか
自分なりに解決してみた。
違うところがあればご指摘してください。
明度
surfaceの方は
#pragma surface surf Lambert vertex:vert
と明示的に拡散を示しているのに対し、fragmentの方は暗示的に行なっているので変わっているのではないか。
揺れの大きさ
試しに両者のappdata_baseを色として出力してみると、
となったので、そもそも取得してきている値が異なっているのではないかと思う。
右側がなぜ4色になるのかは不明