Terrainの青いカーソルやシューティングゲームの着弾地点など、後からオブジェクトに幾何図形を描きたい時に使えるシェーダを紹介します。
今回の記事の内容は次のようになります。
下準備をする
まずはUnityのプロジェクトを作り、シーンに平面と立方体を追加します。ヒエラルキービューから「create」→「3D Object」→「Plane」を選択します。
今配置したPlaneの座標が(0, 0, 0)になっていることをインスペクタで確認してください。
続いてシェーダとシェーダをアタッチするマテリアルを作成します。
プロジェクトビューで「右クリック」→「Create」→「Shader」→「Standard Surface Shader」を選択し、作成したファイル名をCircleDrawに変更します。続いてこのシェーダファイルをアタッチするマテリアルも作成しましょう。CircleDrawのシェーダをを選択した状態で「右クリック」→「Create」→「Material」を選択すると、CircleDrawというマテリアルが作成されます。
いま作成したマテリアルをシーンビューのPlaneにドラッグ&ドロップしてアタッチします。PlaneのシェーダがCustom/CircleDrawになっていることをインスペクタで確認しましょう。
円を書いてみる
まずは基本となる円を描いてみましょう。次のシェーダを入力してください。
Shader "Custom/sample" { SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard #pragma target 3.0 struct Input { float3 worldPos; }; void surf (Input IN, inout SurfaceOutputStandard o) { float dist = distance( fixed3(0,0,0), IN.worldPos ); float radius = 2; if( radius < dist ){ o.Albedo = fixed4(110/255.0, 87/255.0, 139/255.0, 1); } else { o.Albedo = fixed4(1,1,1,1); } } ENDCG } FallBack "Diffuse" }
このシェーダではワールド座標を利用して円を描きます。そのため、サーフェイスシェーダの入力としてワールド座標を受け取れるように、Input構造体には worldPosを宣言しています。
サーフェイスシェーダの中では、現在処理しているワールド座標と原点の距離がradius(=2.0)以内なら白色、radiusより大きければ紫色を指定しています。ここでは、Unityが用意してくれているdistanceメソッド(2点間の距離を求めるメソッドです)を使って原点までの距離を求めています。
実行結果は次のようになります。
リングを描いてみる
次に、先ほどのシェーダを円の外周だけを描くように修正してみましょう。
Shader "Custom/sample" { SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard #pragma target 3.0 struct Input { float3 worldPos; }; void surf (Input IN, inout SurfaceOutputStandard o) { float dist = distance( fixed3(0,0,0), IN.worldPos ); float radius = 2; if( radius < dist && dist < radius + 0.2){ o.Albedo = fixed4(1,1,1,1); } else { o.Albedo = fixed4(110/255.0, 87/255.0, 139/255.0, 1); } } ENDCG } FallBack "Diffuse" }
今回のシェーダでは、現在処理しているワールド座標と原点の距離がradius以上、radius+0.2以下の場合に白色、それ以外は紫色を指定しています。これにより、中心から一定距離の部分のみが白色になるので、リング状の図形を描くことができます。
実行結果は次のようになります。
リングをいっぱい描く
上では一つだけリングを描きました。今度は複数のリングを描いてみましょう。
Shader "Custom/sample" { SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard #pragma target 3.0 struct Input { float3 worldPos; }; void surf (Input IN, inout SurfaceOutputStandard o) { float dist = distance( fixed3(0,0,0), IN.worldPos); float val = abs(sin(dist*3.0)); if( val > 0.98 ){ o.Albedo = fixed4(1, 1, 1, 1); } else { o.Albedo = fixed4(110/255.0, 87/255.0, 139/255.0, 1); } } ENDCG } FallBack "Diffuse" }
前回とは少し違った方法で複数のリングを書いてみます。円の中心からの距離を横軸にとり、sin波を書くと左図のようになります。このsin波の絶対値をとると右図のようになります。
今回のシェーダでは、sin波の山の頂点付近のみを白色、そ例外の部分を紫色に指定しています。これにより、一定間隔で複数のリングを描くことができます。
実行結果は次のとおりです。
リングを動かす
最後に複数のリングを外側に向かって動かすアニメーションを作ってみます。
Shader "Custom/sample" { SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard #pragma target 3.0 struct Input { float3 worldPos; }; void surf (Input IN, inout SurfaceOutputStandard o) { float dist = distance( fixed3(0,0,0), IN.worldPos); float val = abs(sin(dist*3.0-_Time*100)); if( val > 0.98 ){ o.Albedo = fixed4(1, 1, 1, 1); } else { o.Albedo = fixed4(110/255.0, 87/255.0, 139/255.0, 1); } } ENDCG } FallBack "Diffuse" }
先ほどsin波を使ってリングを描いたので、リングを動かすのはとても簡単です。
外側に向けて波を動かすためには、現在の座標があたかも遠ざかっているように見せる必要があります。現在処理している座標から_Timeを引くことで、あたかも現在処理している座標が、時間とともに外側へ移動しているように見せかけています。
_TimeはUnityのシェーダで定義された値です。詳しくはUnityのホームページに書かれています。
シーンビューに立方体なども追加して、最終的な結果は次のようになりました。
今回は、Unityのシェーダを使って円やリングなどの幾何学図形を描く方法を紹介しました。円だけではなく、四角形などもかけるのですが、それはまた別の記事でかきます〜
参考書
私の書いた「Unity5の教科書」もよろしくお願いします〜m(_ _)m