コンピュータグラフィックスの短期コース:簡略化されたOpenGLを自分で作成する、記事4c of 6

メインコースの内容







コード改善










公式の翻訳(少し磨き上げたもの)はこちらから入手できます。






新しいラスタライザーと透視歪み補正



今日の会話のトピックは、補間歪みの補正です。床のテクスチャリングの違いを見てください。







ライティング、法線などに関係するレンダリングからすべてを具体的に削除し、テクスチャのみを残しました。 MrShoorのおかげで、私は怠け者でこの修正をしませんでしたが、結局彼のキックのおかげで混乱しました。 古いバージョンのラスタライザでは面倒でしたが、新しいバージョンでは非常に簡単です。



したがって、新しいラスタライザの仕組みから始めます。このためには、重心座標を操作できる必要があります。



2次元の三角形の点の重心座標を見つける



2次元の三角形ABC、ポイントPが与えられ、すべてデカルト座標である。 私たちのタスクは、三角形ABCに対する点Pの重心座標を見つけることです。 これは3つの数字(1-uv、u、v)であり、これを使用して点Pを見つけることができます。







これは、三角形の対応する頂点に重み(1-uv、u、v)を置くと、システムの重心が点Pにあることを意味します。点Pの座標(u、v )フレーム内(A、 ABAC ):







したがって、ベクトルAB、AC、APが与えられた場合、次の方程式に対応する2つの実数u、vを見つける必要があります。







これは、2つの常用方程式のシステムに相当するベクトル方程式です。







2つの未知数を持つ2つの線形方程式のシステム、この全体は非常に簡単に解決されます。 私は怠け者です。正直に解決策を導きたくありません。次のように解決しましょう。 システムをマトリックス形式で書き直します。







これは、2つの与えられたベクトル(ABx、ACx、PAx)および(ABy、ACy、PAy)に同時に直交するベクトル(u、v、1)を探していることを意味します。 私が得ているものをすでに理解しましたか? そうです、ベクトル乗算(ABx、ACx、PAx)x(ABy、ACy、PAy)と結果の3番目の成分で除算するだけです。



これは小さなヒントです。2dでは、2つの線の交点(ちょうどそれを見つけたところ)が1つのベクトル積と見なされます。 ちなみに、与えられた2点を通る直線の方程式を見つけることはまったく同じとみなされます!



新しいラスタライザー



それでは、新しいバージョンのラスタライザをプログラムしてみましょう。このバージョンでは、単純に説明する長方形を見つけ、そのすべてのピクセルを調べます。 各ピクセルについて、重心座標を考慮します。 少なくとも1つの負の座標(三角形の外側のピクセル)がある場合、それを折りたたみます。 簡単にするために、単純に2次元の三角形を描く独立したプログラムを提供します。



非表示のテキスト
#include <vector> #include <iostream> #include "geometry.h" #include "tgaimage.h" const int width = 200; const int height = 200; Vec3f barycentric(Vec2i *pts, Vec2i P) { Vec3f u = cross(Vec3f(pts[2][0]-pts[0][0], pts[1][0]-pts[0][0], pts[0][0]-P[0]), Vec3f(pts[2][1]-pts[0][1], pts[1][1]-pts[0][1], pts[0][1]-P[1])); if (std::abs(u[2])<1) return Vec3f(-1,1,1); // triangle is degenerate, in this case return smth with negative coordinates return Vec3f(1.f-(u.x+uy)/uz, uy/uz, ux/uz); } void triangle(Vec2i *pts, TGAImage &image, TGAColor color) { Vec2i bboxmin(image.get_width()-1, image.get_height()-1); Vec2i bboxmax(0, 0); Vec2i clamp(image.get_width()-1, image.get_height()-1); for (int i=0; i<3; i++) { for (int j=0; j<2; j++) { bboxmin[j] = std::max(0, std::min(bboxmin[j], pts[i][j])); bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j])); } } Vec2i P; for (Px=bboxmin.x; Px<=bboxmax.x; P.x++) { for (Py=bboxmin.y; Py<=bboxmax.y; P.y++) { Vec3f bc_screen = barycentric(pts, P); if (bc_screen.x<0 || bc_screen.y<0 || bc_screen.z<0) continue; image.set(Px, Py, color); } } } int main(int argc, char** argv) { TGAImage frame(200, 200, TGAImage::RGB); Vec2i pts[3] = {Vec2i(10,10), Vec2i(100, 30), Vec2i(190, 160)}; triangle(pts, frame, TGAColor(255, 0, 0)); frame.flip_vertically(); // to place the origin in the bottom left corner of the image frame.write_tga_file("framebuffer.tga"); return 0; }
      
      









重心関数は、この三角形の点Pの座標を計算します。これについては、前の段落で説明しました。 三角形関数がどのように機能するかを見てみましょう。 何よりもまず、彼女は記述長方形を検討します。 左下と右上の2つの角で定義されます。 三角形のすべての点を調べて、最小および最大の座標を見つけます。 それに加えて、描かれた三角形が画面を越えても時間を無駄にしないように、説明する四角形と画面の四角形の交差点も見つけました。



おめでとう、三角形を描く方法を学びました。







パースペクティブディスプレイ補正



レンダリングでこのラスタライザーをどのように使用しますか? ラインimage.set(Px、Py、色)をフラグメントシェーダーの呼び出しに置き換えたように見えますが、これで終わりです。 残念ながら、これは完全に真実ではありません。



これを行うコード次に示します 。 左のタイトル写真にある彼の作品の結果。 そして、ここに彼の修正があります。これは正しいレンダリングを提供します。 変更は厳密に1つです。bc_screenの重心座標ではなく、bc_clipの重心座標をシェーダーに転送しました。 ふう 正しくしましょう。



問題は、将来、ある時点で、最後の同次座標で割って、パイプラインの直線性を壊してしまうことです。 したがって、ピクセルの重心座標を使用して、元の空間の属性を補間する権利はありません(テクスチャ座標または単なる深さ)。



次のようにタスクを設定しましょう。 三角形ABCに属する特定の点Pは、遠近法による分割後、次の法則に従って点Pに変わることがわかっています。







「三角形A'B'C」に対する点Pの重心座標を知っています(これらは三角形ABCの​​変換された頂点です)。







したがって、三角形A'B'C 'の座標とそれに対する点Pの重心座標がわかっているので、三角形ABCに対する点Pの重心座標を見つける必要があります。







それで、点P 'の座標を書きます:







すべてをrP.z + 1で乗算します。







式P = [ABC] * [理解できないベクトル]を取得しました。 しかし、これは重心座標の定義です! 少し残った。 このベクトルの定義で私たちが知っていることと知らないことは何ですか? アルファベータガンマオールバーは私たちに知られています。 rA.z + 1、rB.z + 1、rC.z + 1は既知であり、これらはラスタライザに転送される三角形の座標です。 1つ残っている= rP.z + 1。 つまり、ポイントPのz座標です。また、その助けを借りてポイントPを決定します。これは閉じた円ですか? 幸いなことにそうではありません。



(正規化された)重心座標では、座標の合計が1、つまり、アルファ+ベータ+ガンマ= 1になるという事実を使用してみましょう。







これで、画面空間の重心座標をグローバル空間の重心座標に変換できます。 座標変換は非線形ですが、これにより通常の線形補間が可能になります。 そのため、修正された座標をフラグメントシェーダーに渡すと、市松模様のテクスチャ上で著しく異なる画像が得られます。



したがって、たとえばテクスチャ座標を見つけるには、(スカラーで)(uv0 uv1 uv2)を(アルファベータガンマ)で乗算すれば十分です。 または(z0 z1 z2)から(alpha beta gamma)。 または(vn0 vn1 vn2)から(alpha beta gamma)。 一般に、補間する必要があるのはすべてです!



良いデータがあれ 、この修正は実際には必要ありません。



この画像は、1つのタイトル画像から別のものを引いたものです。







ヘッドは完全になくなっています。これは、誤った補間によって導入された弱いエラーを示しています。



(オプションの)ボーナスとして、接線空間のカウント方法、法線の接線空間テクスチャの使用方法、輝く表面の作成方法、アンビエントオクルージョンの確認方法が残ります。



All Articles