読者です 読者をやめる 読者になる 読者になる

おもちゃラボ

Unityで遊びを作っていきます

【Unityシェーダ入門】円やリングをかっこよく動かす方法

Terrainの青いカーソルやシューティングゲームの着弾地点など、後からオブジェクトに幾何図形を描きたい時に使えるシェーダを紹介します。

f:id:nn_hokuson:20161114194524p:plain

今回の記事の内容は次のようになります。

下準備をする

まずはUnityのプロジェクトを作り、シーンに平面と立方体を追加します。ヒエラルキービューから「create」→「3D Object」→「Plane」を選択します。
今配置したPlaneの座標が(0, 0, 0)になっていることをインスペクタで確認してください。

f:id:nn_hokuson:20161114195003p:plain

続いてシェーダとシェーダをアタッチするマテリアルを作成します。

プロジェクトビューで「右クリック」→「Create」→「Shader」→「Standard Surface Shader」を選択し、作成したファイル名をCircleDrawに変更します。続いてこのシェーダファイルをアタッチするマテリアルも作成しましょう。CircleDrawのシェーダをを選択した状態で「右クリック」→「Create」→「Material」を選択すると、CircleDrawというマテリアルが作成されます。

f:id:nn_hokuson:20161114195105p:plain

いま作成したマテリアルをシーンビューのPlaneにドラッグ&ドロップしてアタッチします。PlaneのシェーダがCustom/CircleDrawになっていることをインスペクタで確認しましょう。

f:id:nn_hokuson:20161114195308p:plain

円を書いてみる

まずは基本となる円を描いてみましょう。次のシェーダを入力してください。

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点間の距離を求めるメソッドです)を使って原点までの距離を求めています。

f:id:nn_hokuson:20161114200227j:plain

実行結果は次のようになります。

f:id:nn_hokuson:20161114200459p:plain

リングを描いてみる

次に、先ほどのシェーダを円の外周だけを描くように修正してみましょう。

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以下の場合に白色、それ以外は紫色を指定しています。これにより、中心から一定距離の部分のみが白色になるので、リング状の図形を描くことができます。

実行結果は次のようになります。

f:id:nn_hokuson:20161114200738p:plain

リングをいっぱい描く

上では一つだけリングを描きました。今度は複数のリングを描いてみましょう。

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波の絶対値をとると右図のようになります。

f:id:nn_hokuson:20161114201924j:plain

今回のシェーダでは、sin波の山の頂点付近のみを白色、そ例外の部分を紫色に指定しています。これにより、一定間隔で複数のリングを描くことができます。

f:id:nn_hokuson:20161114202438j:plain

実行結果は次のとおりです。

f:id:nn_hokuson:20161114202511p:plain

リングを動かす

最後に複数のリングを外側に向かって動かすアニメーションを作ってみます。

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のホームページに書かれています。

docs.unity3d.com

シーンビューに立方体なども追加して、最終的な結果は次のようになりました。

f:id:nn_hokuson:20161114203305g:plain

今回は、Unityのシェーダを使って円やリングなどの幾何学図形を描く方法を紹介しました。円だけではなく、四角形などもかけるのですが、それはまた別の記事でかきます〜

参考書

DirectX 9 シェーダプログラミングブック

DirectX 9 シェーダプログラミングブック

OpenGL 4.0 シェーディング言語 -実例で覚えるGLSLプログラミング-

OpenGL 4.0 シェーディング言語 -実例で覚えるGLSLプログラミング-

私の書いた「Unity5の教科書」もよろしくお願いします〜m(_ _)m

Unity5の教科書 2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

Unity5の教科書 2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)