2010-01-19 2 views
30

У меня есть рендеринг с использованием directx и openGL и 3d-сцены. Окно просмотра и окно имеют одинаковые размеры.Реализация Ray Picking

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

ответ

27

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

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

EDIT: Если вопрос заключается в том, чтобы построить луч от координаты мыши вам необходимо следующее: проекционную матрицу P и камеры преобразования C. Если координаты указателя мыши (х, у) и размера окна просмотра является (ширина, высота) одно положения в усеченном пространстве вдоль луча:

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

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

следующая также верно:

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 
+0

Я знаю это! вопрос в том, как !!!! –

+3

@Tom Это не совсем ясно из вопроса. В любом случае, я отредактировал свой ответ, надеюсь, что это поможет. –

+0

Стоит отметить, что если вы используете DirectX-подобные матрицы, то порядок умножения меняется на противоположный. – Goz

1

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

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

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

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

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

1

у меня есть немного 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

+0

спасибо, хотя мне нужен API-метод агностики. –

+0

@ Как я уже сказал, если вы не хотите использовать функцию glu, вы можете просто реализовать ее функциональность самостоятельно, все, что вам нужно, это получить представление модели и проекцию матрицы для каждого и получить положение окна z для каждого. – wich

+0

, который потребует от меня выяснить тот же xyz, я мог бы затем опубликовать этот xyz и отметить это как ответ вместо этого, вы видите мои рассуждения? Кто-то еще разместил математику в любом случае –

20

Вот смотровое усеченный:

viewing frustum

Прежде всего, необходимо определить, где на nearplane мышь нажмите произошло:

  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.

Final псевдокод:

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 
+0

Что это за матрица modelView? Это комбинация матрицы modelToWorld модели, которую мы пытаемся ударить, и viewCatrix камеры? – Jubei

+1

Прошло некоторое время с тех пор, как я написал это, но я думаю, что это матрица, которая преобразует мировые координаты в координаты камеры. Если в вашем вершинном шейдере есть строка, например 'gl_Position = projection * modelView * vertexPos;', это бит в середине, где матрица 'projection' - это перевод с камеры на координаты просмотра. HTH: / – nornagon

0

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

Это способ, которым я получил его на работу без вычисления обратной матрицы проекции:

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

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

0

У меня такая же ситуация с обычным выбором луча, но что-то не так. Я выполнил операцию 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;