おもちゃラボ

Unityで遊びを作ってます

【Unityシェーダ入門】テクスチャをブレンドして自然な地形を表示する

例えば草と土、土とレンガ、など複数のテクスチャをうまい感じにブレンドすることで、自然な見た目の地形表現ができるようになります。Photoshopで作成しても良いのですが、リアルタイムに変化させたい場合や、ステージを見ながら調整したい場合はシェーダを使うと便利です。

f:id:nn_hokuson:20161027070404p:plain

テクスチャをブレンドする方針

テクスチャをブレンドする場合、テクスチャ1とテクスチャ2をどのような割合でブレンドするかの指標となる画像(マスク画像)がもう一枚必要になります。通常このマスク画像は白黒の画像を用意します。

f:id:nn_hokuson:20161027065913p:plain

マスク画像が白に近い場合はテクスチャ1を強めで、マスク画像が黒色に近い場合はテクスチャ2の色を強めで出します。これを数式にすると次のようになります。

color = Texture1 * p + Texture2 * (1-p)

ここでpはマスク画像の明度(0.0〜1.0)です。pが0の場合にはテクスチャ1の画像に、pが1の場合はテクスチャ2の画像になります。

このマスク画像自体はPhotoshopなどで簡単に作成できます。Photoshopを起動し、適当にファイルを作成した上で、メニューバーから「フィルタ」→「描画」→「雲模様」を選択します。「フィルタ」→「ぼかし」→「ガウスフィルタ」で少しぼかした方がブレンドした結果は綺麗になります。

f:id:nn_hokuson:20161027183247p:plain

ステージを作成する

ステージとなるPlaneをシーンビューに追加しましょう。ヒエラルキービューから「Create」→「3D Object」→「Plane」を選択してください。

次に、いま作成したパネルにマテリアルとシェーダを設定します。プロジェクトビューで右クリックから「Create」→「Shader」→「Standard Surface Shader」を選択しblendという名前で保存します。続いてblendシェーダを選択した状態で、右クリックから「Create」→「Material」を選択し、groundという名前で保存します。

最後に作成したgroundのマテリアルをPanelドラッグ&ドロップし、シェーダを「Custom/blend」に変更します。シェーダの詳しい作成方法と設定方法は下の記事で紹介していますので参照して下さい。

nn-hokuson.hatenablog.com

シェーダを作成する

次にマスク画像をもとに2枚のテクスチャをブレンドするシェーダを作成しましょう。

Shader "Custom/sample" {
	Properties {
		_MainTex ("Main Texture", 2D) = "white" {}
		_SubTex ("Sub Texture", 2D) = "white" {}
		_MaskTex ("Mask Texture", 2D) = "white" {}	
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Standard fullforwardshadows
		#pragma target 3.0

		sampler2D _MainTex;
		sampler2D _SubTex;
		sampler2D _MaskTex;

		struct Input {
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutputStandard o) {
			fixed4 c1 = tex2D (_MainTex, IN.uv_MainTex);
			fixed4 c2 = tex2D (_SubTex,  IN.uv_MainTex);
			fixed4 p  = tex2D (_MaskTex, IN.uv_MainTex);
			o.Albedo = lerp(c1, c2, p);			
		}
		ENDCG
	}
	FallBack "Diffuse"
}

このシェーダでは、ブレンドする2枚のテクスチャ、マスク画像をインスペクタから操作できるように、Properiesにこれらの変数を記述しています。

サーフェイスシェーダではテクスチャのuv座標を使うため、Input構造体にuv_MainTexを宣言しています。surfメソッドでは上記の数式を使って2枚のテクスチャをブレンドしています。

テクスチャ1の色をc1に、テクスチャ2の色をc2に代入しています。ブレンドの割合を示すpはマスク画像が黒ければ0.0、白ければ1.0にしたいので、マスク画像をグレースケールに変換した上でpに代入しています。グレースケール変換はこちらの記事で紹介しています。

nn-hokuson.hatenablog.com

最後に2枚のテクスチャをpを使ってブレンドしています。lerpは上記の計算をするためのメソッドです。第一引数と第二引数の影響度をpで指定します。c1とc2はそれぞれr・g・bの要素を持つため、実際には下記のような計算になります。シェーダは、こういう処理が簡単にかけるので助かります。

color.r = c1.r * p + c2.r * (1-p);
color.g = c1.g * p + c2.g * (1-p);
color.b = c1.b * p + c2.b * (1-p);

Propertiesブロックで指定した変数にブレンドしたいテクスチャをセットします。プロジェクトビューからインスペクタにブレンドするテクスチャとマスク画像をドラッグ&ドロップします。

f:id:nn_hokuson:20161027184734p:plain

結果

2枚のテクスチャをブレンドして地形を表示した結果は次のようになります。

f:id:nn_hokuson:20161027070904g:plain

私の書いたUnity5の教科書もお願いします!

【Unity2017対応】Google Cardboardで始めるVR開発入門

記事の内容をUnity2017対応にアップデートしました!(2017年7月末更新)

VRのゲームを開発するとなると、Oculus Riftとか買わなきゃだめだし高くつきそう、というイメージがありました。が!Google CardBoardなる超手軽なVRビューアが販売されているのですね。

google CardBoard自体は800〜1500円ぐらいなので、Unityでアプリを作れば安価でVR開発入門ができるじゃん・・・と気付いたのでやってみました。

この入門記事では、Unityを使って視線の先にキノコをどんどん植えるだけのVRアプリケーションを作ってみます。完成イメージは次のような感じです。

f:id:nn_hokuson:20161025212923p:plain

今回は、「UnityによるVRアプリケーション開発」を参考にして作っていきます。

プロジェクトの作成

まずはUnityのプロジェクトを作成します。プロジェクト名はVRTest、2D/3Dは「3D」を選択します。

f:id:nn_hokuson:20161024232736p:plain

プロジェクトをiOS用、またはAndroid用に変更しておきます。メニューバーから「File」→「Build Settings」を選択し、iOS、またはAndroidを選択し「Switch Platform」をクリックします。

f:id:nn_hokuson:20161024233146p:plain

Unityアセットのインポート

続いて、Unity Asset Storeから今回使用する素材をプロジェクトに追加しておきましょう。メニューバーから「Window」→「Asset Store」を選択し、「Mushroom Land」をダウンロード&インポートして下さい。

f:id:nn_hokuson:20161024233550p:plain

https://www.assetstore.unity3d.com/jp/#!/content/1035

VRエミュレータのインポート

続いて下のサイトからgoogle cardboard用のGoogle VR SDK for Unityをダウンロードします。Unity2017ではUnityエディタにVRの機能が統合されたため、実機で確認するだけであればこのパッケージは不要です。

ただ、このパッケージにはエディタでVRの見栄えが確認できるエミュレータが含まれているためインストールしておくと、UnityエディタでVRの見た目が確認できて便利です。

ダウンロードできたら、gvr-unity-sdk-masterフォルダの中にあるGoogleVRForUnity.unitypackageをダブルクリックしてプロジェクトにインポートします。2017年7月末現在では「GoogleVRForUnity_1.70.0.unitypackage」が最新版のようです。

Downloads and samples  |  Google VR  |  Google Developers

ここまでで、アセットの準備は完了です。

f:id:nn_hokuson:20161025193809p:plain

VR空間を作る

では、地面の配置から始めましょう。プロジェクトビューから「Mushroom Land」→「Enviro」→「Prefabs」の中の「Platform1」をシーンビューにドラッグ&ドロップします。ヒエラルキービューでPlatform1を選択した状態でインスペクタからPositionを(0, -25.3, 0)に設定します。

f:id:nn_hokuson:20161025194238p:plain

見た目が寂しいので、同フォルダにある花や草などのPrefabを適当に配置しておきましょう(ここは本質ではないので、別にやらなくてもいいです^^;)

f:id:nn_hokuson:20161025195604p:plain

VR用の視点設定

続いてVR用の視点となるオブジェクトを作成しましょう。ヒエラルキービューから「Create」→「Empty Object」を選択し、作成したオブジェクトの名前をVREyeに変更します。ヒエラルキービューでVREyeを選択した状態で、インスペクタのPositionを(0, 6, 0)に設定します。

f:id:nn_hokuson:20161025201823p:plain

ヒエラルキービューで、Main CameraをVREyeにドラッグ&ドロップして子要素にします。Main Cameraを選択して、インスペクタの歯車からResetを選択します(これでTransformやRotaionがリセットされます)

f:id:nn_hokuson:20161025202201p:plain

つづいて、VR用の設定オブジェクトをVREyeオブジェクトにアタッチします。プロジェクトビューから「GoogleVR」→「Prefabs」→「GvrEditorEmulator」を選択し、ヒエラルキービューのVREyeにドラッグ&ドロップします。

f:id:nn_hokuson:20161025202752p:plain

これでVR用の視点の設定ができました。再生ボタンを押して実行してみましょう。Altキーを押しながらマウスをドラッグすることで視点が回転します。

f:id:nn_hokuson:20161025202949g:plain

VR用の視線カーソルの作成

視線と地面が交わる部分に次のようなカーソルを書きます。カーソル画像を下からダウンロードしてプロジェクトビューにドラッグ&ドロップして下さい。

f:id:nn_hokuson:20161025203202p:plain

まずはカーソルを地面に配置しましょう。ヒエラルキービューの「Create」→「3D Object」→「Plane」を選択して作成したオブジェクトを「Cursol」にリネームして下さい。ヒエラルキービューでCursolを選択した状態でインスペクタからPositionを(0, 0.1, 0)に設定します。

f:id:nn_hokuson:20161025203701p:plain

Cursolオブジェクトにテクスチャを設定します。プロジェクトビューの「circle」をシーンビューのCursolにドラッグ&ドロップして下さい。このままでは透明にならないのでシェーダの設定をします。シーンビューで「Cursol」を選択した状態でマテリアルのシェーダを「Unlit」→「Transparent」に設定してください。

f:id:nn_hokuson:20161025204214p:plain

カーソルを移動させるためのコントローラスクリプトを作成します。プロジェクトビューで右クリックし、「Create」→「C# Script」を選択して、ファイル名をCursolControllerに変更してください。

using UnityEngine;
using System.Collections;

public class CursolController : MonoBehaviour {

	void Update () {
		Ray ray = new Ray (Camera.main.transform.position,
			Camera.main.transform.rotation * Vector3.forward);

		RaycastHit hit;
		if (Physics.Raycast (ray, out hit)) {
			if (hit.transform.gameObject.name == "Platform1") {
				transform.position = hit.point + new Vector3(0, 0.1f, 0);
			}
		}
	}
}

このスクリプトでは視線からRayを飛ばして、地面と交差したときの座標をカーソルオブジェクトにセットしています。このスクリプトをCursolオブジェクトにアタッチします。プロジェクトビューの「CursolController」をヒエラルキービューの「Cursol」にドラッグ&ドロップしてください。

f:id:nn_hokuson:20161025205149p:plain

Rayで地面と当たり判定をするためには地面にColliderがアタッチされている必要があります。ヒエラルキービューで「Platform1」を選択し、インスペクタから「Add Component」→「Phsyics」→「Box」を選択します。

f:id:nn_hokuson:20161025205458p:plain

実行してみましょう!次のようなVR用の画面が表示されると思います。

f:id:nn_hokuson:20161025205829g:plain

視線カーソルの位置にキノコを植える

最後に、視線の先にキノコを植える仕組みを作りましょう。流れとしては下の3ステップです。

  1. キノコのPrefabを設定する
  2. Prefabからキノコのインスタンスを作るスクリプトを書く
  3. スクリプトにPrefabの実体をセットする

Prefabの概念や使い方は、私の書いた「Unity5の教科書」で詳しく説明しています。ぜひ参考にしてみてください。

キノコのPrefabの設定をする

キノコのPrefabには「Mushroom Land」→「Enviro」→「Prefabs」→「Shroom12」を使いましょう。プロジェクトビューでShroom12のPrefabを選択し、インスペクタからTransformのScaleを(0.18, 0.18, 0.18)にします。

f:id:nn_hokuson:20161025210955p:plain:w400

キノコのインスタンスを作る

画面がクリックされたときにキノコが生成されるように、CursolControllerを修正します。

using UnityEngine;
using System.Collections;

public class CursolController : MonoBehaviour {

	public GameObject mushroomPrefab;

	void Update () {
		Ray ray = new Ray (Camera.main.transform.position,
			Camera.main.transform.rotation * Vector3.forward);

		RaycastHit hit;
		if (Physics.Raycast (ray, out hit)) {
			if (hit.transform.gameObject.name == "Platform1") {
				transform.position = hit.point + new Vector3(0, 0.1f, 0);
			}
		}

		if (Input.GetMouseButtonDown (0)) {
			Vector3 pos = transform.position;
			pos.y = -0.5f;
			Instantiate (mushroomPrefab, pos, Quaternion.identity);
		}
	}
}

クラスの先頭でキノコのPrefab変数を宣言しています。画面がクリックされたときは、カーソルオブジェクトがある座標にキノコを生成しています。

Prefabの実体をセットする

最後に、スクリプトで宣言したmushroomPrefab変数に、キノコのPrefabの実体を代入します。ヒエラルキービューから「Cursol」を選択した上で、インスペクタのcursolControllerのmushroomPrefabの欄に、プロジェクトビューから「Mushroom Land」→「Enviro」→「Prefabs」→「Shroom12」をドラッグ&ドロップします。

f:id:nn_hokuson:20161025212006p:plain

これで完成です!Unityの実行ボタンを押してみましょう!クリックするとその場所にキノコが生えます。

f:id:nn_hokuson:20161025212315g:plain

実機にインストールする

これで、キノコを植えるVRアプリケーションが完成しました。実機にインストールして遊んでみましょう。実機にインストールするためには、UnityでVR用の設定が必要になります。まずはメニューバーからFile -> Build Settingsを選択し、「Player Settings」をクリックします。

f:id:nn_hokuson:20161025212518p:plain

インスペクタの「Other Settings」で「Virtual Reality Supported」のチェックをいれてください。その下に出てくるVirtual Reality SDKsの「+」ボタンをクリックし「CardBoard」を選択して下さい。

f:id:nn_hokuson:20170729233029p:plain:w300

「Bundle identifier」を任意の文字列に修正してください。また、「Resolution and Presentation -> Default Orientation」を「Landscape Left」に設定します。

f:id:nn_hokuson:20161025212642p:plain

設定できたら再度「Build Settings」ウインドウを表示し、「Build & Run」をクリックして実機にインストールしてください(iOSの場合、Xcodeとの連携がうまくできないことがあります。その場合は、プロジェクトフォルダを開いて、Xcodeを起動し、手動でインストールします)

まとめ

google CardBoardとUnityを使ってVRアプリケーションの開発をしてみました。たった850円でVRアプリケーションが開発できます。VRは敷居が高いなぁ、と思われていた方もUnityを使ってぜひ挑戦してみてくださいな。

参考書

今回のこの記事は↓の「UnityによるVRアプリケーション開発」参考にしました!UnityのVRに関しては唯一の本なので重宝しています。

【Unityシェーダ入門】uvスクロールで水面を動かす

シェーダを使ってテクスチャを動かすことで、水面を表現してみます。テクスチャを動かせれば水面だけではなく、滝や川など様々なものを表現できるようになります。今回はテクスチャを動かすために、uvスクロールという方法を使います。

f:id:nn_hokuson:20161020201348p:plain

今回の内容は次のような感じです。

UVスクロールとは

前の記事で、ポリゴンにテクスチャを貼り付けるときに、テクスチャのどこの部分を貼り付けるのかは、u座標とv座標を使って指定することを説明しました。

nn-hokuson.hatenablog.com

テクスチャの左下が(u, v)=(0, 0)、右上が(u, v)=(1, 1)になります。テクスチャをスクロールためには、フレームごとにuv座標にオフセットを足していきます。

f:id:nn_hokuson:20161016170315p:plain

ただし、サーフェイスシェーダは前フレームの状態を覚えておくことはできず、分かるのは「テクスチャのどこを使うか」だけです。したがって「前フレームのuv座標にオフセットを足していく」ことはできません。

そこで「フレームごとにuv座標にオフセットを足す」かわりに、「uv座標にスクロール速度✕時間を足す」方法を使います。スクロール速度✕時間=移動距離なので、結局は同じことをしていますが、この方法を使えば非常に簡単なプログラムでuvスクロールが実現できます。

ステージと水面を作る

まずは水面を表示するためのパネルと、水面の周りの地形(Terrain)を配置しましょう。まずはヒエラルキービューから「Create」→「3D Object」→「Terrain」でテラインを配置します。

f:id:nn_hokuson:20161016171707p:plain

Terrainの地形ツールを使って水面を表示するための窪地を作ります。ついでにTerrainにテクスチャも配置しておきます。

f:id:nn_hokuson:20161016172038p:plain

水面となるPlaneを追加します。ヒエラルキービューから「Create」→「3D Object」→「Plane」を選択し、先ほどTerrainで作った窪地に配置します。

f:id:nn_hokuson:20161016172423p:plain

最後に水面のテクスチャを表示するためのマテリアルと、水面を動かすためのシェーダファイルを作成します。プロジェクトビューで右クリックして「Create」→「Shader」→「Standard Surface Shader」を選択しwaterという名前で保存します。いま作成したwaterシェーダを選択した状態で、右クリック→「Create」→「Material」を選択し、waterという名前で保存します。

Panelのシェーダを「Custom/sample」に変更します。シェーダの詳しい作成方法と設定方法は下の記事で紹介していますので参照して下さい。

nn-hokuson.hatenablog.com

水面を動かすシェーダを作る

UVスクロールを使って水面を動かすためのシェーダプログラムは次のようになります。

Shader "Custom/water" {
	Properties {
		_MainTex ("Water Texture", 2D) = "white" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Standard fullforwardshadows
		#pragma target 3.0

		sampler2D _MainTex;

		struct Input {
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutputStandard o) {
			fixed2 uv = IN.uv_MainTex;
			uv.x += 0.1 * _Time;
			uv.y += 0.2 * _Time;
			o.Albedo = tex2D (_MainTex, uv);
		}
		ENDCG
	}
	FallBack "Diffuse"
}

インスペクタから水面のテクスチャをセットできるようにPropertiesにテクスチャ変数を追加しています。また、サーフェイスシェーダが処理するテクスチャ座標を受け取るため、Input構造体にuv_MainTexを宣言しています。

surfメソッドの中でテクスチャをスクロールさせています。まずは「テクスチャのどの部分を使うか」という情報をuv変数に代入しています。次の2行では、uv変数に対してどれだけのオフセットをたすのかを指定しています。オフセットは「スクロール速度✕時間(=移動距離)」で計算しています。ここではu方向とv方向でスクロール速度を変えています。

_Time変数はUnityのシェーダにデフォルトで用意されている変数で、時間とともに値が増加します。詳しくは次のページを参考にして下さい。

docs.unity3d.com

結果

シェーダで水面を動かした結果は次のようになりました。非常に単純なシェーダですが、上にも書いたとおりuvスクロールを使えば滝や川、雲など様々な効果が実現できます。



Unity5の教科書、絶賛発売中です!
よろしくお願いします^^