UXデザインハック頂点・フラグメントシェーダーで3Dキャラクターの一部の色を変える:Unityで始めるシェーダー入門(5)Unityを使ってシェーダーを作る方法を学ぶ連載。前回まで「サーフェースシェーダー」を扱ってきたが、今回は「頂点・フラグメントシェーダー」を扱う。KySS]Unityを使ってシェーダーを作る方法を学ぶ連載「Unityで始めるシェーダー入門」。連載第1回ではシェーダーの概要と作り始めるまでの環境構築を紹介した。前回までは、「サーフェースシェーダー」を扱ってきたが、今回は「頂点・フラグメントシェーダー」を扱う。コード自体も少し複雑になるが、頂点・フラグメントシェーダーは、今回のみの使用であるため、ここでしっかりと理解していただきたい。本稿で作成するシェーダーでは、最終的に図1のような処理が適用される。兵士のヘルメットの部分だけ色が変わっている頂点・フラグメントシェーダーとは頂点・フラグメントシェーダーでは、頂点シェーダーで頂点の位置情報を加工した後、フラグメントシェーダーで最終的なピクセルの色を決定する。ライティングの計算を行う必要がないものや、より高度なエフェクト、表現を作成したい場合に使用する。頂点シェーダーでは、3次元の描画はポリゴンの表示が基本になっている。ポリゴンは一般的に三角形の面をつなぎ合わせたものが使用されている。三角形は頂点の位置が決まれば自動的に面の位置も形も決まるため、実際に計算するのは頂点の位置だけでいいことになる。これが「頂点シェーダー」だ。頂点シェーダーの役割は「モデルの頂点の位置を、画面上の位置に変換すること」だ。頂点の位置は、「モデルの位置」「モーションによる変形」「カメラの位置」などで変化する。これらが「画面上ではどの位置に頂点が来るか」を計算するのが頂点シェーダーの基本的な処理となるのだ。次に、フラグメントシェーダーとは、「色を計算する」ことだ。「テクスチャ(画像)からテクスチャ座標の位置にある色を持ってくる」という処理になるが、他にも、独自の色を設定したり、陰の部分を暗くしたり、半透明にしたりといった、さまざまな処理が行える。フラグメントシェーダーは、DirectXでは「ピクセルシェーダー」とも呼ばれる。詳細については、Unityのマニュアルを参考にしてほしい。頂点・フラグメントシェーダーのコードUnityメニューから新しいScene画面を開く。いつものように「Shaders」フォルダ内に「VertexFragmentShader」というシェーダーを作るが、今回は前回までのようなサーフェースシェーダーの作り方と少し異なる。Shader」と選択する(図2)。次に、「Materials」フォルダの中に「VertexFragmentMaterial」を作成する。この作成手順は前回までと同じだ。Shadersフォルダに作成した、VertexFragmentShaderファイルをダブルクリックすると、エディタが起動してコードが表示される。不要なコードを削除して、必要なコードを多少追加して整形したのが、リスト1だ。VertexFragmentシェーダーのコード今回筆者が行ったのは、5行目と33行目と46行目を少し変更しただけだ。必要なコードのほとんどはUnlitシェーダーを作成すると自動的に記述される。自分で意識的に書く必要はないが、その役割を知っておくことは重要だ。以降、コードの中身を解説する。(1,0,0,1)」を追加した。これにより、カラーピッカーから色の選択が可能になる。{}」は2D型でテクスチャを選択する項目が表示される。12行目の「Pass」ブロックは、Unlitで作成すると自動的に追加される。このブロック内には、ゲームオブジェクトのジオメトリを1回レンダリングする処理を記述する。vert」は、頂点シェーダーとして「ヴェール関数」(vert)を使用するという意味だ。frag」は、フラグメントシェーダーとして「フラグメント関数」(frag)を使用するという意味だ。vertとfrag関数は、この後で定義している。18行目では、「UnityCG.cginc」をインクルードしている。これをインクルードすると、法線/テクスチャ座標/頂点シェーダー入力などのヘルパー関数を利用できる。pos)」関数を使うためにインクルードしている。20〜24行目では、「appdata」構造体を定義している。この中では、Unityから受け取る値を記述する。22行目は、頂点の位置としてvertexを定義している。23行目は、テクスチャの座標としてuvを定義している。25〜29行目では「v2f」構造体を宣言している。この中では、フラグメントシェーダーで使用する値を記述している。Fragment」の略だ。頂点シェーダーからフラグメントシェーダーに複数の値を渡す際には、このように構造体を定義してそれを利用する。v2f構造体内の27行目は、23行目と同じく、テクスチャの座標として「uv」を定義している。27〜28行目では、UV座標(テクスチャ座標)と、その座標の頂点のスクリーン座標をフラグメントシェーダーに渡していることになる。22行目では「POSITION」、28行目では「SV_POSITION」と定義している。POSITIONは、あらゆる任意の座標情報を示すための「セマンティクス」で、SV_POSITIONは「システム上で扱われる任意ではない座標情報である」ことを示すために使用されるという違いがある。セマンティクスとは、シェーダー入力またはシェーダー出力に付加されている文字列で、パラメーターの使用目的に関する情報を伝達する。「vertexシェーダーの入力(関数の引数)は全てセマンティクスを持たねばならない」とされている。セマンティクスの一覧は表1のようになる。float4参考29行目では、ピクセル座標を受け取るvertexを定義している。31行目では、テクスチャ型として「_MainText」を宣言している。3〜7行目のPropertiesで宣言したのと同じ名称でないといけない。プロパティで受け取ったデータをfrag関数内で使うための定義だ。32行目では、RGBAの4原色としてfixed4型の_Colorを宣言している。プロパティで受け取ったデータをfrag関数内で使うための定義だ。34〜40行目では、25〜29行目で定義しているv2f構造体のvert関数を定義している(リスト2)。引数にappdata構造体のvを指定している。vert関数ではフラグメントシェーダーで必要なパラメーターを返す。少なくともピクセル座標を出力する必要がある。vert関数リスト2の3行目では、v2f構造体として「o」を宣言している。4行目では、ローカル座標をカメラから見た座標に変換している。5〜6行目では、4行目で取得した情報を、v2f構造体に詰め込まれてフラグメント関数(frag)に送られる。リスト1の42〜46行目ではリスト3のようにfrag関数を定義している。引数にv2f構造体の「i」を指定している。frag関数frag関数では最終的なピクセルの色を返す。ここでは「tex2D」関数で、インスペクターで指定した色をそのまま返している。フラグメントシェーダーは、カラーを出力し「SV_Target」セマンティクスを持つ。「ピクセルの色」という意味だ。ここでも、SV_Targetを指定して色を返すことを宣言している。リスト3の3行目で、インスペクター内のテクスチャに指定したテクスチャを出力している。リスト3の4行目では、3行目で取得した変数colの値(テクスチャ)に、指定した色を乗算して返している。VertexFragmentMaterialインスペクターを表示させるVertexFragmentMaterialインスペクターを表示させてみよう。Materialsフォルダ内のVertexFragmentMaterialを選択して、インスペクターを表示させると、図3のように表示される。Shaderは「Custom」グループにシェーダーが入っていたが、今回の頂点・フラグメントシェーダーでは「Unlit」というグループの中に入っているのが分かる。Storeからインポートする今回は3Dキャラクターの身体の一部分である衣装の色を変えてみる。そのためには各身体の部位に分かれたテクスチャが必要になる。Pack」という無料のアセットをインポートしてほしい。01.FBX」をScene画面上にドラッグ&ドロップする。カメラやキャラクターの位置を調整して図4のような表示にしてほしい。01.FBX」をScene画面に配置。Game画面でも表示されているVertexFragmentMaterialインスペクターを設定するVertexFragmentMaterialインスペクターを設定しよう。「Color」には「Green」を選択。Texture」から「Head_Hair_ArmyCapColor」(兵士のヘルメット)を指定する(図5)。テクスチャに「Head_Hair_ArmyCapColor」(兵士のヘルメット)を指定したこのVertexFragmentMaterialを、Scene画面にある兵士の頭にドラッグ&ドロップしてみよう。図6のように表示されれば成功だ。兵士のヘルメットの色が変わった次回は「サーフェースシェーダーのリムライティング」について今回は頂点・フラグメントシェーダーについて解説した。理解が難しいのはv2f構造体のvert関数で、フラグメントシェーダーで必要なパラメーターを返すところではないかと思う。理解するには、パラメーターのコードを変えたりするなど経験を積んでいく以外に方法はないのではないだろうか。本稿を参考にして、いろいろと試してみることをお勧めする。次回は「サーフェースシェーダーのリムライティング」について解説する。Basicプログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。KySSの活動に本格的に参加。.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Phoneアプリ開発を経て、現在はWindowsストアアプリを多数公開中。