2

約2日前に、Model-View-Projection( "MVP")マトリックスを明示的に計算して、その動作を理解するためのコードを作成することにしました。それ以来、私が使用している射影行列のせいで、問題が発生しただけです。

iPhoneディスプレイを使用して、次の4つの角の頂点で表される画面中心の正方形を作成します。

        const CGFloat cy = screenHeight/2.0f;
        const CGFloat z = -1.0f;
        const CGFloat dim = 50.0f;

        vxData[0] = cx-dim;
        vxData[1] = cy-dim;
        vxData[2] = z;
        vxData[3] = cx-dim;
        vxData[4] = cy+dim;
        vxData[5] = z;
        vxData[6] = cx+dim;
        vxData[7] = cy+dim;
        vxData[8] = z;
        vxData[9] = cx+dim;
        vxData[10] = cy-dim;
        vxData[11] = z;

OGLES 2.0を使用しているので、MVPをユニフォームとして頂点シェーダーに渡し、次に変換を現在の頂点位置に適用します。

uniform mat4 mvp;
attribute vec3 vpos;
void main()
{
  gl_Position = mvp * vec4(vpos, 1.0);
}

今のところ、MVPを単純化してP行列にしました。以下に示すコードには、2つの射影行列がリストされています。1つ目は標準のパースペクティブ射影行列で、2つ目はオンラインで見つけた明示値射影行列です。

CGRect screenBounds = [[UIScreen mainScreen] bounds];
const CGFloat screenWidth = screenBounds.size.width;
const CGFloat screenHeight = screenBounds.size.height;

const GLfloat n = 0.01f;
const GLfloat f = 100.0f;
const GLfloat fov = 60.0f * 2.0f * M_PI / 360.0f;
const GLfloat a = screenWidth/screenHeight;
const GLfloat d = 1.0f / tanf(fov/2.0f);


// Standard perspective projection.
GLKMatrix4 projectionMx = GLKMatrix4Make(d/a, 0.0f, 0.0f, 0.0f,
                                         0.0f, d, 0.0f, 0.0f,
                                         0.0f, 0.0f, (n+f)/(n-f), -1.0f,
                                         0.0f, 0.0f, (2*n*f)/(n-f), 0.0f);
// The one I found online.
GLKMatrix4 projectionMx = GLKMatrix4Make(2.0f/screenWidth,0.0f,0.0f,0.0f,
                                         0.0f,2.0f/-screenHeight,0.0f,0.0f,
                                         0.0f,0.0f,1.0f,0.0f,
                                         -1.0f,1.0f,0.0f,1.0f);

明示的な値マトリックスを使用する場合、正方形は画面の中央に正確な寸法で正確にレンダリングされます。透視投影行列を使用する場合、画面には何も表示されません。透視投影行列によって画面の中心に生成された位置の値を印刷しましたが(screenWidth/2, screenHeight/2, 0)、それらは膨大です。明示的な値の行列は正しくゼロを生成します。

明示的な値行列は正射影行列だと思います-そうですか?私のフラストレーションは、パースペクティブプロジェクションマトリックスが機能しない理由を理解できないことです。

誰かがこの問題で私を助けてくれたら、私は非常に感謝しています。どうもありがとう。

クリスチャンラウの更新:

 #define Zn 0.0f
 #define Zf 100.0f
 #define PRIMITIVE_Z 1.0f

 //...

 CGRect screenBounds = [[UIScreen mainScreen] bounds];
 const CGFloat screenWidth = screenBounds.size.width;
 const CGFloat screenHeight = screenBounds.size.height;

 //...

 glUseProgram(program);

 //...

 glViewport(0.0f, 0.0f, screenBounds.size.width, screenBounds.size.height);

 //...

 const CGFloat cx = screenWidth/2.0f;
 const CGFloat cy = screenHeight/2.0f;
 const CGFloat z = PRIMITIVE_Z;
 const CGFloat dim = 50.0f;

 vxData[0] = cx-dim;
 vxData[1] = cy-dim;
 vxData[2] = z;
 vxData[3] = cx-dim;
 vxData[4] = cy+dim;
 vxData[5] = z;
 vxData[6] = cx+dim;
 vxData[7] = cy+dim;
 vxData[8] = z;
 vxData[9] = cx+dim;
 vxData[10] = cy-dim;
 vxData[11] = z;

 //...

 const GLfloat n = Zn;
 const GLfloat f = Zf;
 const GLfloat fov = 60.0f * 2.0f * M_PI / 360.0f;
 const GLfloat a = screenWidth/screenHeight;
 const GLfloat d = 1.0f / tanf(fov/2.0f);
 GLKMatrix4 projectionMx = GLKMatrix4Make(d/a, 0.0f, 0.0f, 0.0f,
                                          0.0f, d, 0.0f, 0.0f,
                                          0.0f, 0.0f, (n+f)/(n-f), -1.0f,
                                          0.0f, 0.0f, (2*n*f)/(n-f), 0.0f);

 //...

 // ** Here is the matrix you recommended, Christian:
 GLKMatrix4 ts = GLKMatrix4Make(2.0f/screenWidth, 0.0f, 0.0f, -1.0f,
                                0.0f, 2.0f/screenHeight, 0.0f, -1.0f,
                                0.0f, 0.0f, 1.0f, 0.0f,
                                0.0f, 0.0f, 0.0f, 1.0f);

 GLKMatrix4 mvp = GLKMatrix4Multiply(projectionMx, ts);

更新2

新しいMVPコード:

GLKMatrix4 ts = GLKMatrix4Make(2.0f/screenWidth, 0.0f, 0.0f, -1.0f,
                               0.0f, 2.0f/-screenHeight, 0.0f, 1.0f,
                               0.0f, 0.0f, 1.0f, 0.0f,
                               0.0f, 0.0f, 0.0f, 1.0f);

// Using Apple perspective, view matrix generators
// (I can solve bugs in my own implementation later..!)
GLKMatrix4 _p = GLKMatrix4MakePerspective(60.0f * 2.0f * M_PI / 360.0f,
                                          screenWidth / screenHeight,
                                          Zn, Zf);
GLKMatrix4 _mv = GLKMatrix4MakeLookAt(0.0f, 0.0f, 1.0f,
                                      0.0f, 0.0f, -1.0f,
                                      0.0f, 1.0f, 0.0f);
GLKMatrix4 _mvp = GLKMatrix4Multiply(_p, _mv);
GLKMatrix4 mvp = GLKMatrix4Multiply(_mvp, ts);

それでも画面の中心には何も表示されず、画面の中心の変換されたx、y座標はゼロではありません。

更新3

ts上記のコードで代わりにの転置を使用すると機能します!しかし、正方形はもはや正方形に見えません。アスペクト比が設定されているように見えます。screenHeight/screenWidthつまり、(短い)画面の幅に平行な長い寸法と、(長い)画面の高さに平行な短い寸法があります。

transpose(ts)(a)転置が必要な理由と、それが有効な修正であるかどうか、(b)非正方形の次元を正しく修正する方法、および(c)使用するこの追加の行列がどのように適合するかを知りたいです。ビューポート*プロジェクション*ビュー*モデル*ポイントの変換チェーン。

(c)の場合:マトリックス何をするか、つまり、範囲[-1、1]にどのように変換するかについてのChristianRauによる説明を理解しています。しかし、この追加の作業を別個の変換行列として含めるのは正しいですか、それともMVPチェーンの一部が代わりにこの作業を行う必要がありますか?

これまでの貴重な貢献をしてくれたChristianRauに心から感謝します。

更新4

「tsがどのように適合するか」についての私の質問はばかげていますね。要点は、頂点に画面座標を使用することを選択しているため、マトリックスが必要なだけです。最初から世界空間の座標を使用する場合、この作業は必要ありません。

すべてのあなたの助けをクリスチャンに感謝します、それはかけがえのないものでした:)問題は解決しました。

4

1 に答える 1

2

これは、最初の射影行列が変換のスケーリングと変換の部分を考慮していないのに対し、2番目の行列はそれを考慮しているためです。

したがって、モデルビュー行列は単位行列であるため、最初の射影行列はモデルの座標がのどこかにあると想定します[-1,1]が、2番目の行列はすでにスケーリングと変換の部分を含んでおり(screenWidth/Heightそこにある値を見てください)、したがって座標はにあると想定します[0,screenWidth] x [0,screenHeight]

したがって、射影行列に、最初にスケール[0,screenWidth]ダウン[0,2]およびスケール[0,screenHeight]ダウンしてから(forおよびforを使用して)に[0,2]変換[0,2]される行列を右乗算する必要があります。[-1,1]wscreenWidthhscreenHeight

[ 2/w   0     0   -1 ]
[ 0     2/h   0   -1 ]
[ 0     0     1    0 ]
[ 0     0     0    1 ]

これにより、マトリックスが作成されます

[ 2*d/h   0       0             -d/a        ]
[ 0       2*d/h   0             -d          ]
[ 0       0       (n+f)/(n-f)   2*n*f/(n-f) ]
[ 0       0       -1            0           ]

したがって、2番目のマトリックスが90度の視野、1:1のアスペクト比、[-1,1]の近距離範囲に対応していることがわかります。さらに、y軸も反転するため、原点は左上になり、2番目の行が否定されます。

[ 0   -2*d/h   0   d ]

しかし、最後のコメントとして、これらすべてを説明するように射影行列を構成しないことをお勧めします。代わりに、投影マトリックスは最初のマトリックスのようになり、modelviewマトリックスに世界の変換またはスケーリングを管理させる必要があります。変換パイプラインがモデルビューとプロジェクションマトリックスに分離されたのは偶然ではありません。シェーダーを使用する場合も、この分離を維持する必要があります。もちろん、CPUで両方のマトリックスを乗算し、単一のMVPマトリックスをシェーダーにアップロードすることもできます。

また、一般的に、3次元の世界で作業する場合、画面ベースの座標系は実際には使用しません。これを行うのは、2Dグラフィックス(GUI要素やHUDなど)を描画している場合のみです。この場合、とにかく、より単純な正射影行列を使用します。これは、上記のスケール変換行列にすぎません。視点の複雑さ。

編集:あなたの3番目の更新へ:

GLKMatrix4Make(a)関数がそのパラメーターを列優先形式で受け入れ、行列を行単位で配置するため、転置が必要です。

(b)少し間違えました。マトリックス内screenWidthのをに変更する必要があります(またはその逆であるかどうかはわかりません)。アスペクト比はすでに射影行列によって処理されているため、実際には均一なスケールが必要です。tsscreenHeight

(c)このマトリックスを通常のMVPパイプラインに分類することは容易ではありません。これは、あまり一般的ではないためです。レンダリングの2つの一般的なケースを見てみましょう。

  1. 3D:3次元の世界では、画面ベースの単位で座標を定義することはあまり一般的ではありません。これは、3Dシーンから2D画面へのマッピングがなく、単位がピクセルに等しい座標系を使用しているためです。意味がありません。この設定では、世界を別の単位系に変換するためのモデルビューマトリックスの一部として分類する可能性があります。しかし、この場合、そのような中途半端な2Dソリューションだけでなく、実際の3D変換が必要になります。

  2. 2D:2Dシーン(GUI、HUD、またはテキストなど)をレンダリングする場合、画面ベースの座標系が必要になることがあります。ただし、この場合、正投影を使用する可能性が最も高くなります(遠近法なし)。このような正投影行列は、実際にはこのts行列にすぎません(近距離から遠距離に基づいて、zのスケール変換が追加されています)。したがって、この場合、行列は射影行列に属しているか、実際には射影行列です。古き良きglOrtho関数がその行列をどのように構築するかを見るだけで、それは。に過ぎないことがわかりますts

于 2011-10-16T19:02:38.807 に答える