Як можна перетворити клацання миші в промінь?


18

У мене перспективна прогноза. Коли користувач натискає на екран, я хочу обчислити промінь між ближньою та далекою площинами, що проектується з точки миші, тому я можу зробити якийсь код перетину променів зі своїм світом.

Я використовую власні матричні та векторні та променеві класи, і всі вони працюють як слід.

Однак, коли я намагаюся перетворити промінь на координати світу, мій далеко не завжди закінчується як 0,0,0, і тому мій промінь переходить від клацання миші до центру простору об'єкта, а не через нього. (Координати 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

@Ricket Чи не застаріло це? Не знаю, але цілком впевнений.
Нотабене

Чи х і у в пікселях? в цьому і буде проблема ...
Нотабене

А ви множите vec3 на матрицю4. Це не добре ... яке число переходить на 4 місце, коли ви робите vec_t (1-й, 2-й, 3-й, 4-й)?
Нотабене

@notebene Нічого собі, я гадаю, я не мав поняття. Ця дискусія є хорошою і пропонує альтернативу, яку мені доведеться розглянути, і тепер Я РЕЙСНО сподіваюся на гарну відповідь на це питання.
Ricket

Відповіді:


14

Нещодавно мені довелося вирішити це самостійно для програми WebGL. Я додав повний вихідний код, але, на випадок, він не працює як раз для вас ось кілька порад щодо налагодження:

  1. Не налагоджуйте метод безпроектування у грі. Якщо можливо, спробуйте написати тести стильового тесту, щоб легше було виділити те, що йде не так.
  2. Не забудьте надрукувати вихідні промені як для близької, так і для далекої площин відсікання.
  3. Пам'ятайте, що математика матриці НЕ комутативна. A x C! = C x A. Перевірте математику.

Крім того, щоб відповісти на деякі коментарі вище, ви майже ніколи не хочете використовувати API вибору OpenGL. Це допомагає вибирати наявні елементи, наприклад, якщо ви створювали меню, однак це не вдається виконати більшість сценаріїв реального світу, таких як редагування 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 + ')');
  };
};

У якому випадку, якщо (resultArr [3] == 0,0) оцінюється як істинне?
Халсафар

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 переносяться в пам'ять. (це одна велика, можливо, вибачте)

Ще одне
непроектування радіомовлення - це не лише спосіб отримання променя з пікселя. Це мій код для 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);

Ви можете отримати камеруRight, Up та Direction з матриці перегляду (що корисно в основному в шейдері) Переглянути матрицю в opengl

Підбір кольорів
кольорів - це техніка, коли ви рендеруєте кожен об'єкт, який можна натиснути, іншим (суцільним) кольором, а потім зчитуєте значення кольору в точці, що натиснув. Він працював як GL_SELECTION у opengl. Але це тепер застаріло. Але підбір кольорів легко кодувати як 1 додатковий пропуск візуалізації.

Використовуйте буфер кадру та візуалізуйте текстуру з такою ж роздільною здатністю, як і роздільна здатність екрана, використовуйте простий шейдер, який просто повертає колір у фрагменті шейдера. Після "передачі кольору" прочитайте значення з адресації текстури з натиснутою точкою x та y. Знайдіть об’єкт з кольором, який ви прочитали. Легко і досить швидко.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.