(In)Consistent Normal Interpolation

ポリゴンモデルの、grazing angle付近の、法線の処理についてです。

参照

Consistent Normal Interpolation[William et al. 2010], SIGGRAPH Asia 2010

裏返る法線

PixelShaderでシェーディングを行う際に、BackfaceCullingを行っていると、視線から180度を超える、裏側の法線を持ったピクセルはシェーディングの計算対象から除外して考えてしまいがちですが、実際には、Phongで法線を補完すると、裏側の法線を持ったピクセルは、意外とたくさん存在します。
これは、頂点法線と、ポリゴンの面法線が異なる向きを持った時点で不可避なもので、この二つのベクトルが乖離するほど、裏側のピクセルも増えていきます。
この問題はよく知られていますが、放置されていたり、シェーディングの計算でクリップすることで対処されています。

鏡面反射ベクトル

鏡面反射ベクトルは、法線をはさんで、入射ベクトルと反射ベクトルが、それぞれ法線に対して等しい角を持ちます。
しかし、この計算を行うときに、Phongで補間された法線を用いると、反射ベクトルがポリゴン面の中に入り込んでしまうことがあります。
ここでは、これを避けるように、法線を補正する計算する方法を考えます。

補正角α

ここで、ポリゴンの面法線と、Phongで補間された法線の成す角をαとすると、Phongで補間された法線を用いた際の反射ベクトルと、法線の成す角は、(PI/2 – α)より小さい必要があります。これ以上角度が大きくなれば、反射ベクトルがポリゴン面の中に入り込んでしまう恐れがあります。

一方で、入射ベクトルと、Phongで補間された法線の成す角は、(PI/2 + α)が最大値となります。これを超えれば、面法線が裏になる為、ピクセルは描画されません。また、このときの、cos(PI/2 + α)の値が余弦の最小値となります。
視線ベクトルと、Phongで補間された法線の成す角は、(0)が最小値となります。cos(0)の値が余弦の最大値となります。

ここで、補正された法線との余弦を計算する関数を定義するためには、cos(0)~cos(PI/2 + α)の間で連続な関数を考える必要があることが分かります。
入射ベクトルと、Phongで補間された法線の成す角が(0)のときは、余弦が最大値になる場合で、ここでは値を変更する必要がありません。
次に、入射ベクトルと、Phongで補間された法線の成す角が(PI/2 + α)となる場合を考えると、このときの補正された法線と反射ベクトルの成す角は、(PI/2 – α)である必要があります。
これを元に、余弦の変換関数を考えると、
cos(PI/2 – α)はsin(α)なので、
sin(α)/(-sin(α)) を乗算すれば、cos(PI/2 + α)をcos(PI/2 – α)に換算することが出来ます。
このままでは、cos(0)~cos(PI/2 + α)の全域にうまく適用できないので、関数に下駄を履かせ、
1 + (1-sin(α))/ (1+sin(α))(cos(θ)- 1)
という式にします。θは、入射ベクトルと、Phongで補間された法線の成す角です。
こうすることで、余弦の変換関数が出来ます。
この、(1-sin(α))/ (1+sin(α))の係数項は、計算コストの低い式で近似できます。(参照論文を参照して下さい。)

αの値の計算方法

αの値は、ジオメトリの頂点上で、隣接するポリゴンの面法線をチェックすることで計算可能ですが、ポリゴンの面上では計算することは出来ません。
従って、頂点属性の値として投入して、平面上で補間するのですが、αの値を平面上で補間すると、平面全域で、αの値の条件を満たすことが出来なくなってしまいます。
(単純な補間では、反射ベクトルが面法線の中に入り込んでしまう)
そのため、αの値にバイアスを掛けます。この計算はの導出は複雑ですが、結果的には比較的簡単に計算可能です。(参照論文を参照して下さい。)

実際の計算

バイアスを掛けてある、αを頂点ごとに事前計算。
頂点属性として投入したαを用いて、余弦の換算関数の係数項を計算。
これを元に、必要に応じ、反射ベクトルや、法線ベクトルの補正を行うことが出来る。
(参照論文を参照して下さい。)

実用上の問題

上記の計算では、面法線と頂点法線の乖離角は、PI/2以下であると仮定して計算してきました。この仮定は、計算上必要で、面法線と頂点法線の内積が負になる(乖離がPI/2以上になる)ことは避けなければなりません。
そのため、実際の使用においては、全ての頂点が、この条件を満たすように、頂点法線を事前に補正する必要があります。
大抵のモデルでは、この条件を満たすベクトルが存在するので、そうなるように補正します。
(論文では、内積が負になる面から、頂点法線を追い出す処理を再帰的に適用して求めています。)

考察

ここまでの計算を用いれば、反射ベクトルのみでなく、法線ベクトルの修正も可能なことから、単なるBlinnPhongのシェーディングですら、シェーディングの向上が望めます。
この補正の効果は、粗いメッシュで曲面を表現した場合(面法線と頂点法線の乖離が大きい場合)に効果が大きく、逆に、使用しているポリゴンメッシュが精細であれば、あまり効果がありません。
また、フレネル項のような、grazing angleで大きく値が変化する項は、この補正の影響を大きく受けると思われます。

この手法は、αの値と、入射ベクトルによって、補正された法線の向きが変わるため、頂点法線を正確にそろえたモデルを用意しても、モデルの形状により、各頂点でαの値が変化し、ベクトルの補正量が変化します。
従って、アーティストが積極的に法線を変更するケースでは、この手法は使えないと思われます。

また、入射するベクトルの影響を受けるため、複数のライトを計算するときに、ライトの方向を入射ベクトルとする場合は、ライト間で、法線の方向が一貫性を欠きます。したがって、補正に使用する入射ベクトルは、事実上、視線ベクトルに固定されると思います。

静的なモデルであれば、αの値は、コンバーターなどで事前計算を行うことで、比較的容易に算出可能だと思われます。格納情報も、スカラー値が1要素なので、帯域やメモリに対する影響は軽微だと思います。
補正計算は、PixelShaderでそれなりの計算が発生しますが、最近のGPUの傾向からすると、決して非現実的な計算量ではありません。

一方、動的なモデル(頂点Skinningなどを適用されたモデル)では、一変してしまいます。
動的なモデルで、法線の再計算を面法線から行っているケースでは、追加する必要の処理は、αの算出と格納のみですが、そうでない場合は、追加の処理と計算量は多く、導入には検討が必要だと思います。

この手法では、頂点周辺のポリゴンの面法線から最適な補正量を求めているため、参照するデータ量や、計算量は多いですが、座標変換時に、頂点単位で、視線ベクトルとのチェックを行い、補正するなどの方法でも、簡易的にですが、裏側の法線を対処することは可能です。

コメントを残す