Unityを使ってシェーダーを作る方法を学ぶ連載。今回は、Unityに用意されているサーフェースシェーダープログラムの中身を解説する。
Unityを使ってシェーダーを作る方法を学ぶ連載「Unityで始めるシェーダー入門」。前回の「Unityゲーム/アプリの表現力の幅を広げるシェーダーとは――シェーダー作成の初歩」では、シェーダーの概要と、作り始めるまでの環境構築を紹介した。今回は、前回作成したサーフェースシェーダープログラムを細かく見ていく。
今回解説するのは、前回紹介した図1のような表示になるシェーダーだ。
リスト1はシェーダープログラム(Standard Suface Shader)のコードだ。
Shader "Custom/Standard Surface Shader" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
まず、全体が「Shader{}」というブロックで囲まれているのが分かる。
Shader "グループ名/シェーダー名" {
/*・・・・・・・・・・*/
}
このShaderの最初に「Properties{}」がある(2〜7行目)。書式は下記の通りだ。
Properties {
プロパティ変数名 ("インスペクター表示名", 変数の型) = 初期値
}
ここで宣言したものが「インスペクター表示名」で、UnityのInspector内に表示されることになる。
Propertiesの内容は下記のように記述されている(リスト2)
Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 }
この中では、プロパティ変数名の、色(_Color)、テクスチャ(_MainTex)、滑らかさ(_Glossiness)、メタリック(金属的な質感)の度合い(_Metallic)などの質感をパラメータとして利用できる。
プロパティの変数は、Unityではデフォルトでアンダースコアと大文字で始めるようになっている。今後も、この記述に従う。
この部分をInspectorで見ると、図2のように表示されている。リスト2で、「インスペクター表示名」に指定した名前がInspector内に表示されているのが分かる。
Properties構文の変数の型には下記のものがある。
範囲制限付きの数値をスライダーで設定できる(図2のSmoothnessやMetallic)。基本的には、0〜1の値を設定したいときに使う。
初期値には、範囲内の適当な数値を設定しておくといい。リスト2では、_Glossinessと_Metallicプロパティ変数をRange型としているので、図2を見るとスライダーが表示されているのが分かる。
float(高精度)、half(中精度)、fixed(低精度)の3種類がある。3つの違いは数値の精度だ。floatが最も高く、fixedの精度が最も低い。
使い分け方としては、Unityのデフォルトに従い、通常の座標はfloat(通常のfloatと同じ一般的な32ビット)、色はfixed(固定小数点数)、それ以外はhalf(浮動小数点値で、16ビット)にするといいだろう。halfは、ショートベクトル、方向、オブジェクトの空間位置、HDRカラーに用いられる。
含まれる数値が2つ以上の場合は、float2、float3、float4と後ろに数字を付ける。他についても同じだ。例えば、ColorはR、G、B、Aの4つの数値を持っているのでfixed4となる数値を設定できる。座標を表すX、Y、Zの場合はfloat3となる(参考)。
カラーピッカー(図3)から色を選択できる。
初期値が(1, 1, 1, 1)となっており、4つの数値を指定する。左から(R, G, B, A) を指している。
ベクトルを4つの数値で設定できる。
初期値が(1, 1, 1, 1)となっている。左から(X, Y, Z, W) を指している。
テクスチャを設定できる(図2の「Non(Texture)」と記述されている箇所)。
初期値には、「"white"{}」もしくは「"black"{}」を設定する。「テクスチャが適用されていないときに、どのように表示されるか」という指定になる。
キューブマップを設定できる。Cubemapとは図4のようなものだ。
CUBEの詳細に関しては連載の後の回で解説する予定だ。初期値には、「"white"{}」もしくは「"black"{}」を設定する(参考)。
この変数の型を頭においてリスト2を見ると内容が理解しやすいのではないだろうか。
次は、SubShader{ /*・・・*/ }で囲まれているリスト1の8〜20行目について解説する。
SubShaderは、Unityがメッシュを描画するとき、使用するシェーダーを見つけ、グラフィックスカードで実行する最初のサブシェーダーとなる。
SubShader内にはTags(タグ)が存在している(リスト1の9行目)。タグは幾つでも指定可能だ。
Tags {"TagName1" = "Value1" "TagName2" = "Value2"……}
「Tags」の記述については、初心者の間は、基本的に下記の2つを覚えておくだけでいい。
Tags { "Queue" = "Geometry" "RenderType" = "Opaque"/*不透明*/ }
Tags { "Queue" = "Transparent" "RenderType" = "Transparent"/*透明*/ }
Queue(キュー)では、オブジェクトを描画する順番を判定できる。デフォルトでは、省略されている。通常は、「Tags{"RenderType" = "Opaque"}」と記述することが多い。
リスト1の10行目には「LOD 200」という記述がある。
LODとは「Level Of Detail」の略だ。状況に応じて描画クオリティーを変更する仕組みのことを指している。
「LOD 200」は「現在設定されたLOD値が200以上なら、このShaderは描画してもいい」という意味で、負荷調整のために用いられるようだが、初心者のうちは意識しないでもいいだろう。
リスト1の12〜33行目の間に、下記の記述がある。これは、シェーダープログラムの「開始位置」と「終了位置」に記述する。
CGPROGRAM
/*・・・・・・*/
ENDCG
この中に、描画処理を記述していく。
リスト1の13行目に、下記の記述がある。
#pragma surface surf Standard fullforwardshadows
#pragma surface 関数名 ライティングモデルオプション
#pragmaは「pragmaディレクティブ」と呼ぶ。これは、「サーフェースシェーダーを使用する」という意味で、関数名は各自が自由に設定できるが、Unityではデフォルトで「surf」(リスト3として後述)が使われているので、これに従う。
13行目のコードは、関数名が「surf」でライティングモデルオプションが「fullforwardshadows」ということだ。
fullforwardshadowsは、フォワードレンダリングで、ポイントライトやスポットライトを使いたい場合に必要だ。今回の場合は、fullforwardshadowsは特に必要ないが、デフォルトで設定されている。
ライティングモデルオプションには「Standard」「Lambert」(拡散反射光)、「BlinnPhong」(鏡面反射光のモデルであるSpecular)の指定ができる。今回は中に「Standard」が入っている。これは、ライティングモデルはSurfaceOutputStandard出力構造体(リスト3)を使用するということだ。Unityの標準シェーダーと一致している。
ライティングモデルオプションがLambertの場合は、「void surf(Input IN,inout SurfaceOutput o)」、Standardの場合は「void surf(Input IN,inout SurfaceOutputStandard o)」、StandardSpecularの場合は「void surf(Input IN,inout SurfaceStandardSpecular o)」となる。
その他にもいろいろあるので、気になる方は下記URLを参照してほしい。
リスト1の14行目には「#pragma target 3.0」の記述がある。
#pragma target シェーダーモデル
シェーダーモデルによって、使えるテクスチャの数や機能が異なる。数値が高い方がさまざまなことが行える。
テッセレーションなど、DirectX 11の機能を使いたい場合は、5.0にする必要があるが、取りあえずはデフォルトの3.0にしておいても問題はない。
リスト1の16行目に「sampler2D _MainTex;」の記述がある。
_MainTexプロパティは、リスト1の4行目にあるように図1のAlbedo(RGB)を呼び出す場合に使われる。
sampler2Dは、テクスチャの型(2D)を意味する。よって、プロパティ_MainTexはテクスチャの型であると宣言していることになる。
リスト18〜20行目は入力構造体を定義している。下記では、そこだけリスト1から切り出している。
struct Input { float2 uv_MainTex; };
これはテクスチャの座標を表す。uとvの2次元座標なので、float2としている。uvの後にはProperties{}内で宣言したテクスチャの変数名_MainTexを付加している。これによって、テクスチャを貼り付けるための「UV座標」の使用が可能になる。
UV座標とは、テクスチャ座標のことで、オブジェクトの頂点座標にテクスチャのどの部分を描画するかを0〜1の値で表したものを指す。
プロパティ名_MainTexの前にuvと付けることで、自動的にマテリアルのテクスチャ座標設定(TilingとOffset)が適用されたUV座標が入ってくる(図6参照)。テクスチャのUV値は「uv_HogeTex」のように先頭に「uv_」を付けるということだ。
リスト1の22〜24行目に下記の記述がある。
half _Glossiness; half _Metallic; fixed4 _Color;
「滑らかさ」を表す_Glossinessプロパティをhalf型で宣言している。
同様に、_Metalilicプロパティもhalf型で宣言している。これらは、テクスチャの座標や色ではないのでhalf型で宣言している。
次の_Colorプロパティは色を表しRGBA値を持っているのでfixed4で宣言している。これらのInspectorにおける表示は図2を参照してほしい。
これらはsurf関数内で使用されることになる。
リスト1の26〜32行目、surf関数内には、Unityから渡されるシェーダー引数(プロパティ変数など)が入ったSurfaceOutputStandard構造体の内容がある。リスト3として切り出しておく。
void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; }
SurfaceOutputStandardは、Unityのツール側であらかじめ定義されているので、ユーザーが意識することはない。
Unityでは、下記のように定義されている(リスト4)。
struct SurfaceOutputStandard { fixed3 Albedo; /* ベース(ディフューズまたはスペキュラ)カラー、デフォルトは黒*/ fixed3 Normal; /*法線(X,Y,Z)、デフォルトは設定なし*/ half3 Emission; /*表面から放出される光の色と強度を制御する*/ half Metallic; /*金属か否か(0=金属ではない, 1=金属)*/ half Smoothness; /* 滑らかさの度合い(0=粗い, 1=滑らか)*/ half Occlusion; /* オクルージョン(遮蔽(しゃへい))、デフォルトは1で0の場合は完全遮蔽*/ fixed Alpha; /* 不透明度(0〜1)、デフォルトは0*/ };
これを基に、リスト3のコードを見てみよう。
surfの中に「Input IN」という記述がある。Input構造体から渡ってきた情報をINという名前で読み込んでいる。特に「IN」という名前でなくても構わないが、デフォルトではINになている。
まず、テクスチャを使用するために、text2D関数を呼び出している(リスト3の2行目)。tex2D関数は、UV座標(uv_MainTex)からテクスチャ(_MainTex)上のピクセルの色を計算して返す関数だ。_MainTextと_Colorを乗算して、fixed4型の変数cに代入している。Inspectorでテクスチャの明度や色を調整することになる。
その後、o.AlbedoにcのRGB値を代入する(リスト3の2行目)。SurfaceOutputStandardを「o」という名前で書き出しているわけだ。カラーピッカーで選択した色がAlbedoに適用されることになる。Albedo(アルベド)とは、「外部からの入射光に対する反射率」という意味だ。
o.Metallicとo.Smoothnessには、プロパティ変数をそのまま接続している(リスト3の3〜4行目)。プロパティの_Glossinessや_MetallicはRange型なのでInspector内でスライダーによって調節が可能だ。注意しなければならないのは、Properties内で_Gossinessを定義しないで、_Metallicだけを定義しただけでは、オブジェクトに光沢が表示されない。光沢を表示させる場合は、これらをセットで使用する必要がある。
o.Alphaにはcの値(テクスチャと色の透過度を掛け合わせた値)を接続している(リスト3の5行目)。今回は不透明シェーダーなので、何を入れても特に変化はない。「o.Alpha = 1」としても問題はない。
また、接続していない項目は、自動的にデフォルト値が適用される。
リスト1の35行目には「FallBack "Diffuse"」という記述がある。FallBackの書式は下記のようになる。
FallBack "すべり止めシェーダー名"
「すべり止め」とは、「もし、SubShaderの中に記述したシェーダープログラムが、ビデオカードでサポートされていない場合は、Unityのビルドインシェーダー「Diffuse」を使用する」という意味だ。ここを深く追求すると、難しくなるので、初心者のうちは、「FallBack "Diffuse"」と記述すると覚えておくといいだろう。
これでStandard Suface Shaderのコードの解説は終わりだ。
前回紹介したように、「Materials」フォルダ内のStandard Suface Materialには、リスト1のシェーダーがひも付いているので、マテリアルのInspectorを開いて、Colorの横に表示されている白い長方形をクリックする。するとカラーピッカーが表示されるので、適当な色を選択する。今回は黄色を選択した(図5)。
次に、同じくInspector内の、Smoothnessにはデフォルトで0.5を指定している。ここの値が0だと金属的な質感が出ないので注意が必要だ。Propertiesの中では初期値として0.5と指定している。Metallicのスライダーの値を1まで移動させる。すると、光沢のある黄金色のマテリアルが完成する(図6)。
このマテリアルを、前回記事で配置しておいた、Scene上の片方の3Dキャラクター(Ethan)の上にドラッグ&ドロップする。すると、図7のようにGame画面上に3Dキャラクターの全体がメタリックな黄色で表示される。
今回は、ここまでだ。Surface Standard Shaderの中身のコードを詳しく見てきたが、いかがだっただろうか。Inspector内でどのように表示されるかを確認しながら進めていくと分かりやすいと思う。
次回は、今回のシェーダーをカスタマイズして、光沢を消すシェーダーを作成してみよう。
薬師寺 国安(やくしじ くにやす) / 薬師寺国安事務所
薬師寺国安事務所代表。Visual Basicプログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。
1997年に薬師寺聖とコラボレーション・ユニット「PROJECT KySS」を結成。
2003年よりフリーになり、PROJECT KySSの活動に本格的に参加。.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。
Windows Phoneアプリ開発を経て、現在はWindowsストアアプリを多数公開中。
Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。
Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。
Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。
Microsoft MVP for Development Platforms-Windows Platform Development(Oct 2014-Sep 2015)。
Copyright © ITmedia, Inc. All Rights Reserved.