2011-12-16 15 views
3

私は概念と数学を学ぶためにソフトウェアベースの3Dレンダラを作成しています。それは楽しいと私は床の一種として動作するグリッドの上に素晴らしい回転キューブを持っています。グリッド/フロアは線分を使用してレンダリングされます。シンプルなルックアット変換を使って仮想カメラを配置し、向きを変えました。表示面は、「目」から距離nにあるように、またはz = -nにあるように任意に設定されます。表示面にクリップされた3D点の投影をどのように扱うべきですか?

1つのことを除いてすべてがうまくいきます(オブジェクトからワールドへの変換、カリング、プロジェクト、クリップ、レンダリング)。グリッドをレンダリングするとき、線分の端点は仮想カメラのビュープレーンにまたがることがあります。表示されている部分をレンダリングして、表示面にクリップします。クリッピングされた端点は、表示面に投影されます。投影は次のとおりです。

p'(x) = -n * p(x)/p(z) 
p'(y) = -n * p(y)/p(z) 

潜在的に見えるポイントはすべてp(z) ≤ -nです。表示面にクリップされた点はp(z) = -nです。したがって、私は効果があります:

p'(x) = p(x) 
p'(y) = p(y) 

このような点については、正射投影。

ここでの値は、ビュープレーンのウィンドウの外側にあるため、ビューポート変換によってこれらのポイントがOSウィンドウの境界から外れることがあります。その効果は、私が定期的に周りを飛んでいる迷路を参照していることです。それは恐ろしいです。

OpenGLを使っているだけで、OpenGLを使っているというだけのことをやっているだけでは不十分です(私はOpenGLを使っています)。

ありがとうございました。

次は、異常を示すスクリーンショットです。グリッドの右端のコーナーは見えなくなっています。グリッドの左下隅に向かって来る線分は、カメラの後ろにあり、クリップされます。終点は、(間違った)正射投影を受け、左のフィールドで途中で終わります。

私は(まだ)錐台カリングを行っていません。おそらく私はすべきですか?

enter image description here

+0

私はあなたのロジックに従っていますが、私には、これらのポイントのためにまっすぐな正射投影を持つべきではないようです! –

+0

この質問では、いくつかの問題が混在しています。1)ウィンドウの外にあり、視点の背後にあるというトピックを混在させます。 2)正射影の投影を言いますが、これは透視投影の問題です。 – ideasman42

答えて

0

あなたの錐台のボリュームを見てすることなく、画面スペースの外にこれらのポイントそのプロジェクトを取り除くことができるはずです。したがって、錐台のカリングを行う前に、あなたのスクリーンスペースクリッピングアルゴリズムはどうですか?これらのアルゴリズムを見てみましょう:http://en.wikipedia.org/wiki/Line_clippingとあなたが利益を得ることができるかどうか参照してください。

いずれにしても、レンダラーがOSウィンドウの外側にあるポイントを処理できるようにすることを検討する必要があります。上記のクリッピングアルゴリズムを使用すると、ウィンドウの外側にある線分を完全に外し、1つの点だけが外側にあるか、両方の点が外側にありますが、線は画面空間を移動します。

+1

私は、近くの平面に近い平面の後ろにある線分の端点を切り取ります。これにより、投影面がz = -distanceにあるので、zによる除算が距離項で相殺される。したがって、投影は、有効な正射投影になります。私は標準のSHクリッピングを使ってウィンドウにクリッピングしています。 – z8000

+0

この回答は非常に一般的なものであり、(私が見る限りでは)ウィキペディアへのリンクは、3Dコンテキストでのラインクリッピングについての洞察を実際に与えるものではありません。 – ideasman42

+0

@ z8000、re:* "したがって、投影は実際には正射影投影になります。" * - あなたが何を意味するのかはっきりしていませんが、正しいとは言えません。パースペクティブ投影は、ビューの背後にあるので、単に正射投影にはなりません。 – ideasman42

0

この非常に同じトピックを調べただけで、この操作を実行するために必要なことはそれほど賢明ではないことがわかりました。

まず、ニアプレーンとファープレーンを定義し、そのプレーンでセグメントをクリップします(see example)。

これはうまくいきますが、私は投影の上に余分な計算を避けたいと思っていました。 これは投射行列に精通している人にとっては明白かもしれませんが、正しく動作することを確認するために再確認しなければなりませんでした。

単純なロジックで近/遠線のクリッピングを実行することができます。

  1. 4Dベクトルを得るために、位置を透視行列で掛けます。
  2. 4番目のコンポーネントをnear/farクリップの距離と比較します。
  3. 必要に応じてセグメントをクリップします。

これは、完全投影を計算する前に、ベクトルの4番目の成分を計算することによって最適化することができます。

また、クリッピング後に再度XYZコンポーネントを再計算する必要がないことを意味します。

たとえば、これは4Dベクトルに4x4行列を乗算します。

pub fn mul_m4v4(m: &[[f64; 4]; 4], v: &[f64; 4]) -> [f64; 4] { 
    [ 
     v[0] * m[0][0] + v[1] * m[1][0] + v[2] * m[2][0] + m[3][0] * v[3], 
     v[0] * m[0][1] + v[1] * m[1][1] + v[2] * m[2][1] + m[3][1] * v[3], 
     v[0] * m[0][2] + v[1] * m[1][2] + v[2] * m[2][2] + m[3][2] * v[3], 
     v[0] * m[0][3] + v[1] * m[1][3] + v[2] * m[2][3] + m[3][3] * v[3], 
    ] 
} 

これは3Dポジションなので、4番目のコンポーネントを1.0と見なすことができます。

pub fn mul_m4v3_as_v4(m: &[[f64; 4]; 4], v: &[f64; 3]) -> [f64; 4] { 
    [ 
     v[0] * m[0][0] + v[1] * m[1][0] + v[2] * m[2][0] + m[3][0], 
     v[0] * m[0][1] + v[1] * m[1][1] + v[2] * m[2][1] + m[3][1], 
     v[0] * m[0][2] + v[1] * m[1][2] + v[2] * m[2][2] + m[3][2], 
     v[0] * m[0][3] + v[1] * m[1][3] + v[2] * m[2][3] + m[3][3], 
    ] 
} 

完全な計算を避けるには、別の関数を分割して4番目のコンポーネントを取得します。ここ

pub fn mul_project_m4_v3_zfac(m: &[[f64; 4]; 4], v: &[f64; 3]) -> [f64; 4] { 
    v[0] * m[0][3] + v[1] * m[1][3] + v[2] * m[2][3] + m[3][3] 
} 

は、上述したようにクリッピング実装しcommitです。

注:マトリックスは(OpenGLのような)列メジャーです。

関連する問題