2020年2月21日金曜日

最適化とか

 ジワジワと早くなっていってる。

 深度(スクリーン座標)


 法線ベクトル(グローバル座標)


 テクスチャID


 UVマップ


 最終出力


 トータル100msくらいから82msくらいまで短くなったので、15%くらいの高速化。
 この内、座標変換が支配的。ここだけで15msくらい稼いでる。元々は、ポリゴンをグローバル座標へ変換した後に、グローバル空間の視線ベクトルとの内積を比較していたが、ポリゴン総当たりの前に、視線ベクトルをローカル座標へ変換した上で、内積と比較して合格したポリゴンのみをグローバル座標へと変換するようにした。
 グローバル座標への変換は単純な回転以外にも、移動や拡大や様々な処理を並行するので、1回あたりのコストが高い。あらかじめ判定を行っておけば、座標変換の回数を約半分まで削減できる。
 残りは、テクスチャの取り扱いを変更して、テクセルの読み出しが微妙に早くなったかな?程度。

 逆に、処理速度の低下を招くコード変更もあった。これは保守性の向上を狙った部分で、かなり劣化したので、元のコードに戻した。
 具体的には、ポリゴンのラスタライズの処理。ラスタライズ時は、様々な線形補間を繰り返す。頂点位置やUVマップ、法線ベクトルなど。いままではVector3やVector2やfloatや、必要なものをすべて個別に書いていた。
 これを、頂点単位での変換に置き換えようとした。3種類の座標変換を2回ずつ、6行のコードが頂点の配置によって3種類あるので、18行分のコードが、1種類の変換を2回ずつ3種類で、6行と、3分の1になる。実際にはもうコードが減る。
 ただ、頂点単位ではすでに不要になったの処理も行ってしまう。例えばポリゴンを上から下にラスタライズする場合、上下ループの中では、上下方向の線形補間はもはや必要ない。それにも関わらず、頂点単位での補完ではその処理も行われてしまう。

***

 頂点ベクトルの補間の比較。補間無しと有り。









 ポストエフェクトで陰影を表現しない場合は、テクスチャの輝度がそのまま出てくるので、特に気にならない。光線ベクトルで陰影を評価する場合は、ポリゴンの法線を使うと離散的に内積が変化するので、ガタガタになる。頂点ベクトルで補間すると、連続的な内積が得られるため、なめらかな質感となる。

***

 輪郭。

 輪郭無し


 ベクトルで輪郭


 深度で輪郭(1)


 深度で輪郭(2)


 ベクトルで輪郭を作る場合、しきい値にもよるが、髪の隙間のような細かい部分にも輪郭が出やすい。逆に、法線が近い角度の部分では、遠近差があっても輪郭が出ない(前髪の目隠れ付近)。
 深度で輪郭を作る場合、谷折り部分には輪郭が出づらいデメリットがあるが、逆に言えばベクトルのように細かい輪郭が大量に出ることはないので、黒く(汚く)なりづらい利点がある。
 また、深度値や閾値のスケジューリングを工夫すれば、近い場所では浅い深度差で輪郭が出て、遠い場所では深い深度差がなければ輪郭が出ない、といった表現も可能となる(深度影1と2、首元あたり)。

 輪郭をポストエフェクトで作る場合、隣接ピクセルへのアクセスが大量に発生するので、CPUのようなシーケンシャル処理ではパフォーマンスが出づらい。GPUのように、大量のスレッドで並行してそれぞれのピクセルを参照すれば、パフォーマンスの悪化はある程度軽減できる。
 目立つ輪郭を作りたい場合、ある程度のピクセル数の範囲を参照する必要があるので、特にCPU処理は分が悪い。

***

 陰影。

 陰影なし


 線形的な陰影


 非線形な陰影


 線形・非線形共に、法線ベクトルと光線ベクトルの内積から輝度を計算する。
 非線形な陰影は、セル絵風の効果になる。見てわかりやすい影が出るが、例えば服の中のような、本来は影になるべきところが影になっていない、という部分もわかりやすく出てしまう。他にも、髪の影になるべきところが明るいとか、必要以上に細かく陰影が出てしまうとか、いろいろデメリットが有る。

 法線ベクトルを使った影生成は、内積を1回取るだけなので非常に簡単に生成できる。輪郭生成と比べ物にならないほど早い(実際には時間で5倍程度の負荷量の差)。

***

 もう一つ、輪郭アルゴリズム。

 輪郭無し


 マテリアルの違いで輪郭


 マテリアルの貼り分け(参考)


 マテリアルIDの差で輪郭を生成するアルゴリズム。依然として隣のピクセルへの参照は必要だが、整数値の比較で済むので、比較的軽量。
 髪の部分は、2種類のマテリアルを貼り分けているため、髪の周りには多めに輪郭が出ている。一方で、服は1枚のテクスチャを円筒状に張っているため、合わせ部分(ネクタイの下)には、輪郭は出ていない。また、ネクタイと服は別のマテリアルを参照しているので、陰影が出ている。
 欠点としては、この画像では隠れているが、例えば顔と胴体の継ぎ目でマテリアル(テクスチャ)が切り替わっているので、そういう部分をどう処理するかが問題になる。キャラクターによっては、チョーカーで隠すみたいな、デュラハンばりのキャラ設定が必要になるかもしれない。まさか、キャラがチョーカーをつけている理由がテクスチャの問題とは思うまい…… じつはそういうキャラそこら中にいたりして。そう、まるでこの世ならざる者が人間社会に数多く浸透しているように……
 それはさておき
 マテリアルやテクスチャで輪郭を作る場合、素材の使い方によって輪郭の出方を制御できるし、演算コストが比較的少なくて済む。継ぎ目に関しても、例えばIDの下位2bitをマスクして、4個分のマテリアルを1個の連続体として扱う、といった実装も可能なはず。

 思いつきで実装したアルゴリズムだが、思いの外性能が良くてちょっとびっくり。

***

 3Dキャラぐりぐり動かしてると、NEW GAME!の世界に入り込んだ気分。エンジンまわりいじってるから、プログラミングチームか。あ、ということは、上司にサバゲに誘われるまであるか?

 大昔にMMD落としてちょっと触ったことはあるけど、それ以外だと3Dキャラを動かすような経験はないので、結構楽しい(人物扱うのやだとかどの口が言ったんだ……)
 あまり複雑な計算はできないにしろ、本に書いてあるアルゴリズムを実際に実装して動くのを見るのも面白いし。

***

 株式会社インフィニットループのマスコットキャラクターのモデルデータ「あいえるたん3Dモデル」を使用しています。
 配布元:https://www.infiniteloop.co.jp/special/iltan/

0 件のコメント:

コメントを投稿