【UnityShader】頂点の位置を色で表す #22
前回の成果
今回やること
下記の記事で色がうまく取得できなかったので修正していきます。
Vertex Surfaceのソースコード
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" }
こちらは以前と変更しません。
問題のソースコード(Vertex Fragment)
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 ShaderとFragment Shader
右がVertex ShaderとSurface Shaderです。
右が正しい動きです。
問題点
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; }
なぜ問題かというと、Clip座標系に変換してからampの差分を足しているからです。
Suface Shaderの内部
Surface ShaderにVertex Shaderをフックした場合、Clip座標系に明示的に変換していません。
なので、どちらが先に呼ばれるか中身を見て確認していこうと思います。
Surface Shaderの中身はこちらのサイト様に乗っています。
ベースパスの見出しのコードのvert_surfに注目してください。
v2f_surf vert_surf(appdata_full v)
{
v2f_surf o;
UNITY_INITIALIZE_OUTPUT(v2f_surf, o);
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
// 省略
71行目にClip座標に変換している処理がありました。
しかし、フックしたVertex Shaderはどこで呼ばれるのかが記載していません。
フックしたVertex Shaderの呼び先
Unityの公式に記載してありました。
vertex:VertexFunction - Custom vertex modification function. This function is invoked at start of generated vertex shader , and can modify or compute per-vertex data. See Surface Shader Examples.
とあります。
これは、フックした頂点シェーダーは頂点シェーダーの開始時に呼び出されるという意味です。
具体的にどこで呼び出されているのかは不明でしたが、これで順番が
- フックしたVertex Shaderが呼ばれる(ampの差分を足す)
- Clip座標系に変換される
ということがわかりました。
このことから先ほど問題点であげたことの意味が理解できたと思います。
修正後のソースコード
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.uv = TRANSFORM_TEX(v.texcoord, _MainTex); 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); o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag(v2f i) :Color { fixed4 c = tex2D(_MainTex, i.uv); return c; } ENDCG } } }
ampの計算をしてからClip座標系に変換するように変更しました。
挙動
両方同じ挙動になりました。
色が違う理由はSurface Shaderで自動でライティングの設定を行なっているからです。
Directional Light:ON
Directional Light:OFF
頂点座標で色を変更する
色の出力にも失敗していたので確認します。
Vertex Surfaceのソースコード
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; float3 rgb; }; // vert関数 void vert(inout appdata_base v, out Input IN) { // 波の揺れを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); UNITY_INITIALIZE_OUTPUT(Input, IN); IN.rgb = float3(v.vertex.x / 10, v.vertex.y / 10, v.vertex.z / 10); IN.rgb.x = clamp(IN.rgb.x, 0, 1); IN.rgb.y = clamp(IN.rgb.y, 0, 1); IN.rgb.z = clamp(IN.rgb.z, 0, 1); } // surf関数 void surf(Input IN, inout SurfaceOutput o) { // テクスチャのピクセルの色を返す fixed4 c = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = c.xyz; // 色出力用 o.Albedo = IN.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
Vertex Fragmentのソースコード
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; float3 rgb : NORMAL; }; v2f vert(appdata_base v) { v2f o; o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); 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); o.rgb = float3(v.vertex.x / 10, v.vertex.y / 10, v.vertex.z / 10); o.rgb.x = clamp(o.rgb.x, 0, 1); o.rgb.y = clamp(o.rgb.y, 0, 1); o.rgb.z = clamp(o.rgb.z, 0, 1); o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag(v2f i) :Color { fixed4 c = tex2D(_MainTex, i.uv); // 色出力用 c.xyz = i.rgb.xyz; return c; } ENDCG } } }
各構造体にrgb値を受け取れる変数を用意しています。
結果
色が同じになりました。
今回はこれで終わりです。
ご視聴ありがとうございました!