グリッドシェーダ
グリッド状の線を表示するシェーダです。
shaderの作成
頂点座標を用いてグリッドの表示を行います。vertex shaderから頂点座標をそのままfragment shaderへ渡します(o.pos)。
1 2 3 4 5 6 7 8 |
v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.pos = v.vertex; return o; } |
vertex shaderから渡された頂点座標を用いて、グリッドの描画を行います。ここでは、x方向のみを扱います。
1 2 3 4 5 6 7 |
fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv) * _Color; float f = smoothstep(0.5, _Edge + 0.5, i.pos.x); col = fixed4(f, f, f, 1); return col; } |
これを実行すると
となります。_Edgeの数値を大きくすると
このように、境界がぼやけます。つぎに、グリッドの大きさを調整できるように処理を変更します。frac(i.pos.x)とすることでsmoothstepの入力値が0~0.999・・・を繰り返すようになります。さらに、_GridSizeをi.pos.xにかけることにより、グリッドの大きさを任意に変更できるようになります。
1 |
float f = smoothstep(0.5, _Edge + 0.5, frac(i.pos.x * _GridSize)); |
あとはグリッド線の太さですが、0.5としている部分が太さを決める部分となります。よって、
1 |
float f = smoothstep(_LineWidth, _Edge + _LineWidth, frac(i.pos.x * _GridSize)); |
とすると、任意に線の太さを変更できるようになります。
上画像を見てわかるように、線の片側しか表示できていません。そこで反対側も表示できるように変更します。
1 2 |
float f = smoothstep(_LineWidth, _Edge + _LineWidth, frac(i.pos.x * _GridSize)) + smoothstep(1 - _LineWidth, 1 - _Edge - _LineWidth, frac(i.pos.x * _GridSize)); |
これを実行すると
となり、反対側も表示されるようになりました。さらに、横線を表示できるように変更します。
1 2 3 4 |
float2 f = smoothstep(_LineWidth, _Edge + _LineWidth, frac(i.pos.xz * _GridSize)) + smoothstep(1 - _LineWidth, 1 - _Edge - _LineWidth, frac(i.pos.xz * _GridSize)); float grid = max(f.x, f.y); col = fixed4(grid, grid, grid, 1); |
平面ではこれで十分ですが、立体的なオブジェクトで使用できるよう上記コードを以下のように変更します。
1 2 3 |
float3 f = smoothstep(_LineWidth, _Edge + _LineWidth, frac(i.pos.xyz * _GridSize)) + smoothstep(1 - _LineWidth, 1 - _Edge - _LineWidth, frac(i.pos.xyz * _GridSize)); float grid = max(max(f.x, f.y), f.z); |
立体にグリッドを表示することができました。しかし、一つ問題があります。
問題点と解決方法
GridSizeを6へ変更すると
このように真っ白になってしまいます。この現象は、線がエッジの部分に重なった場合なるようです。xのみに変更すると
となります。この画像より、i.pos.xが同じ頂点で囲まれている面が白くなっていることが分かります。そこで、fwidthを使用してマスキングを行います。fwidthは入力値がどれだけ変化しているかを返してくれます。よって、
1 |
float fw = step(0.001, fwidth(i.pos.x)); |
とすることで、fwへi.pos.xの変化がほぼない場合は0を、それ以外は1を代入することができます。fw を出力してやると
となり、i.pos.xに変化がない場所は黒(fw =0)、他の部分は白(fw =1)となっていることがわかります。これをグリッド線の式に乗算してやれば、問題が解決するはずです。
1 2 3 4 |
float2 f = smoothstep(_LineWidth, _Edge + _LineWidth, frac(i.pos.x * _GridSize)) + smoothstep(1 - _LineWidth, 1 - _Edge - _LineWidth, frac(i.pos.x * _GridSize)); float grid = max(f.x, f.y); grid *= step(0.001, fwidth(i.pos.x)); |
これを実行すると
となり、問題が解決しました。このコードを3次元にしてやると
1 2 3 4 |
float3 f = smoothstep(_LineWidth, _Edge + _LineWidth, frac(i.pos.xyz * _GridSize)) + smoothstep(1 - _LineWidth, 1 - _Edge - _LineWidth, frac(i.pos.xyz * _GridSize)); f *= step(0.001, fwidth(i.pos.xyz)); float grid = max(max(f.x, f.y), f.z); |
となり、正常にグリッド線が表示されるようになりました。
shader
テクスチャ、グリッド線の色及び基本色を変更できるようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
Shader "Unlit/Grid" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("MainColor", Color) = (0, 0, 0, 1) _GridColor ("GridColor", Color) = (1, 1, 1, 1) _GridSize ("GridSize", Range(0, 20)) = 1 _LineWidth ("LineWidth", Range(0.4, 1)) = 0.1 _Edge ("Edge", Range(0.001, 0.8)) = 0.1 } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; float4 pos : TEXCOORD1; }; sampler2D _MainTex; float4 _MainTex_ST; float4 _GridColor; float4 _Color; float _GridSize; float _LineWidth; float _Edge; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.pos = v.vertex; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv) * _Color; float3 f = smoothstep(_LineWidth, _Edge + _LineWidth, frac(i.pos.xyz * _GridSize)) + smoothstep(1 - _LineWidth, 1 - _Edge - _LineWidth, frac(i.pos.xyz * _GridSize)); f *= step(0.001, fwidth(i.pos.xyz)); //float grid = saturate(f.x + f.y + f.z); float grid = max(max(f.x, f.y), f.z); col.rgb = col.rgb * (1 - grid) + grid * _GridColor.rgb; col = fixed4(grid, grid, grid, 1); return col; } ENDCG } } } |
実行結果
上記shaderの実行結果は以下の通りです。
-
前の記事
法線マップをオブジェクト空間へ変換しシェーディング 2018.11.14
-
次の記事
水中の屈折(shader) 2018.12.08
コメントを書く