Реализация выбора лучей - PullRequest
31 голосов
/ 19 января 2010

У меня есть средство визуализации, использующее DirectX и OpenGL, и 3D-сцена.Окно просмотра и окно имеют одинаковые размеры.

Как реализовать выбор заданных координат мыши x и y независимо от платформы?

Ответы [ 6 ]

29 голосов
/ 19 января 2010

Если вы можете, выберите процессор, рассчитав луч от глаза через указатель мыши и пересекая его с вашими моделями.

Если это не вариант, я бы использовал какой-то тип рендеринга идентификаторов. Назначьте каждому объекту, который вы хотите выбрать уникальный цвет, визуализируйте объекты с этими цветами и, наконец, считайте цвет из буфера кадров под указателем мыши.

РЕДАКТИРОВАТЬ: Если вопрос заключается в том, как построить луч из координат мыши, вам необходимо следующее: матрица проекции P и преобразование камеры C, Если координаты указателя мыши равны (x, y) , а размер области просмотра равен (ширина, высота) одна позиция в пространстве клипа вдоль луча:

mouse_clip = [
  float(x) * 2 / float(width) - 1,
  1 - float(y) * 2 / float(height),
  0,
  1]

(обратите внимание, что я перевернул ось y, поскольку часто координаты мыши находятся в верхнем левом углу)

Справедливо также следующее:

mouse_clip = P * C * mouse_worldspace

Что дает:

mouse_worldspace = inverse(C) * inverse(P) * mouse_clip

Теперь у нас есть:

p = C.position(); //origin of camera in worldspace
n = normalize(mouse_worldspace - p); //unit vector from p through mouse pos in worldspace
24 голосов
/ 07 июня 2011

Вот просмотр усечения:

viewing frustum

Сначала необходимо определить, где на ближней плоскости произошел щелчок мыши:

  1. изменить масштаб координат окна (0..640,0..480) на [-1,1], с (-1, -1) в нижнем левом углу и (1,1) в верхнем правый.
  2. 'отменить' проекцию, умножив масштабированные координаты на то, что я называю матрицей 'unview': unview = (P * M).inverse() = M.inverse() * P.inverse(), где M - матрица ModelView и P - матрица проекции.

Затем определите, где находится камера в мировом пространстве, и нарисуйте луч, начинающийся с камеры и проходящий через точку, найденную на ближней плоскости.

Камера находится на M.inverse().col(4), то есть в последнем столбце обратной матрицы ModelView.

Конечный псевдокод:

normalised_x = 2 * mouse_x / win_width - 1
normalised_y = 1 - 2 * mouse_y / win_height
// note the y pos is inverted, so +y is at the top of the screen

unviewMat = (projectionMat * modelViewMat).inverse()

near_point = unviewMat * Vec(normalised_x, normalised_y, 0, 1)
camera_pos = ray_origin = modelViewMat.inverse().col(4)
ray_dir = near_point - camera_pos
1 голос
/ 20 января 2010

У меня мало опыта в DirectX, но я уверен, что он похож на OpenGL. Вам нужен вызов gluUnproject.

Если у вас есть действительный Z-буфер, вы можете запросить содержимое Z-буфера в позиции мыши с помощью:

// obtain the viewport, modelview matrix and projection matrix
// you may keep the viewport and projection matrices throughout the program if you don't change them
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);

// obtain the Z position (not world coordinates but in range 0 - 1)
GLfloat z_cursor;
glReadPixels(x_cursor, y_cursor, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z_cursor);

// obtain the world coordinates
GLdouble x, y, z;
gluUnProject(x_cursor, y_cursor, z_cursor, modelview, projection, viewport, &x, &y, &z);

если вы не хотите использовать glu, вы можете также реализовать gluUnProject, вы также можете реализовать его самостоятельно, его функциональность относительно проста и описана в opengl.org

1 голос
/ 20 января 2010

Ну, довольно просто, теория этого всегда одна и та же

1) Отпроецируйте два раза вашу 2D-координату на 3D-пространство. (каждый API имеет свою функцию, но вы можете реализовать свою собственную, если хотите). Один в Мин Z, один в Макс Z.

2) С этими двумя значениями вычислите вектор, который идет от Min Z и указывает на Max Z.

3) С вектором и точкой вычислите луч, который проходит от Min Z до MaxZ

4) Теперь у вас есть луч, с помощью которого вы можете сделать пересечение луч-треугольник / луч-плоскость / луч-нечто и получить свой результат ...

0 голосов
/ 20 ноября 2014

У меня такая же ситуация с обычным сбором лучей, но что-то не так. Я выполнил операцию unproject надлежащим образом, но она просто не работает. Я думаю, что допустил ошибку, но не могу понять где. Мое умножение матикса, обратное и векторное умножение - все хорошо, я проверял их. В моем коде я реагирую на WM_LBUTTONDOWN. Таким образом, lParam возвращает координаты [Y] [X] как 2 слова в слове. Я извлекаю их, затем преобразую в нормализованное пространство, я проверил, эта часть также работает нормально. Когда я щелкаю по левому нижнему углу - я получаю близкие значения к -1 -1 и хорошие значения для всех 3 других углов. Затем я использую массив linepoins.vtx для отладки, и он даже близко не соответствует действительности.

unsigned int x_coord=lParam&0x0000ffff; //X RAW COORD
unsigned int y_coord=client_area.bottom-(lParam>>16); //Y RAW COORD

double xn=((double)x_coord/client_area.right)*2-1; //X [-1 +1]
double yn=1-((double)y_coord/client_area.bottom)*2;//Y [-1 +1]

_declspec(align(16))gl_vec4 pt_eye(xn,yn,0.0,1.0); 
gl_mat4 view_matrix_inversed;
gl_mat4 projection_matrix_inversed;
cam.matrixProjection.inverse(&projection_matrix_inversed);
cam.matrixView.inverse(&view_matrix_inversed);

gl_mat4::vec4_multiply_by_matrix4(&pt_eye,&projection_matrix_inversed);
gl_mat4::vec4_multiply_by_matrix4(&pt_eye,&view_matrix_inversed);

line_points.vtx[line_points.count*4]=pt_eye.x-cam.pos.x;
line_points.vtx[line_points.count*4+1]=pt_eye.y-cam.pos.y;
line_points.vtx[line_points.count*4+2]=pt_eye.z-cam.pos.z;
line_points.vtx[line_points.count*4+3]=1.0;
0 голосов
/ 29 апреля 2012

Хорошо, эта тема старая, но это было лучшее, что я нашел по этой теме, и она мне немного помогла, поэтому я опубликую здесь для тех, кто читает; -)

Вот так я и начал работать без необходимости вычислять обратную матрицу проекции:

void Application::leftButtonPress(u32 x, u32 y){
    GL::Viewport vp = GL::getViewport(); // just a call to glGet GL_VIEWPORT
vec3f p = vec3f::from(                        
        ((float)(vp.width - x) / (float)vp.width),
        ((float)y / (float)vp.height),
            1.);
    // alternatively vec3f p = vec3f::from(                        
    //      ((float)x / (float)vp.width),
    //      ((float)(vp.height - y) / (float)vp.height),
    //      1.);

    p *= vec3f::from(APP_FRUSTUM_WIDTH, APP_FRUSTUM_HEIGHT, 1.);
    p += vec3f::from(APP_FRUSTUM_LEFT, APP_FRUSTUM_BOTTOM, 0.);

    // now p elements are in (-1, 1)
    vec3f near = p * vec3f::from(APP_FRUSTUM_NEAR);
    vec3f far = p * vec3f::from(APP_FRUSTUM_FAR);

    // ray in world coordinates
    Ray ray = { _camera->getPos(), -(_camera->getBasis() * (far - near).normalize()) };

    _ray->set(ray.origin, ray.dir, 10000.); // this is a debugging vertex array to see the Ray on screen

    Node* node = _scene->collide(ray, Transform());
   cout << "node is : " << node << endl;
}

Это предполагает перспективную проекцию, но в первую очередь для орфографического вопроса никогда не возникает.

...