お米 is ライス

C#やらUnityやらを勉強していて、これはメモっといたほうがええやろ、ということを書くつもりです

【Cg Programming/Unity】Basics ~ Shading in World Space【順番にやっていく】

今回はこれ
en.wikibooks.org
この節の目的は、頂点シェーダにおいて頂点のローカル座標をワールド座標に変換する方法を通して、その過程で用いる「uniform変数」を理解すること。

オブジェクトの座標をワールド座標に変換する

これまで頂点シェーダにおいて : POSITIONセマンティクスを付与することによって受け取っていた座標は頂点の相対的な座標、つまりローカル座標である。
これを世界に対する絶対的な座標、つまりワールド座標に変換することを、頂点に対して4x4の正方行列をかけることによって実現する。
(座標は3次元なのに4x4行列なの?と思うかもしれないが、簡単に言うと回転用に3、平行移動用に1が用意されていると理解するとよい)

モデル行列

この時に用いる4x4正方行列を「モデル行列」と呼ぶらしい。
(モデルを変換する行列だからこう呼ぶらしいが、ちょっとミスリーディングな名前と感じる)
このモデル行列は自前で計算せずともTransformコンポーネント側で計算してくれていて、シェーダからはunity_ObjectToWorldという変数を参照することで使用することができる。

uniform変数

unity_ObjectToWorld変数のように(簡単に言うと)各頂点における値が共通のものを「uniform変数」と呼ぶ。
The Book of Shaders: uniforms
uniform変数を使うために本来はuniform float4x4 unity_ObjectToWorldというuniform変数の宣言が必要であるが、unity_ObjectToWorldのようによく使用される変数に関してはUnity側で自動で宣言されているので自前で宣言する必要はない。

コード

Shader "Custom/ShadingInWorldSpace"
{
    Properties
    {
        //Materialの設定から変更するためにはここで定義しておく
        _ColorOffset ("offset of color", Color) = (0, 0, 0, 0)
    }
    SubShader
    {
        pass
        {
            CGPROGRAM
            #pragma vertex v
            #pragma fragment f

            //Unityが宣言してくれているので宣言しなくてよい
            //(宣言するとエラー)
            //uniform float4x4 unity_ObjectToWorld;

            //Propertiesで定義した変数をシェーダ側で使用するため、
            //ここでuniform変数宣言する
            uniform float4 _ColorOffset;

            struct vertexOutput
            {
                float4 position : SV_POSITION;
                float4 color : TEXCOORD0;
            };

            vertexOutput v(float4 vertexPos : POSITION)
            {
                vertexOutput output;
                output.position = UnityObjectToClipPos(vertexPos);
                output.color = mul(unity_ObjectToWorld, vertexPos) + _ColorOffset;  //uniform変数を使用して出力する色を決定する
                return output;
            }

            float4 f(vertexOutput input) : COLOR
            {
                return input.color;
            }
            ENDCG
        }
    }
}

output.color = mul(unity_ObjectToWorld, vertexPos)のように書けば、RGBCubeの節で各頂点のローカル座標を色として出力していた代わりに、各頂点のワールド座標を色として出力することができる。
以下が実際に描画したもの。CubeをY軸に45度回転させ、少しX軸正方向に移動させてあるので、赤の成分が角に向かって全体的に多くなっている。
f:id:spi_8823:20200504235633p:plain

Properties

ところで、unity_ObjectToWorldとは別にもう一つ_ColorOffsetというuniform変数を使っているのに気付いただろうか?
この変数は私が自分で勝手に定義したもので、どこで定義しているかというとコードの上のほう、Propertiesという部分である。
ShaderLab記法ではこのようにPropertiesの中で定義すればその値をインスペクタからMaterialの変数として操作することができる。
多分アンダーバー始まりで命名しないと怒られる。
上の状態のCubeに対して_ColorOffsetを(1, 1, 1, 0)とすると以下のような見た目になる。座標が負の部分以外は真っ白になっている。
f:id:spi_8823:20200504235806p:plain

以下のように書くとインスペクタだけでなくC#コードからも値を制御することができる。

GetComponent<Renderer>().sharedMaterial.SetColor("_ColorOffset", new Color(0, 0, 0, 0));

その他のuniform変数

Unityにおいてデフォルトで定義されているuniform変数はほかにも色々ある。
いっぱいあるが、ここではごく一部だけ紹介する。

float4 _Time

_Time.yでゲームが始まってからの経過時間が取得できる。なぜfloat4になっているのかというと、経過時間に1/20, 1, 2, 3をかけたものがそれぞれx, y, z, wに入っているからだ。これらの変数はシェーダの中でアニメーションをするのに使用できる。
他にも_SinTime(1/8, 1/4, 1/2, 1)や_CosTime(左に同じ)で時間の正弦、余弦をとったものが参照できたり、unity_DeltaTimeで前のフレームからの経過時間が取れたりする。

float4x4 UNITY_MATRIX_MVP

UNITY_MATRIX_MVPという変数名で頂点のローカル座標をクリッピング座標に変換する行列が取得できる。
mul(UNITY_MATRIX_MVP, vertexPos)としてやればUnityObjectToClipPos(vertexPos)と等価なやつだ。
他にも、ワールド座標をクリッピング座標に変換するUNITY_MATRIX_VPなどの行列が定義されている。
(なんとなく察したと思うがMがモデル行列、Vがビュー行列、Pがプロジェクション行列であり、MVなど2つ組み合わせたものやPのようにプロジェクション行列のみのものもある)

Unityで定義されているuniform変数は以下の公式ドキュメントでまとめられているので詳しくはそちらを参照されたし。
docs.unity3d.com


以上。
ようやっと基礎編が終わったぜよ~~。
次はこちら。
spi8823.hatenablog.com