1 / 4 頂点の法線とWorld空間への変換
このステップでは、法線を頂点属性として受け取り、World空間へ変換してフラグメント関数へ渡す一連の流れを学びます。
位置と法線の役割を分ける
メッシュは、頂点ごとに位置やUV、法線などの属性を持っています。法線(Normal)とは、照明の計算などで利用する面の向きを表すベクトルです。曲面を滑らかに見せたいメッシュでは、隣り合う頂点ごとに少しずつ異なる向きの法線が設定されています。
同じ位置にある頂点であっても、角をくっきり見せるために法線の向きを分ける場合があります。したがって、シェーダーは頂点位置から法線を推測するのではなく、メッシュにあらかじめ用意された法線を受け取って使用します。
入力と出力に法線を加える
LambertStudy.shaderのAttributesとVaryingsで、それぞれuvの宣言の直後に法線用のメンバーを追加します。
struct Attributes
{
float3 positionOS : POSITION;
float2 uv : TEXCOORD0;
float3 normalOS : NORMAL;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normalWS : TEXCOORD1;
};入力のNORMALは、メッシュの法線を受け取るためのセマンティクスです。normalOSのOSは、値がObject空間で表されていることを示しています。
出力のnormalWSは、World空間の法線を渡すために付けた名前です。TEXCOORD1は、この値を補間して渡すためのスロットを指定しています。TEXCOORDという名前が付いていても、渡す値をUV座標に限定する必要はありません。ここではすでにTEXCOORD0をUV座標用に使用しているため、法線にはTEXCOORD1を使います。
法線に適した変換を使う
頂点関数 Vert() で、UVを設定する行と return output; の間に次の処理を追加します。法線をObject空間からWorld空間へ変換します。
output.uv = input.uv * _BaseMap_ST.xy + _BaseMap_ST.zw;
output.normalWS = TransformObjectToWorldNormal(input.normalOS);
return output;TransformObjectToWorldNormalは、Object空間の法線をWorld空間へ変換する関数です。この関数は、オブジェクトの回転や拡大縮小を考慮し、面に対応する正しい法線の向きを求めます。
法線の変換には、位置をClip空間へ変換するTransformObjectToHClipを使いません。位置を画面へ配置する処理と、照明用にベクトルの向きをWorld空間へそろえる処理とでは、求める結果や目的とする空間が異なるためです。
補間後に正規化する
GPUは、頂点関数から渡された法線を三角形の内部で補間します。頂点ごとの法線の長さが1であっても、補間した結果のベクトルの長さは必ずしも1にはなりません。
そのため、フラグメント関数で使用する直前に法線を正規化する必要があります。
float3 normalWS = normalize(input.normalWS);normalizeは、向きを保ったままベクトルの長さを1にします。照明計算では純粋に向きの差を比べたいので、法線の長さの違いが明るさの計算結果へ混ざらないようにします。
法線を色で確認する
法線が正しく取得できているか確かめるために、フラグメント関数 Frag() の本文を次の2行へ置き換えます。直前の normalWS の宣言はこの中に含まれるので、重複して追加しません。
float3 normalWS = normalize(input.normalWS);
return float4(normalWS * 0.5 + 0.5, 1.0);法線の各成分は、向きによって負の値をとる場合があります。式の* 0.5 + 0.5は、成分の範囲をおおむね−1〜1から0〜1へ変換し、向きをRGBカラーとして観察しやすくするための工夫です(この段階での色は照明計算の結果ではありません)。
オブジェクトを回転させて、色の変化を観察してみましょう。World空間で面の向きが変わると、それに応じて法線を表す色も変化することが確認できます。
確認後はこの色表示を残し、次のステップで照明の白黒表示へ置き換えます。比較用にオブジェクトの回転は変更前へ戻してください。
- 入力の
NORMALと出力のTEXCOORD1の役割を正しく区別できていますか。 - 位置と法線に対して、それぞれ適切な変換関数を使い分けていますか。
- 補間後の法線を正規化する理由を説明できますか。
- 法線の色表示を使って、面の向きが正しく取れているか確認できましたか。