TT Lab
はじめる
学ぶ 学習パス コース

3D数学とソフトウェアラスタライザ

ラスタライザがやっていること

TT Labで続きを見る

一言でいうと

ラスタライザーは、3つの頂点を受け取って、その中に入るピクセルを選び出し、頂点に付いた値を重心座標で混ぜて、ピクセルごとに渡します。前後は、深度バッファーがピクセル単位で決めます。

なぜ必要なのか

前の段階までで、頂点が画面座標を持ちます。ところが、画面はピクセルの格子で、三角形の辺は、格子にぴったり合いません。どのピクセルをこの三角形のものとみなすかを決める規則が必要です。

そして、色は頂点にしかありません。三角形の内側のピクセルの色は、どこから来るのでしょうか。3つの頂点の値を、距離に応じて混ぜる必要がありますが、「距離に応じて」を正確に定義するのが、重心座標です。

最後に、物体が複数あれば、どれが前かを決める必要があります。三角形単位で並べ替えて、後ろから描く方法(画家のアルゴリズム)は、三角形が互いに貫通したり、循環して重なったりするときは、答えがありません。そのため、ハードウェアは、ピクセルごとに深度を記憶する側を選びました。

どう動くのか

重心座標は、点Pを3つの頂点の加重和P = u*A + v*B + w*Cと書いたときの、(u, v, w)です。3つの値の和は常に1で、3つがすべて0以上なら、Pは三角形の中にあります。これが、内外の判定と、値の補間を同時に解決します。

d = (By-Cy)(Ax-Cx) + (Cx-Bx)(Ay-Cy)          # 삼각형 넓이의 두 배(부호 있음)
u = ((By-Cy)(Px-Cx) + (Cx-Bx)(Py-Cy)) / d
v = ((Cy-Ay)(Px-Cx) + (Ax-Cx)(Py-Cy)) / d
w = 1 - u - v

実際の実装は、三角形のバウンディングボックスの中だけを走査します。画面全体を回るのに比べて、はるかに速く、GPUもタイル単位で同じことをします。

ラスタライザーが、三角形のバウンディングボックスの中だけを走査して、各ピクセルの中心の重心座標を求め、3つの値がすべて0以上のピクセルだけを、三角形のものとして選ぶ様子。内側と判定されたピクセルは塗られていて、外のピクセルは中心の点だけが打たれており、例に挙げたピクセル1つのu、v、wの値もあわせて書かれている

色だけでなく、深度も同じ方式で補間します。ピクセルの深度が求まったら、その位置の深度バッファーの値と比べて、より近いときだけ、色と深度をあわせて書きます。これがZバッファーで、描く順序に関係なく、同じ絵が出るようにしてくれます。順序に依存しないことが、この方法の本当の価値です。半透明でない限り、並べ替えが必要なくなります。

背面カリング(back-face culling)は、それよりも前の段階で取り除きます。画面座標で3つの頂点を回る方向が、時計回りか反時計回りかは、符号付きの面積でわかります。

signed_area = ((Bx-Ax)(Cy-Ay) - (Cx-Ax)(By-Ay)) / 2

閉じた物体なら、裏面はどのみち表面に隠れるので、この符号1つで、三角形の半分を、ラスタライズの前に捨てられます。ただで得られる2倍です。

補間には、もう1つ落とし穴があります。画面座標で求めた重心座標で、値をそのまま混ぜると、遠近が入ったシーンでは、誤った結果が出ます。画面で均等に分けた間隔が、3次元空間では均等ではないからです。色は目立ちにくいですが、テクスチャは確実にゆがみます。正しい方法は、値をwで割った状態で補間してから、最後に戻すことで、これを、透視補正補間と呼びます。シェーダーのコースで、このゆがみを目で見ることになります。

GPUが、ピクセルを1つずつ処理せずに、2x2のまとまりで処理することも、知っておく価値があります。フラグメントシェーダーが、テクスチャの詳細度を選ぶには、隣のピクセルとの差を知る必要があり、その差は、まとまりの中で求めます。そのため、三角形がとても細いと、まとまりの半分以上が、捨てられる計算になり、三角形を細かく分割するほど、この無駄が大きくなります。

現場での姿

隣り合う2つの三角形の間に、1行だけの隙間が見えるバグがあります。共有する辺の上のピクセルを、両側とも自分のものではないと判定すると生じます。逆に、両側とも自分のものだと言うと、そのピクセルが2回描かれて、半透明の合成で色が濃くなります。そのため、仕様は、フィルルール(top-left rule)を定めて、境界のピクセルの持ち主を1つに決めます。

もう1つは、Zファイティングです。2つの面がほぼ同じ深度にあると、深度バッファーの精度の中で前後が揺らいで、画面がちらつきます。解決法は、2つの面を実際に離すか、深度オフセットを与えるか、near平面を大きくすることです。

そして、背面カリングは、閉じた物体でだけ安全です。紙1枚や木の葉のように、両面とも見る必要があるものにオンにしておくと、特定の角度で、まるごと消えます。

最後に、性能の感覚を1つ付け加えておきます。ラスタライズのコストは、三角形の数ではなく、塗るピクセルの数に近く比例します。画面いっぱいを埋める大きな三角形1つが、画面の外の小さな三角形1000個より高くつきます。そのため、最適化の方向は、2つに分かれます。1つは、画面に出ないものを先に捨てること(錐台カリング、背面カリング、遮蔽判定)で、もう1つは、同じピクセルを何度も塗らないことです。後者の問題をオーバードローと呼び、不透明な物体を、前から後ろの順に描くと、深度判定が先に取り除いてくれるので、フラグメントの計算が減ります。深度バッファーが並べ替えを不要にしてくれますが、それでも、性能のためには、おおまかな並べ替えが得になるという意味です。

次のラボですること

重心座標を自分で作り、それで三角形を塗りつぶしたあと、頂点の色を補間します。次に、互いに重なる2つの三角形に深度を与えて、Zバッファーで前後を分け、深度だけを入れ替えて、絵がどう変わるかを目で見ます。最後に、符号付きの面積で、裏面を選んで捨てます。