マウスクリックをレイに変換するにはどうすればよいですか?


18

透視図法があります。ユーザーが画面をクリックすると、マウスポイントから投影される近距離面と遠距離面の間の光線を計算したいので、自分の世界で光線交差コードを実行できます。

私は自分のマトリックスとベクターとレイのクラスを使用していますが、それらはすべて期待どおりに機能します。

ただし、レイをワールド座標に変換しようとすると、遠方は常に0,0,0になるため、レイはマウスクリックからオブジェクト空間の中心ではなく、オブジェクト空間の中心に移動します。(nearとfarのx座標とy座標は同一であり、互いに負であるz座標のみが異なります)

GLint vp[4];
glGetIntegerv(GL_VIEWPORT,vp);
matrix_t mv, p;
glGetFloatv(GL_MODELVIEW_MATRIX,mv.f);
glGetFloatv(GL_PROJECTION_MATRIX,p.f);
const matrix_t inv = (mv*p).inverse();
const float
    unit_x = (2.0f*((float)(x-vp[0])/(vp[2]-vp[0])))-1.0f,
    unit_y = 1.0f-(2.0f*((float)(y-vp[1])/(vp[3]-vp[1])));
const vec_t near(vec_t(unit_x,unit_y,-1)*inv);
const vec_t far(vec_t(unit_x,unit_y,1)*inv);
ray = ray_t(near,far-near);

何が間違っていますか?(マウスポイントをどのように投影解除しますか?)


好奇心から、組み込みのGL_SELECTIONモードを使用しない理由はありますか?(情報はこちら)
リケット

@Ricket廃止されていませんか?わからないが、確かに。
ノタベネ

xとyはピクセル単位ですか?それが問題になります
...-ノタベネ

そして、vec3にmatrix4を掛けています。それは良くありません... vec_t(1st、2nd、3th、4th)を実行すると、4番目の位置に何の数字が入りますか?
ノタベン

@notebeneうわー、私はそれがそうだと思う、私は手がかりを持っていなかった。この議論は良いものであり、私が検討しなければならない代替案を提供します。そして今、私は本当にこの質問に対する良い答えを望んでいます。
リケット

回答:


14

私は最近、WebGLアプリケーションのためにこれを自分で解決しなければなりませんでした。完全なソースコードを添付しましたが、すぐに動作しない場合のデバッグのヒントを次に示します。

  1. ゲームでunprojectメソッドをデバッグしないでください。可能であれば、単体テストスタイルのテストを作成して、問題の原因を簡単に特定できるようにしてください。
  2. 必ず、ニアクリッピングプレーンとファークリッピングプレーンの両方の出力レイを印刷してください。
  3. 行列演算は可換ではないことに注意してください。A x C!= C x A.数学を再確認してください。

また、上記のコメントに返信するには、OpenGLの選択APIを使用することはほとんどありません。これは、メニューを作成する場合のように、既存のアイテムを選択するのに役立ちますが、3Dモデル編集のような実際のシナリオのほとんどを実行できません。クリックの結果としてジオメトリを追加する必要がある場所。

これが私の実装です。ここで何も魔法はありません。JavaScriptとGoogleのクロージャーライブラリのみ。

gluUnProject

/**
 * Port of gluUnProject. Unprojects a 2D screen coordinate into the model-view
 * coordinates.
 * @param {Number} winX The window point for the x value.
 * @param {Number} winY The window point for the y value.
 * @param {Number} winZ The window point for the z value. This should range
 *    between 0 and 1. 0 meaning the near clipping plane and 1 for the far.
 * @param {goog.math.Matrix} modelViewMatrix The model-view matrix.
 * @param {goog.math.Matrix} projectMatrix The projection matrix.
 * @param {Array.<Number>} view the viewport coordinate array.
 * @param {Array.<Number>} objPos the model point result.
 * @return {Boolean} Whether or not the unprojection was successful.
 */
octorok.math.Matrix.gluUnProject = function(winX, winY, winZ,
                        modelViewMatrix, projectionMatrix,
                        viewPort, objPos) {
  // Compute the inverse of the perspective x model-view matrix.
  /** @type {goog.math.Matrix} */
  var transformMatrix =
    projectionMatrix.multiply(modelViewMatrix).getInverse();

  // Transformation of normalized coordinates (-1 to 1).
  /** @type {Array.<Number>} */
  var inVector = [
    (winX - viewPort[0]) / viewPort[2] * 2.0 - 1.0,
    (winY - viewPort[1]) / viewPort[3] * 2.0 - 1.0,
    2.0 * winZ - 1.0,
    1.0 ];

  // Now transform that vector into object coordinates.
  /** @type {goog.math.Matrix} */
  // Flip 1x4 to 4x1. (Alternately use different matrix ctor.
  var inMatrix = new goog.math.Matrix([ inVector ]).getTranspose();
  /** @type {goog.math.Matrix} */
  var resultMtx = transformMatrix.multiply(inMatrix);
  /** @type {Array.<Number>} */
  var resultArr = [
    resultMtx.getValueAt(0, 0),
    resultMtx.getValueAt(1, 0),
    resultMtx.getValueAt(2, 0),
    resultMtx.getValueAt(3, 0) ];

  if (resultArr[3] == 0.0) {
    return false;
  }

  // Invert to normalize x, y, and z values.
  resultArr[3] = 1.0 / resultArr[3];

  objPos[0] = resultArr[0] * resultArr[3];
  objPos[1] = resultArr[1] * resultArr[3];
  objPos[2] = resultArr[2] * resultArr[3];

  return true;
};

使用法

  this.sys.event_mouseClicked = function(event) {
    // Relative x and y are computed via magic by SystemModule.
    // Should range from 0 .. viewport width/height.
    var winX = event.relativeX;
    var winY = event.relativeY;
    window.console.log('Camera at [' + me.camera.position_ + ']');
    window.console.log('Clicked [' + winX + ', ' + winY + ']');

    // viewportOriginX, viewportOriginY, viewportWidth, viewportHeight
    var viewPort = [0, 0, event.viewPortWidth, event.viewPortHeight];
    var objPos = [];  // out parameter.

    // The camera's model-view matrix is the result of gluLookAt.
    var modelViewMatrix = me.camera.getCameraMatrix();
    // The perspective matrix is the result of gluPerspective.
    var perspectiveMatrix = pMatrix.get();

    // Ray start
    var result1 = octorok.math.Matrix.gluUnProject(
      winX, winY, 0.0,
      modelViewMatrix, perspectiveMatrix,
      viewPort, objPos);
    window.console.log('Seg start: ' + objPos + ' (result:' + result1 + ')');

    // Ray end
    var result2 = octorok.math.Matrix.gluUnProject(
      winX, winY, 1.0,
      modelViewMatrix, perspectiveMatrix,
      viewPort, objPos);
    window.console.log('Seg end: ' + objPos + ' (result:' + result2 + ')');
  };
};

if(resultArr [3] == 0.0)が真と評価されるのはどのような場合ですか?
-Halsafar

3

コードに問題は見られません。だから私はいくつかの提案しかありません:

unit_x = (2.0f*((float)(x-vp[0])/(vp[2]-vp[0])))-1.0f,
unit_y = (2.0f*((float)(y-vp[1])/(vp[3]-vp[1])))-1.0f;

そして、行列の乗算をに変更しようとしてい(p*mv).inverse();ます。glMatricesがニュートラリーにメモリに入れ替わるからです。(これはたぶん1つの大きなです、ごめんなさい)

別のレイキャスティング
アンプロジェクティングは、ピクセルからレイを取得する方法だけではありません。これはレイキャスターの私のコードです:

//works for right handed coordinates. So it is opengl friendly.
float mx = (float)((pixel.x - screenResolution.x * 0.5) * (1.0 / screenResolution.x) * camera.fovX * 0.5);
float my = (float)((pixel.y - screenResolution.y * 0.5) * (1.0 / screenResolution.x) * camera.fovX * 0.5);
float4 dx = cameraRight * mx;
float4 dy = cameraUp * my;

float3 dir = normalize(cameraDir + (dx + dy).xyz * 2.0);

ビューマトリックスからcameraRight、Up、およびDirectionを取得できます(これはシェーダーで主に役立ちますopenglのビューマトリックス

カラーピッキング
カラーピッキングは、すべてのクリック可能なオブジェクトを別の(単色)色でレンダリングし、クリックされたポイントで色の値を読み取る手法です。openglでGL_SELECTIONとして機能しました。しかし、現在は非推奨です。しかし、カラーピッキングは1つの追加レンダーパスとして簡単にコーディングできます。

フレームバッファを使用し、画面解像度と同じ解像度でテクスチャにレンダリングします。フラグメントシェーダで色を返すだけのシンプルなシェーダを使用します。「カラーパス」の後に、clickedPoint xおよびyを使用してテクスチャアドレッシングから値を読み取ります。読んだ色のオブジェクトを見つけます。簡単で非常に高速です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.