1. サイトトップ
  2. ブログ
  3. Unity
  4. 【Unity】TEXCOORDのz,wについて(スクリプト編)

【Unity】TEXCOORDのz,wについて(スクリプト編)

はじめに

こんばんは、代表の堂前です!

今回はUnityのシェーダのTEXCOORDについて取り上げようと思います。
Unityのシェーダを扱う上での問題点と、その回避の方法になります。

※検証したのはMacのUnity5.4.1系になります。


UnityのTEXCOORDの現状

Unityに関わらずシェーダにおいて、頂点シェーダへの入力は表現のための大事な要素になっており、座標(POSITION)や法線(NORMAL)、カラー(COLOR)の他にUV座標(TEXCOORD)で構成し、情報を頂点シェーダに渡します。

POSITIONやNORMAL、COLORなどはその命名などもあり、用途が割と限定されがちですが、TEXCOORDは用途がUV座標だけに関わらず、トリッキーな処理を行う場合でも強く活用しやすいです。
(しかも複数のTEXCOORD所持が可能です。)

そんな便利なTEXCOORDなのですが、Unityの頂点情報(Mesh)として与えられる各種情報は以下のようになっています。

Meshのメンバシェーダ入力要素備考
verticesPOSITIONx,y,z,w 
colors
colors32
COLORx,y,z,w 
normalsNORMALx,y,z 
tangentsTANGENTx,y,z,w 
uvTEXCOORD0x,y・基本はテクスチャ貼り付け用UVとして。
uv2TEXCOORD1x,y・ライトマップ利用時はこれが使われる。
uv3TEXCOORD2x,y 
uv4TEXCOORD3x,y 

TEXCOORDに関して言えば、Unityは4セット用意してあるのですが、それぞれがx,yしか利用できません!
更にuv,uv2は割と用途が限定されがちなので、残りはuv3,uv4で、x,y,x,yの4要素しか残りません。
これはベクトル1つ分なので、非常に心許ないです。

UV座標が4セットなのは様々なスペックの互換を考えると致し方ないところがありますが、だとしてもそれぞれの後半のz,wも使わせてもらえるとわずかですが余裕も出てくると思います。


Mesh.SetUVsの利用

そんな中、少し古い話題にはなりますが、5.2.0よりMesh.SetUVsという関数が追加されました。
これを読むとx,yのみ、つまりVector2以外にもVector3やVector4も利用できそうという感じのものになってます。

これなら1つのTEXCOORDに4要素(x,y,z,w)持たせられるかもしれませんね。
早速検証しましょう。

unity_uv4_cube_before

適当なメッシュ(今回はキューブ)を用意し、それに以下のStart関数を含めたスクリプトを付与してみましょう。
全ての頂点のTEXCOORD3に(0, 0, 1, 0.5)を与えるものです。

void Start ()
{
    // Meshを取得する
    MeshFilter  mf   = GetComponent<MeshFilter>();
    Mesh        mesh = mf.mesh;

    // Vector4形式のuv4(TEXCOORD3)を作る
    List<Vector4>   uv4 = new List<Vector4>();
    for (int h = 0;h < mesh.vertices.Length;++h) {
        uv4.Add( new Vector4( 0.0f, 0.0f, 1.0f, 0.5f ) );       // z,wが(1.0, 0.5)の状態
    }

    // Meshにuv4(TEXCOORD3)を追加する
    mesh.SetUVs( 3, uv4 );
}

次に下の様なシンプルなシェーダを作成し、キューブにマテリアルとして割り当てます。(Unlitを基にして改造・一部のみ抜粋)

struct appdata
{
    float4 vertex : POSITION;
//  float2 uv     : TEXCOORD0;
    float4 uv4    : TEXCOORD3;      // 追加分!
};

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

sampler2D _MainTex;
float4 _MainTex_ST;

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
//  o.uv  = TRANSFORM_TEX(v.uv, _MainTex);
    o.uv4 = v.uv4;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
//  fixed4 col = tex2D(_MainTex, i.uv);
    fixed4 col = fixed4( i.uv4.z, i.uv4.w, 0, 1 );      // z,wをR,Gにしてみる
    return col;
}

この状態でUnityを実行しますと、先程のキューブが下の様にオレンジ色に変化します。
これはTEXCOORD3:(0, 0, 1, 0.5)のzとwをそれぞれR、Gにカラー出力しているのですが、R=1.0、G=0.5、B=0.0となりオレンジ色となる道理です。

unity_uv4_cube_after

これより、Mesh.SetUVsを使えばTEXCOORDにも4要素で渡せそうなのが分かりました!
何かの時には役に立ちそうです。


問題点

これはTEXCOORDの4要素化に限った話ではありませんが、各頂点情報の精度は気にした方が良いでしょう。
例えば先のTEXCOORDに、異様な大きさの数値を入れる時などです。

「Player Settings」に「Vertex Compression」という項目があります。

unity_uv4_vertex_compression

これのチェックが入っているものはサイズを小さくします。(うろ覚えですがhalf相当になります。)
ですのでそれだと困る場合、こちらも適切な設定を行う必要が出てきます。

あと今回はMeshをスクリプトで加工して〜という処理になりました。
そのため一時的にメモリを消費する&処理負荷が掛かります。

本当はfbxで既に4要素化され、Unityにインポートすればそのまま4要素で利用出来るというのがベストですが、、、後日、その検証もご紹介できたらと思います。
(もし出来なかったらゴメンなさい。)


【免責事項】
本サイトでの情報を利用することによる損害等に対し、株式会社ロジカルビートは一切の責任を負いません。