1

ソフトウェアの 3D ビューにパン ツールを実装しています。これは、たとえば Photoshop や Acrobat Reader のグラブ ツールと同じように機能するはずです。つまり、ユーザーがマウスでつかんだポイント (クリックしたままにしてからマウスを移動) は、マウスが移動してもマウス カーソルの下にとどまります。

これは一般的なパラダイムであり、以前に SO で質問されたものであり、OpenGL のテクニックに関するこの質問に対する最良の答えです。いくつかのヒントもある別の記事があり、私はこの非常に有益な CodeProject の記事を読んでいます。(コード例の変数などの多くは説明されていませんが、テキストを読んでテクニックを理解していると思います。) しかし、私の 3D 環境のナビゲーションはそれらの記事とはかなり異なって設定されているため、いくつかの実装上の問題があります。私はいくつかのガイダンスを求めています。

私のテクニック - これは根本的に欠陥がある可能性があるので、そう言ってください - は次のとおりです。

  • シーンの「カメラ」はD3DXVECTOR3、目の位置とルック ポイントの 2 つのポイントとして保存されます。ビュー マトリックスは次のD3DXMatrixLookAtLHように作成されます。

    const D3DXVECTOR3 oUpVector(0.0f, 1.0f, 0.0f); // Keep up "up", always.
    D3DXMatrixLookAtLH(&m_oViewMatrix, &m_oEyePos, &m_oLook, &oUpVector);
    
  • マウス ボタンが押されると、そのピクセルを介して光線を放ち、以下を見つけます。クリックされたピクセルの座標 (投影されていないシーン/ワールド空間)。その光線と近平面との交点。近平面点と物体の間の距離。これは、これらの 2 点間の長さです。これとマウスの位置、および元のナビゲーション (目と視線) を保存します。

    // Get the clicked-on point in unprojected (normal) world space
    D3DXVECTOR3 o3DPos;
    if (Get3DPositionAtMouse(roMousePos, o3DPos)) { // fails if nothing under the mouse
    
    // Mouse location when panning started
    m_oPanMouseStartPos = roMousePos;
    
    // Intersection at near plane (z = 0) of the ray from camera to clicked spot
    D3DXVECTOR3 oRayVector;
    CalculateRayFromPixel(m_oPanMouseStartPos, m_oPanPlaneZ0StartPos, oRayVector);
    
    // Store original eye and look points
    m_oPanOriginalEyePos = m_oEyePos;
    m_oPanOriginalLook = m_oLook;
    
    // Store the distance between near plane and the object, and the object position
    m_dPanPlaneZ0ObjectDist = fabs(D3DXVec3Length(&(o3DPos - m_oPanPlaneZ0StartPos)));
    m_oPanOriginalObjectPos = o3DPos;
    

    Get3DPositionAtMouseは、マウスの下の 3D 座標を取得する既知の方法です。CalculateRayFromPixelは、スクリーン空間のマウス座標を取り込んで光線を投射し、他の 2 つのパラメータを近平面 (Z = 0) での光線の交点と正規化された光線ベクトルで満たす、既知の適切な方法です。

  • マウスが移動すると、新しい位置に別のレイをキャストしますが、古い (元の) ビュー マトリックスを使用します。(これを指摘してくれた以下の Nico に感謝します。) 近平面から光線を延長して、オブジェクトと近平面の間の距離をオブジェクトがどこにあるべきかを計算します (この方法では、元のオブジェクトと新しいオブジェクト ポイントが平行平面にある必要があります。近い面。) 目とルック座標をこれだけ動かします。Eye と Look は、元の (パンが開始されたときの) 値から設定されます。違いは、元のマウスと新しいマウスの位置です。これは、マウスの移動に伴う粒状 (整数) のピクセル移動による増分または減分による精度の低下を減らすためです。つまり、毎回ナビゲーションの全体の差を計算します。

    // Set navigation back to original (as it was when started panning) and cast a ray for the mouse
    m_oEyePos = m_oPanOriginalEyePos;
    m_oLook = m_oPanOriginalLook;
    UpdateView();
    D3DXVECTOR3 oRayVector;
    D3DXVECTOR3 oNewPlaneZPos;
    CalculateRayFromPixel(roMousePos, oNewPlaneZPos, oRayVector);
    
    // Now intersect that ray (ray through the mouse pixel, using the original navigation)
    // to hit the plane the object is in.  Function uses a "line", so start at near plane
    // and the line is of the length of the far plane away
    D3DXVECTOR3 oNew3DPos;
    D3DXPlaneIntersectLine(&oNew3DPos, &m_oPanObjectPlane, &oNewPlaneZPos, &(oRayVector * GetScene().GetFarPlane()));
    
    // The eye/look difference /should/ be as simple as:
    //  const D3DXVECTOR3 oDiff = (m_oPanOriginalObjectPos - oNew3DPos);
    // But that lags and is slow, ie the objects trail behind.  I don't know why.  What does
    // work is to scale the from-to difference by the distance from the camera relative to
    // the whole scene distance
    const double dDist = D3DXVec3Length(&(oNew3DPos - m_oPanOriginalEyePos));
    const double dTotalDist = GetScene().GetFarPlane() - GetScene().GetNearPlane();
    const D3DXVECTOR3 oDiff = (m_oPanOriginalObjectPos - oNew3DPos) * (1.0 + (dDist / dTotalDist));
    
    // Adjust the eye and look points by the same amount, so orthogonally changed
    m_oEyePos = m_oPanOriginalEyePos + oDiff;
    m_oLook = m_oPanOriginalLook + oDiff;
    

ダイアグラム

この図は、これを実装するための私の作業スケッチです。

3D パン スケッチ

うまくいけば、テキストよりもはるかに簡単に上記を説明できます。移動するポイントと、そのポイントを同じ相対位置に保つためにカメラを移動する必要がある場所を確認できます。クリックされたポイント (カメラからオブジェクトへの光線) は、中心ピクセルを表す直線の光線のすぐ右にあります。

問題

しかし、おそらくご想像のとおり、これは私の期待どおりには機能しません。私が見たかったのは、クリックされたオブジェクトがマウス カーソルと共に移動することです。私が実際に見ているのは、オブジェクトがマウスの方向に移動することですが、十分ではありません。つまり、クリックしたポイントがカーソルの下に保持されません。第二に、動きがちらつき、ジャンプし、時には最大 20 ~ 30 ピクセルのジッターが発生し、その後ちらつきが発生します。一定のものに置き換えるoDiffと、これは発生しません。

DirectX (D3DX、DX マトリックス順序など) を使用してこれを実装する方法を示すアイデアやコード サンプルは、ありがたく読まれます。

編集

以下のコメンター Nico は、マウス カーソルの移動位置を使用して新しい位置を計算するときに、元のビュー マトリックスを使用する必要があることを指摘しました。そうすることは大いに役立ち、オブジェクトはマウス位置の近くにとどまります。ただし、まだ正確ではありません。私が気付いたのは、画面の中央では正確だということです。マウスが中心から遠ざかるにつれて、どんどん出てきます。これは、オブジェクトがどれだけ離れているかに基づいて変化するようにも見えました. 純粋な「自分が何をしているのかわからない」という当て推量によって、これをニア/ファー平面の係数とオブジェクトの距離でスケーリングしました。これにより、マウスカーソルに非常に近づきますが、それでもいくつかピクセル離れています (画面の一番端で 1 から 30 まで、これは間違っていると感じるのに十分です)。

4

2 に答える 2

2

これが私がこの問題を解決する方法です。

float fieldOfView = 45.0f;

float halfFOV = (fieldOfView / 2.0f) * (DEGREES_TO_RADIANS);
float distanceToObject = // compute the world space distance from the camera to the object you want to pan
float projectionToWorldScale = distanceToObject * tan( halfFov );
Vector mouseDeltaInScreenSpace = // the delta mouse in pixels that we want to pan

Vector mouseDeltaInProjectionSpace = Vector( mouseDeltaInScreenSpace.x * 2 / windowPixelSizeX, mouseDeltaInScreenSpace.y * 2 / windowPixelSizeY ); // ( the "*2" is because the projection space is from -1 to 1)

// go from normalized device coordinate space to world space (at origin)
Vector cameraDelta = -mouseDeltaInProjectionSpace * projectionToWorldScale;

// now translate your camera by "cameraDelta".

これは視野のアスペクト比が1の場合に機能することに注意してください。垂直の視野が水平の視野と異なる場合は、「スケール」を別々のx成分とy成分に分割する必要があると思います

また、「見る」ベクトルについて言及しました。カメラは常に z 軸を真下に向けているため、そのために数学をどのように変更する必要があるかわかりません。

于 2013-02-28T19:15:07.657 に答える