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

おもちゃラボ

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

【Unityシェーダ入門】頂点カラーを表示するシェーダを作る

シェーダ Unity

3Dモデルの表現には通常テクスチャを使いますが、3Dモデルの頂点ごとに色を設定したモデルを使用することもできます。

こちらの動画で使われているモデルは、テクスチャを使わずすべて頂点カラーで表現されています。たぶん、世界観に統一感をもたせるためのDavid Oreillyの工夫だと思います。

ちなみに、この動画は面白いです(笑)

vimeo.com

Unityでも頂点カラーを使用できるのですが、標準のシェーダは頂点カラーをサポートしていないので、自分でシェーダを書く必要があります。

頂点カラーの表現

頂点カラーとは、頂点ごとに設定された色情報のことです。3Dモデルの頂点には、座標や法線ベクトル以外に色情報も設定することができます。
設定にはBlenderやMaya、3DS Maxなどの3Dモデリングソフトを使用します。

f:id:nn_hokuson:20170329191941p:plain:w350

頂点情報を扱うためには頂点シェーダ(vertex shader)をフックし、頂点カラーを取り出す必要があります。取り出した頂点カラーは頂点シェーダからsurfシェーダに渡して表示します。

f:id:nn_hokuson:20170329191948p:plain:w450

頂点カラー用のシェーダを作る

Unityに頂点カラーを設定した3Dモデルをインポートした状態では、モデルはグレー色のままです。

f:id:nn_hokuson:20170328160857j:plain:w400

頂点カラーを表示するシェーダを作りましょう。シェーダファイル(VertexColor.shader)を作成し、作成したシェーダをアタッチしたマテリアルを作ってください。

f:id:nn_hokuson:20170329192004p:plain:w400

次にVertexColorに次のシェーダプログラムを入力してください。

Shader "Custom/VertexColor" {
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert vertex:vert
		#pragma target 3.0

		struct Input {
			float4 vertColor;
		};

		void vert(inout appdata_full v, out Input o){
			UNITY_INITIALIZE_OUTPUT(Input, o);
			o.vertColor = v.color;
		}

		void surf (Input IN, inout SurfaceOutput o) {
			o.Albedo = IN.vertColor.rgb;
		}
		ENDCG
	}
	FallBack "Diffuse"
}

トゥーンシェーダなどと比べると非常に単純なシェーダになります。

nn-hokuson.hatenablog.com

このプログラムでは上図にも書いたとおり、頂点シェーダ(vertex shader)をフックしています。頂点シェーダをフックするための手順は次の2つです。

  1. 頂点シェーダをフックすることをUnityに伝える
  2. 頂点シェーダのメソッドを作成する

#pragmaの行に「vertex:頂点シェーダのメソッド名」と書くことで、Unityに頂点シェーダをカスタムで作成することを伝えることができます。ここではvertという名前の頂点シェーダを作成しています。

続いて、頂点シェーダのメソッドを作成します。引数には頂点シェーダへの入力としてappdata_full型、頂点シェーダからの出力にInput型をとります。appdata_full型の詳細はこちらで説明されています。

docs.unity3d.com

頂点シェーダの中ではUNITY_INITIALIZE_OUTPUTを使って出力の値を初期化したあと、appdata_full型のcolor変数から頂点カラーの情報を取り出し、Input型のvertColor変数に値を詰めています。
surfシェーダではこのInput型の値を受け取り、Albedoとして表示しています。

f:id:nn_hokuson:20170329192028p:plain:w400

表示結果

頂点カラーを表示するシェーダをモデルにアタッチした結果がこちらになります。
ちゃんと緑色のカエルが表示できましたね!

f:id:nn_hokuson:20170329192709j:plain:w450

頂点カラーのモデルを使用することはまれかもしれませんが、頂点シェーダをフックする方法はよく使うので、ここで紹介しました。

今回のかえるのデータはこちらのものを使用させていただきましたm(_ _)m
f:id:nn_hokuson:20170329194931j:plain:w300
www.davidoreilly.com

【Blender】ASCIIのFBXファイルが読み込めない場合の対処法

3DCG

Blenderでfbxファイルをインポートしようとすると、次のようなエラーが出ることがあります。

ASCII FBX files are not supported

f:id:nn_hokuson:20170328193629p:plain:w250

これはBlenderがASCII形式のFBXファイルをサポートしていないために出るエラーです。
Blenderで読み込めるようにするためには、FBXファイルをBinary形式に変換する必要があります

FBXファイルをBinary形式にするには次のAutodesk FBX Converterを使います。
FBX ConverterはWindows用とMac用が用意されているので、次のサイトからお使いの環境のものをインストールしてください。

usa.autodesk.com

使い方は非常に簡単で、FBX Converterが起動したら、画面左上の「Add FBX Converter」をクリックしてください。

f:id:nn_hokuson:20170328193639p:plain:w400

次のようなウインドウが表示されるので、左側の欄に変換したいFBXファイルをドラッグ&ドロップします。

画面右側に出力フォーマットの選択画面が出るので、出力フォルダと出力形式(FBX 2013かOBJあたり・・・)を選択し、「FBX Save Mode」が「Binary」になっているのを確認してから、「Convert」ボタンを押してください。

変換する3Dモデルがメッシュだけならobj形式でも問題ありませんが、ボーンが入っているモデルの場合はfbx形式を選択して下さい。

f:id:nn_hokuson:20170328193652p:plain:w600

指定したパスにBinary形式のFBXファイルが生成されるので、これをBlenderで読み込めば、正しく3Dモデルが表示されます。

【Unityシェーダ入門】トゥーンシェーダを自作してみる

Unity シェーダ

Unityではフォトリアルな映像を作るのは簡単ですが、風ノ旅ビトやGravity dazeなどの独自にデフォルメされた映像を作るためにはシェーダを使う必要があります。

f:id:nn_hokuson:20170327192144j:plain

その中でアニメ風の映像を作るためには、トゥーンシェーダ(Toon Shader)を使います。UnityにはEffectパッケージにToonシェーダが用意されているのですが、ここでは勉強のためにトゥーンシェーダを自作してみましょう。

Unityに標準で付属するToonシェーダについては、↓の記事で非常にわかりやすく説明されていますよ〜!
tips.hecomi.com

Toonシェーダのアルゴリズム

今回作るトゥーンシェーダではRampテクスチャと呼ばれる、色をサンプリングするための専用テクスチャを使います。

f:id:nn_hokuson:20170327201313p:plain:w300

オブジェクト表面の暗い所はこの色、明るい所はこの色、といった感じでRampテクスチャから色を取得していきます。

f:id:nn_hokuson:20170327193602p:plain:w400

では、どのようにして暗い・明るいを計算するのでしょうか。
オブジェクト表面の明るさを計算するためには、ライトの方向とオブジェクトの法線の内積を取ります。これにより、ライトと法線方向が一致する場合は値が大きくなり、ライトと法線が垂直に近づくにつれて値が小さくなります。

f:id:nn_hokuson:20170327193238p:plain:w500

ライトの方向とオブジェクトの法線の内積で明るさが計算できたら、それを0〜1にスケーリングしてRampテクスチャのu座標とします。
Rampテクスチャはu座標に沿って段階的に明るくなるため、アニメのような明るさの変化がしっかり出る表現になります。

Unity 5.x Shaders and Effects Cookbook

Unity 5.x Shaders and Effects Cookbook

Toonシェーダファイルを作る

まずはトゥーンシェーダ用のシェーダファイルと、シェーダをアタッチしたマテリアルを作ります。

プロジェクトビューで「Create」→「Shader」→「Standard Surface Shader」を選択し、「Toon」というファイル名で保存します。
このファイルを選択した状態で、「Create」→「Material」を選択し、Toonマテリアルを作成してください。作成したマテリアルにToonシェーダが使われていればOKです。

f:id:nn_hokuson:20170327192212p:plain:w400

Toonシェーダのファイルに次のシェーダプログラムを入力してください。

Shader "Custom/Toon" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _RampTex ("Ramp", 2D) = "white"{}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf ToonRamp
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _RampTex;

        struct Input {
            float2 uv_MainTex;
        };

        fixed4 _Color;

        fixed4 LightingToonRamp (SurfaceOutput s, fixed3 lightDir, fixed atten)
        {
            half d = dot(s.Normal, lightDir)*0.5 + 0.5;
            fixed3 ramp = tex2D(_RampTex, fixed2(d, 0.5)).rgb;
            fixed4 c;
            c.rgb = s.Albedo * _LightColor0.rgb * ramp;
            c.a = 0;
            return c;
        }

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

今回作成するToonシェーダの主要な部分はLightingToonRampメソッドです。このメソッドでは上のアルゴリズムどおり、ライトの方向とオブジェクトの法線の内積を取り、その値に応じてRampテクスチャから値を取得しています。

このLightingToonRampメソッドはカスタムライティングを行うためのメソッドです。どのようにして、カスタムライティングのためのメソッドを作るのかを下で紹介します。

サーフェイスシェーダでカスタムライティングを使う

トゥーンシェーダのプログラムは、これまで出てきたsurfシェーダだけでは作れません。サーフェイスシェーダに追加するかたちで、ライティングの工程をフックする必要があります。

前の記事でも書いたとおり、Unityのサーフェイスシェーダはマテリアルの表面の色を定義する工程とライティングをする工程に分かれています。

f:id:nn_hokuson:20170327193743p:plain:w450

これまでは、ライティングの工程はUnityにお任せしていましたが、今回は自分でライティングまで行いたいのでライティングの工程をフックします。

f:id:nn_hokuson:20170327193754p:plain:w450

ライティングの工程をフックするためには次の3つの手順が必要になります。

  1. ライティング用のメソッド(Lighting◯◯◯)を作る
  2. メソッド名をUnityに伝える
  3. StandardSurfaceOutputを使わないようにする

まずはライティング用のメソッドを作ります。ライティング用のメソッド名はLightingから始める必要があります。ここではLightingToonRampという名前にしました。

fixed4 LightingToonRamp (SurfaceOutput s, fixed3 lightDir, fixed atten)

また、ライティングのメソッドをフックしたことをUnityに伝えるため、#pragmaの行にライティングのメソッド名の◯◯◯の部分を追加します。

#pragma surface surf ToonRamp

surfシェーダでライティングの工程をフックした場合には、surfの出力にはSurfaceOutputStandard型を使うことができません。ここではSurfaceOutput型に書き換えています。
SurfaceOutput型にはEmmisionやSmoothnessは定義されていないので、surfメソッドからこれらの行も削除します。

Rampテクスチャをマテリアルにセットする

最後にToonシェーダのマテリアルにRampテクスチャをセットします。プロジェクトビューでToonマテリアルを選択し、インスペクタからRampの欄にRampテクスチャをドラッグ&ドロップして下さい。

f:id:nn_hokuson:20170327201900p:plain:w300

実行すると次のようなシェーダの表現になりました。Rampテクスチャを差し替えるだけで、さまざまな絵作りが出来るので、試してみて下さい!
f:id:nn_hokuson:20170327192812p:plain:w500