Подтвердить что ты не робот

Внедрение луча

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

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

4b9b3361

Ответ 1

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

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

EDIT: Если вопрос заключается в том, как построить луч из координат мыши, вам понадобится следующее: матрица проецирования 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

Ответ 2

Здесь просмотр усечения:

viewing frustum

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

  • перетащите координаты окна (0..640,0..480) в [-1,1], с (-1, -1) в нижнем левом углу и (1,1) право.
  • "отмените" проекцию, умножив масштабированные координаты на то, что я называю матрицей "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

Ответ 3

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

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

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

3) При вычислении вектора и точки луча, идущего от Min Z до MaxZ

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

Ответ 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

Ответ 5

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

Так я получил его, чтобы не работать, не вычисляя обратную матрицу проекции:

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

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

Ответ 6

У меня такая же ситуация с обычным выбором луча, но что-то не так. Я выполнил операцию unproject надлежащим образом, но он просто не работает. Думаю, я ошибся, но не могу понять, где. Мое умножение matix, обратное и векторное умножение matix все видели, чтобы работать нормально, я их протестировал. В моем коде я реагирую на WM_LBUTTONDOWN. Поэтому lParam возвращает [Y] [X] координаты как 2 слова в dword. Я извлекаю их, а затем конвертирую в нормализованное пространство, я проверил эту часть, также отлично работает. Когда я нажимаю левый нижний угол - я получаю близкие значения -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;