お米 is ライス

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

【Cg Programming/Unity】Basics ~ RGB Cube【順番にやっていく】

一日坊主を何としてでも避けるため、中途半端に寝てしまって夜中に妙に冴えてしまった頭に鞭を入れつつ筆を執る。
今回はこちら。
en.wikibooks.org

ピクセルの色の指定方法

ピクセルの色はRGBAであらわされる。シェーダをやろうとしてこれ知らん人はおるまいので飛ばす。

各頂点のローカル座標によって出力する色を変える

Shader "Custom/RGBCube"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex v
            #pragma fragment f

            //頂点シェーダで値を入れてフラグメントシェーダに渡すための構造体
            struct vertexOutput
            {
                float4 pos : SV_POSITION;
                float4 col : TEXCOORD0;
                //nointerpolation float4 col : TEXCOORD0;
            };

            vertexOutput v(float4 vertexPos : POSITION)
            {
                vertexOutput output;
                output.pos = UnityObjectToClipPos(vertexPos);
                output.col = vertexPos + float4(0.5, 0.5, 0.5, 0.0);
                return output;
            }

            //やっていることはv関数とまったく同等
            void v2(float4 vertexPos : POSITION, out float4 pos : SV_POSITION, out float4 col : TEXCOORD0)
            {
                pos = mul(UNITY_MATRIX_MVP, vertexPos); // UnityObjectToClipPos(vertexPos)と同等
                col = vertexPos + float4(0.5, 0.5, 0.5, 0.0);
                return;
            }

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

            //nointerpolationを指定すると補間をせず頂点シェーダで代入された値をそのまま使用する
            //float4 f2(float4 pos : SV_POSITION, nointerpolation float4 col : TEXCOORD0) : 
            float4 f2(float4 pos : SV_POSITION, float4 col : TEXCOORD0) : COLOR
            {
                return col;
            }
            ENDCG
        }
    }
}

まずは頂点シェーダとしてv関数、フラグメントシェーダとしてf関数を見てほしい。

構造体の宣言

目立つのはvertexOutputという構造体の宣言である。
頂点シェーダで値を決定し、フラグメントシェーダに渡す値はこうして構造体にすることができる。ここではTEXCOORD0というセマンティクスで宣言した変数をRGB値の受け渡しに使っている。一般的にこのセマンティクスはテクスチャの座標指定に使用されることが多いが、どう使用するかは基本的に自由である。

このようにして宣言した構造体を頂点シェーダの返り値およびフラグメントシェーダの引数として用いれば、シェーダ間の値の受け渡しをまとめて行うことができる。

色の指定

v関数ではcolという出力に頂点のローカル座標にオフセットを足した値を代入している。
(オフセットを足しているのは、-0.5 ~ 0.5となっている値を0 ~ 1とするためである。)
フラグメントシェーダのf関数ではこの値を各ピクセルの値として出力している。

フラグメントシェーダの線形補間

さて、上記のように実装したシェーダを用いてCubeを描画してみよう。
すると以下のような見た目になる。
f:id:spi_8823:20200503033055p:plain
見ればわかるように、各頂点から他の頂点に向かって徐々に色が変わって描画されている。
頂点シェーダでは各頂点の座標、つまり離散的な値を出力したはずなのになぜこのような見た目になるのか。
フラグメントシェーダでは3つの頂点で定められる"面"に含まれる"点"が描画される。したがってフラグメントシェーダでは3つの頂点の出力をミックスした値が引数として渡されているのだ。

別の書き方

v関数とv2関数、f関数とf2関数はそれぞれ全く同じ処理を書いている。

構造体でやりとりしていた値は頂点シェーダ側ではout引数に、フラグメントシェーダ側では通常の引数に指定してやることでもやりとりすることができる。

また、v関数でUnityObjectToClipPos関数を用いて計算していた各頂点のクリッピング座標は、mul(UNITY_MATRIX_MVP, vertexPos)のように変換行列と頂点のローカル座標をかけたものに等しい。
(ちなみにこちらの方法で書くとUnityが勝手にUnityObjectToClipPosのほうに書き換えてしまう)

線形補間をあえてさせない

フラグメントシェーダに渡される引数の値は3つの頂点の値を線形補間したものだった。
col変数の各宣言の個所でnointerpolationというオプションを指定することによって線形補間しない値を使用することができる。
(コード中にはコメントアウトして書いてあるので適宜入れ替えて実行されたし)
このようにして描画を行ったものは以下のような見た目になる。
各頂点座標がそのまま使用されているのがわかる。
f:id:spi_8823:20200503035036p:plain

余談

和訳するのが面倒なので本家のページの内容そのままではないけど許してくれ~



次はこちら。
spi8823.hatenablog.com