おもちゃラボ

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

【Unityシェーダ入門】ポリゴンの表面と裏面に別テクスチャを貼る

UnityでPlaneにテクスチャを貼って普通に回転させると、裏面が透明になってしまいます。これを防ぐためにカリングをオフにしたシェーダを作りました。

nn-hokuson.hatenablog.com

このように、カリングをオフにすることで、両面ともテクスチャを表示することができます。この記事ではもう一歩すすめて、表面と裏面で違うテクスチャを貼る方法を紹介したいと思います。
f:id:nn_hokuson:20180220203042p:plain

ポリゴンが裏面か表面かを判定する

ポリゴンが表向きか裏向きかはポリゴンの頂点を指定する順番で決まります。詳しくは次の記事を参考にして下さい。

nn-hokuson.hatenablog.com

Unityのシェーダでポリゴンが表向きか裏向きかを判定するには、フラグメントシェーダにfacingという引数を追加します。facingはfixed型の値で0以上なら表面、0未満なら裏面を表します。
f:id:nn_hokuson:20180220200802p:plain:w550

このfacing変数を使ってポリゴンの向きに応じてテクスチャを割り当てます。

ポリゴンの向きを判定するシェーダを作る

まずはシェーダファイルを作成しましょう。プロジェクトウィンドウで右クリックし、Create→Shader→Unlit Shaderを選択して、reverseible.shaderという名前で保存してください。また、作成したreversible.shaderを右クリックしてCreate→MaterialでUnlit_reversible.matというマテリアルを作成しておきます。

f:id:nn_hokuson:20180220200817p:plain:w200

続いて作成したreversible.shaderに次のプログラムを入力してください。

Shader "Unlit/reversible"
{
    Properties
    {
        _MainTex ("Texture", 2D)    = "white" {}
        _MainTex2("Texture2", 2D) = "white"{}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Cull off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            sampler2D _MainTex2;
            float4 _MainTex_ST;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos( v.vertex );
                o.uv = v.uv;
                return o;
            }
            
            fixed4 frag (v2f i, fixed facing : VFACE) : SV_Target
            {                
                return (facing > 0 ) ? tex2D(_MainTex, i.uv) : tex2D(_MainTex2, i.uv);
            }
            ENDCG
        }
    }
}

ポリゴンの両面を表示するため、今回も「Cull off」を指定してカリングをしないように設定しています。

頂点シェーダでは頂点座標とテクスチャのUV座標を受け取り、ワールド座標に変換して出力しています。UV座標はフラグメントシェーダに受け渡しています。

フラグメントシェーダでは引数に頂点シェーダからの構造体のほか、facing引数を追加しています。このfacing > 0の場合はポリゴンが表面、facing <= 0の場合はポリゴンが裏面を向いています。

そこで、facingの値をみて、ポリゴンが表向きであれば_MainTexを使用し、裏向きであれば_MainTex2を使用しています。

マテリアルとテクスチャを割り当てる

シェーダプログラムができたら、マテリアルをPlaneにドラッグ&ドロップして割り当ててください。

f:id:nn_hokuson:20180220201116j:plain

また、マテリアルには表面用のテクスチャを裏面用のテクスチャを指定する必要があります。ヒエラルキーウインドウでPlaneを選択した状態で、インスペクタの_MainTexと_MainTex2にテクスチャをドラッグ&ドロップしてください。

f:id:nn_hokuson:20180220201822j:plain

実行して、Planeを回してみるとポリゴンの表面と裏面で別々のテクスチャが表示されていることが確認できると思います。

f:id:nn_hokuson:20180220202752g:plain:w500

7日間でマスターするUnityシェーダ入門

この記事は、これまでにおもちゃラボで紹介してきたUnityのシェーダ入門記事40本のまとめです。

1日に5記事読めば7日間で読み切れるはず...今のところ(笑)

シェーダって時々聞くけど難しそう・・・というイメージをお持ちの方も多いと思います。でも、Unityを使えばかなりのメンドウな部分はUnityにおまかせできちゃうので、本当に必要な部分のシェーダを書くだけでイメージ通りの絵作りができるようになります。

f:id:nn_hokuson:20180214201002j:plain

Unityで使えるシェーダにはsurfaceシェーダと頂点/フラグメントシェーダの2種類があります。ここではこの2つのシェーダの他、ライティング・ポストエフェクトの内容も解説しています。それぞれの各記事へのリンクを下にまとめておきます。これからもドシドシ追加予定です!

surfaceシェーダ入門

【Unityシェーダ入門】透明なシェーダを作る - おもちゃラボ
【Unityシェーダ入門】氷のような半透明シェーダを作る - おもちゃラボ
【Unityシェーダ入門】リムライティングのシェーダを作る - おもちゃラボ
【Unityシェーダ入門】テクスチャを表示する - おもちゃラボ
【Unityシェーダ入門】ステンドグラスのシェーダを作る - おもちゃラボ
【Unityシェーダ入門】uvスクロールで水面を動かす - おもちゃラボ
【Unityシェーダ入門】テクスチャをブレンドして自然な地形を表示する - おもちゃラボ
【Unityシェーダ入門】円やリングをかっこよく動かす方法 - おもちゃラボ
【Unityシェーダ入門】シェーダで作るノイズ5種盛り - おもちゃラボ
【Unityシェーダ入門】粘性のある液体をシェーダで作る - おもちゃラボ
【Unityシェーダ】テクスチャの両面を描画する方法 - おもちゃラボ
【Unityシェーダ入門】トゥーンシェーダを自作してみる - おもちゃラボ
【Unityシェーダ入門】頂点カラーを表示するシェーダを作る - おもちゃラボ
【Unityシェーダ入門】シェーダで旗や水面をなびかせる - おもちゃラボ
【Unityシェーダ入門】Dissolve(溶けるような)シェーダをつくる - おもちゃラボ
【Unityシェーダ入門】シェーダを使って世界に雪を降らせよう - おもちゃラボ
【Unityシェーダ】オブジェクトが重なった部分をくり抜く - おもちゃラボ
【Unityシェーダ入門】ポリゴンをポイント(点)で表現する - おもちゃラボ
【Unityシェーダ入門】スパイクノイズを作る - おもちゃラボ
【Unityシェーダ入門】綺麗に半透明のモデルが表示できるシェーダを作る - おもちゃラボ

シェーダプログラミングおすすめ参考書

シェーダで使う数学や物理の理論から、シェーダプログラミングの基礎、グラフィックスプログラミング、画像処理までひとつひとつ丁寧に解説してくれている超良書。使用言語はDirectXだけれども、Unityでも普通に役立ちます!ただ絶版・・・

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

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

Unityのシェーダを解説している本の中では一番サンプルが豊富で分かりやすいです。surfaceシェーダも頂点・フラグメントシェーダだけでなくポストエフェクトについても解説されています。洋書なので英語ですが、だいたい雰囲気でわかります(笑)
Unity 5.x Shaders and Effects Cookbook

Unity 5.x Shaders and Effects Cookbook

シェーダとGPUは切っても切り離せない関係。グラフィックスプログラミングをやる人は読んでおきたい一冊。英語で良ければ無料で公開されていますが、上のUnityシェーダ本と違って結構ボリュームがあるから、できれば日本語で読みたい・・・
GPU Gems 3 日本語版

GPU Gems 3 日本語版

シェーダは出てこないけど拙書「Unityの教科書」もよろしくです!
Unityの教科書 Unity 2017完全対応版  2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

Unityの教科書 Unity 2017完全対応版 2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

【Unity】四角形のポリゴンを作ってテクスチャを貼る

UnityにはPlaneなど、板ポリ用のポリゴンが用意されています。大概の場合はこれらのポリゴンを利用すれば十分ですが、ゲーム実行時に動的にポリゴンを作りたい場合にはスクリプトから作ることもできます。

ここではスクリプトを使って四角形のポリゴンを作成し、そこにテクスチャを貼る方法を紹介したいと思います。想像よりかは面倒だと思います(笑)ただ、応用すれば(そして、頑張れば・・・)板ポリゴンだけでなく、3Dモデルも作れるようになったりします。

四角形のポリゴンを作るのに必要な事前知識

御存知のとおり、ゲームで使う3Dモデルの最小単位は三角形のポリゴンです。四角形ポリゴンを書く場合も三角形のポリゴンを2つ描くことになります。

f:id:nn_hokuson:20180213185922p:plain:w300

ポリゴンを描くために必要な情報は次の3つです。

  • 頂点座標
  • UV座標
  • インデックス

頂点座標

ひとつ目の頂点座標は名前の通りポリゴンに使われている頂点の座標です。この頂点座標を頂点数ぶん配列で用意します。四角形なら4個ですね。

UV座標

ふたつ目UV座標はテクスチャの座標になります。頂点ごとにテクスチャのどの部分を使うかをテクスチャ座標(UV座標)を使って指定します。UV座標は0〜1の範囲で指定します。こちらも頂点数ぶん必要になります。

f:id:nn_hokuson:20180213194517p:plain:w400

ちなみに・・・モデルはキイロイトリです。北海道の雪の上で撮影しました(笑)

インデックス

最後のインデックスは聞き慣れないかもしれません(OpenGLやWebGL、DirectXなどを使っていた方にはおなじみですが・・・)頂点配列の何番目のデータを使ってポリゴンを描くかを指定します。

四角形を描く場合、三角形ごとに頂点座標を指定した場合、指定しなければいけない頂点座標は6点必要になります。

f:id:nn_hokuson:20180213190042p:plain:w400

2頂点ぶんはデータがかぶってしまいますね。これではメモリの無駄遣いなので、頂点配列は4頂点ぶんだけ用意して、その中の何番目のデータを使用するかだけを指定する方式がよく使われます。この使用する頂点データの番号をインデックスとして指定します。

f:id:nn_hokuson:20180213190104p:plain:w500

四角形のポリゴンを作成する

まずは四角形ポリゴンを描画するためのGameObjectを作りましょう。ヒエラルキーウインドウからCreate→Empty Objectを選択して空のゲームオブジェクトを作ります。名前はQuadに変更しておきましょう。

f:id:nn_hokuson:20180213190122p:plain:w300

Unityでポリゴンを表示するためにはMesh FilterとMesh Rendererコンポーネントが必要になります。インスペクタからAdd Componentでこれらのコンポーネントを追加してください。

f:id:nn_hokuson:20180213190133p:plain:w420

続いて四角形ポリゴンを動的に生成するためのスクリプトを作成しましょう。プロジェクトウィンドウで右クリックしてCreate→Script→C# Scriptを選択し、ファイル名をQuadGen.csに変更しましょう。

スクリプトファイルが作成できたら、次のスクリプトを入力してください。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Quad : MonoBehaviour 
{
    public Material mat;

    void Start () 
    {
    Mesh mesh = new Mesh ();
    mesh.vertices = new Vector3[] {
        new Vector3 (-1.2f, -1.2f, 0),
        new Vector3 (-1.2f,  1.2f, 0),       
        new Vector3 (1.2f , -1.2f, 0),
        new Vector3 (1.2f ,  1.2f, 0),
    };

    mesh.uv = new Vector2[] {
        new Vector2 (0, 0),
        new Vector2 (0, 1),
        new Vector2 (1, 0),              
        new Vector2 (1, 1),
    };

    mesh.triangles = new int[] {
        0, 1, 2, 
        1, 3, 2,
    };
    GetComponent<MeshFilter> ().sharedMesh = mesh;
    GetComponent<MeshRenderer> ().material = mat;
    }
}

このスクリプトでは頂点座標を格納するvertices配列、UV座標を格納するuv配列、インデックスを格納するindex配列を用意しています。

ここでは次のように(-1.2f,-1.2f)から(1.2f, 1.2f)まで、2.4m四方の正方形のポリゴンになるように座標を指定しています。ここでは、頂点座標は「左下」→「左上」→「右下」→「右上」の順番(ピンク色のインデックス)で宣言しています。

f:id:nn_hokuson:20180213190330p:plain:w300

uv配列には頂点座標と対応したUV座標を指定します。ここでは次のような対応になるようにUV座標を指定しました。UV座標の順番は各頂点の宣言の順番と対応するように「左下」→「左上」→「右下」→「右上」の順番で宣言していることに注意してください。

f:id:nn_hokuson:20180213191626j:plain:w300

またまた登場、キイロイトリ(笑)

Rilakkuma (リラックマ) 忍者 あつめて ぬいぐるみ キイロイトリ

Rilakkuma (リラックマ) 忍者 あつめて ぬいぐるみ キイロイトリ


最後にインデックス配列です。今回は次のように2つの三角形を作ります。ひとつ目の三角形のインデックスは「0, 1, 2」、2つめの三角形のインデックスは「1, 3, 2」になります。

f:id:nn_hokuson:20180213195308p:plain:w300

ここでふたつ目の三角形のインデックスを「1,2,3」ではなく「1,3,2」としていることに注意してください。

インデックスは必ず「時計回り」になるように指定する必要があります。反時計回りで指定すると法線が逆向きのポリゴンが生成されて、表示されていないように見えてしまうので注意してください。

f:id:nn_hokuson:20180213190655p:plain:w500

最後に頂点座標・UV座標・インデックスの3つの情報をMeshオブジェクトにセットし、MeshFilterコンポーネントのshaderedMesh変数に登録しています。同時に表示するテクスチャのマテリアルも登録しています。

四角形のポリゴンを表示してみる

ここまでで四角形のポリゴンを作成するスクリプトができました。一旦動作を確認するために、QuadGen.csスクリプトをヒエラルキーウインドウのQuadにドラッグ&ドロップしてください。

f:id:nn_hokuson:20180213192614j:plain

実行すると次のようにピンク色の四角形が表示されると思います。これはまだテクスチャを指定していないことが原因です。

f:id:nn_hokuson:20180213192639p:plain:w350

テクスチャをセットして表示する

そこで、テクスチャを指定したマテリアルを作りましょう。プロジェクトウィンドウでCreate→Materialを選択し、インスペクタのShaderを「Unit/Texture」に変更して、使用するTextureをドラッグ&ドロップしてください。

f:id:nn_hokuson:20180213193013j:plain

最後に作成したテクスチャのマテリアルをQuadGen.csに指定します。ヒエラルキーウインドウでQuadを選択して、インスペクタからQuadGenスクリプトのmat欄を探します。そこに作成したテクスチャのマテリアルをドラッグ&ドロップしてください。

f:id:nn_hokuson:20180213193221j:plain

実行するとちゃんとテクスチャが表示されているのが確認できるはずです。

f:id:nn_hokuson:20180213193245p:plain:w350

キイロイトリ、めっちゃカワイイですね(もうええっちゅーねん)

対策とまとめ

もしもうまく表示されていないときは・・・

  • UV座標の順番は頂点座標と対応しているか
  • インデックスの指定順序は正しいか
  • インデックスは時計回りに指定したか

などを確認してみてください。

今回はスクリプトを使って四角形の板ポリゴンを作成してみました。使う機会は多くないかもしれませんが、動的にポリゴンを作りたい場合に役に立つので覚えておきたいですね!

Unityの教科書 Unity 2017完全対応版  2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)

Unityの教科書 Unity 2017完全対応版 2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)