У меня есть средство визуализации, использующее directx и openGL, и трехмерную сцену. Окно просмотра и окно имеют одинаковые размеры.
Как реализовать выбор данных координат мыши x и y независимо от платформы?
У меня есть средство визуализации, использующее directx и openGL, и трехмерную сцену. Окно просмотра и окно имеют одинаковые размеры.
Как реализовать выбор данных координат мыши x и y независимо от платформы?
Если можете, выберите процессор, рассчитав луч, идущий от глаза через указатель мыши, и пересеките его с вашими моделями.
Если это не вариант, я бы выбрал какой-нибудь тип визуализации идентификатора. Назначьте каждому объекту, которому вы хотите выбрать уникальный цвет, визуализируйте объекты с этими цветами и, наконец, считайте цвет из фреймбуфера под указателем мыши.
РЕДАКТИРОВАТЬ: Если вопрос в том, как построить луч по координатам мыши, вам понадобится следующее: матрица проекции 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
mouse_clip = [float(x) * 2 / float(width) - 1, 1 - float(y) * 2 / float(height), -1 * near_clipping_plane, 1]
- person Alex W; 22.04.2012
mouseWorldSpace
делить по его собственной w
координате, поскольку к ней применено преобразование перспективы?
- person bwroga; 18.03.2018
Вот усеченная пирамида:
Для начала нужно определить, где на ближней плоскости произошел щелчок мышью:
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
gl_Position = projection * modelView * vertexPos;
, это бит посередине, где матрица projection
- это перевод координат камеры в координаты окна просмотра. HTH: /
- person nornagon; 06.11.2012
Что ж, довольно просто, теория, лежащая в основе этого, всегда одна и та же.
1) Отмените проецирование двумерных координат на трехмерное пространство. (у каждого API есть своя функция, но вы можете реализовать свою, если хотите). Один на Min Z, один на Max Z.
2) Используя эти два значения, вычислите вектор, который идет от Min Z и указывает на Max Z.
3) Используя вектор и точку, вычислите луч, который идет от Min Z до MaxZ.
4) Теперь у вас есть луч, с его помощью вы можете пересечь луч-треугольник / луч-плоскость / луч-что-то и получить результат ...
У меня мало опыта работы с 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
Хорошо, эта тема старая, но это было лучшее, что я нашел по этой теме, и это немного помогло мне, поэтому я опубликую здесь для тех, кто подписан ;-)
Вот как я заставил его работать без необходимости вычислять обратную матрицу проекции:
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;
}
Это предполагает перспективную проекцию, но никогда не возникает вопроса об орфографической проекции.
У меня такая же ситуация с обычным выбором лучей, но что-то не так. Я выполнил операцию непроекта должным образом, но она не работает. Я думаю, что ошибся, но не могу понять где. Мое умножение matix, обратное и векторное умножение matix - все работают нормально, я их проверил. В моем коде я реагирую на WM_LBUTTONDOWN. Таким образом, lParam возвращает координаты [Y] [X] как 2 слова в двойном слове. Я извлекаю их, затем конвертирую в нормализованное пространство, я проверил, что эта часть также работает нормально. Когда я нажимаю на нижний левый угол - я получаю значения, близкие к -1 -1, и хорошие значения для всех трех других углов. Затем я использую массив 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;