Làm thế nào tôi có thể chuyển đổi một nhấp chuột để một tia?


18

Tôi có một hình chiếu phối cảnh. Khi người dùng nhấp vào màn hình, tôi muốn tính toán tia giữa các mặt phẳng gần và xa chiếu từ điểm chuột, vì vậy tôi có thể thực hiện một số mã giao cắt tia với thế giới của mình.

Tôi đang sử dụng các lớp ma trận và vectơ và tia của riêng tôi và tất cả chúng đều hoạt động như mong đợi.

Tuy nhiên, khi tôi thử và chuyển đổi tia sang tọa độ thế giới, điểm xa của tôi luôn kết thúc là 0,0,0 và do đó tia của tôi đi từ nhấp chuột đến trung tâm của không gian đối tượng, thay vì xuyên qua nó. (Tọa độ x và y của gần và xa giống hệt nhau, chúng chỉ khác nhau ở tọa độ z nơi chúng là phủ định của nhau)

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);

Tôi đã làm gì sai? (Làm thế nào để bạn không dự đoán điểm chuột?)


Vì tò mò, có lý do gì bạn không sử dụng chế độ GL_SELMENT tích hợp không? (thông tin ở đây)
Ricket

@Ricket Không phải là nó bị phản đối sao? Không biết, nhưng khá chắc chắn.
Notabene

Là x và y tính bằng pixel? đó sẽ là vấn đề ...
Notabene

Và bạn đang nhân vec3 với ma trận4. Điều đó không tốt ... con số nào đi vào vị trí thứ 4 khi bạn làm vec_t (thứ 1, thứ 2, thứ 3, thứ 4)?
Notabene

@notebene Wow, tôi đoán là vậy, tôi không có manh mối. Cuộc thảo luận này là tốt và cung cấp một giải pháp thay thế mà tôi sẽ phải xem xét, và bây giờ tôi THỰC SỰ hy vọng có câu trả lời tốt cho câu hỏi này.
Ricket

Câu trả lời:


14

Gần đây tôi đã phải tự giải quyết vấn đề này cho một ứng dụng WebGL. Tôi đã đính kèm mã nguồn hoàn chỉnh, nhưng trong trường hợp nó không hoạt động ngay lập tức cho bạn đây là một số mẹo gỡ lỗi:

  1. Đừng gỡ lỗi phương pháp không dự đoán của bạn trong trò chơi của bạn. Nếu có thể, hãy thử viết các bài kiểm tra kiểu kiểm tra đơn vị để dễ dàng cách ly những gì đang xảy ra.
  2. Đảm bảo in các tia đầu ra cho cả hai mặt phẳng cắt gần và xa.
  3. Hãy nhớ rằng toán học ma trận KHÔNG giao hoán. A x C! = C x A. Kiểm tra lại toán học của bạn.

Ngoài ra, để trả lời một số nhận xét ở trên, bạn gần như không bao giờ muốn sử dụng API lựa chọn của OpenGL. Điều đó giúp bạn chọn các mục hiện có, như nếu bạn đang tạo menu, tuy nhiên nó không thực hiện được hầu hết các tình huống trong thế giới thực như chỉnh sửa mô hình 3D. Nơi bạn cần thêm hình học là kết quả của nhấp chuột.

Đây là thực hiện của tôi. Không có gì kỳ diệu xảy ra ở đây. Chỉ cần thư viện đóng cửa của JavaScript và 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;
};

Sử dụng

  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 + ')');
  };
};

Trong trường hợp nào thì if (resultArr [3] == 0.0) đánh giá là đúng?
Halsafar

3

Tôi không thấy bất kỳ vấn đề với mã của bạn. Vì vậy, tôi chỉ có một số gợi ý:

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;

Và cố gắng thay đổi nhân ma trận thành (p*mv).inverse();. Chỉ vì glMatrices là nutaraly chuyển trong bộ nhớ. (đây là một điều có thể lớn, xin lỗi)

Một phép chiếu tia khác
không chỉ là cách lấy tia từ pixel. Đây là mã của tôi cho raycaster:

//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);

Bạn có thể lấy cameraRight, Up và Direction từ ma trận xem (phần lớn hữu ích trong shader) Xem ma trận trong opengl

Chọn màu Chọn
màu là một kỹ thuật trong đó bạn kết xuất mọi đối tượng có thể nhấp bằng màu khác (rắn) và sau đó đọc giá trị của màu tại điểm được nhấp. Nó hoạt động như GL_SELMENT trong opengl. Nhưng nó bị phản đối bây giờ. Nhưng chọn màu rất dễ mã hóa khi thêm 1 kết xuất.

Sử dụng bộ đệm khung và kết xuất thành kết cấu với độ phân giải tương tự như độ phân giải màn hình, sử dụng trình đổ bóng đơn giản chỉ trả lại màu trong trình đổ bóng mảnh. Sau khi "vượt qua màu sắc", hãy đọc giá trị từ một địa chỉ kết cấu với clickedPoint x và y. Tìm đối tượng với màu mà bạn đọc. Dễ dàng và khá nhanh chóng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.