2013-06-06 3 views
0

У меня есть сцена, в которой я обрабатываю пару кубов в ней с помощью openGL (структура программы не использует GLUT, она находится в структуре программ win32, но я просто рисую кубы с glutSolidCube) сейчас Я хочу выбрать эти кубы мышью, выбрав. это то, что я делаю: сначала, когда пользователь нажимает кнопку мыши на сцене, я получаю позицию мыши и пытаюсь найти ее координаты в координатах сцены (templateSkeletons - это скелет, который я создал из кубов, не более):mouse picking in opengl using `gluUnProject`

if (mouse.buttonPressed(Mouse::BUTTON_LEFT)) 
     { 
      mouse.update(); 
      templateSkeletons[0].selectionMode = true; 
      Vector3* points; 
      points = GetOGLPos(); 
      templateSkeletons[0].setIntersectionPoints(points[0],points[1]); 
     }else 
      templateSkeletons[0].selectionMode = false; 

это GerOGLPos функция, я извлекая координаты в сцене (уведомление, что у меня есть свой собственный camrea и свою собственную матрицу проекции, но я выборки матрицу проекции здесь, в этой функции только по телефону glGetDoublev (GL_PROJECTION_MATRIX, projmatrix); это неправильно и Я должен получить матрицу проекций собственной камеры?):

Vector3* GetOGLPos() 
    {Vector3 pointsOnLine[2]; 
double mvmatrix[16]; 
double projmatrix[16]; 
int viewport[4]; 
double dX, dY, dZ, dClickY,zz; 
glGetIntegerv(GL_VIEWPORT, viewport); 
glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix); 
glGetDoublev (GL_PROJECTION_MATRIX, projmatrix); 
dClickY = double (viewport[3] - mouse.yPos()); 
// OpenGL renders with (0,0) on bottom, mouse reports with (0,0) on top 
//glReadPixels(mouse.xPos(), int(dClickY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &zz); 
gluUnProject ((double) mouse.xPos(), dClickY, 0.0, mvmatrix, projmatrix, viewport, &dX, &dY, &dZ); 
pointsOnLine[0] = Vector3((float) dX, (float) dY, (float) dZ); 
gluUnProject ((double) mouse.xPos(), dClickY, 1.0, mvmatrix, projmatrix, viewport, &dX, &dY, &dZ); 
pointsOnLine[1] = Vector3((float) dX, (float) dY, (float) dZ); 

    return pointsOnLine; 
     } 

Теперь я полагаю, что у меня есть две точки, указывающие мой луч на сцене. теперь, когда я делаю кубы, я пытаюсь рассчитать расстояние линии, созданной лучом и кубом, и если оно меньше значения, я меняю цвет куба, чтобы знать, что я его выбрал (суставыОфШкелетон указывает каждый куб, который не создается скелет больше ничего, здесь я тестирую только куб номер 6 в массиве):

if(selectionMode) 
     { 

      distToLine = Vector3::PointToLineDistance3D(rayPoints[0],rayPoints[1],Vector3::Vector3(jointsOfSkeleton[6].x, 
       jointsOfSkeleton[6].y,jointsOfSkeleton[6].z)); 
      //distToLine = sqrt(distToLine); 
      if(distToLine < 0.5) 
       glColor3f(1.0,0.0,0.0); 

      else 
       glColor3f(1.0,1.0,1.0); 
     } 

, когда я нажимаю на нерелевантные позиции на окне я вижу цвета изменений куба, он не работает правильно, я смотрю расстояния на отладчике, и расстояния не выглядят правильно. и это функция, которую я использовал для поиска расстояния Line-точка:

static float PointToLineDistance3D(Vector3 a, Vector3 b, Vector3 point) 
{ 

    Vector3 lineDirection = b - a; 
    float t = (Vector3::dot(point,lineDirection) - Vector3::dot(lineDirection,a))/(Vector3::dot(lineDirection,lineDirection)); 
    Vector3 direction; 
    direction.x = a.x + (lineDirection.x *t) - point.x; 
    direction.y = a.y + (lineDirection.y *t) - point.y; 
    direction.z = a.z + (lineDirection.z *t) - point.z; 

    float ShortestDistance = sqrtf((direction.x*direction.x)+(direction.y*direction.y)+(direction.z*direction.z)); 

    return ShortestDistance; 

} 

ответ

2

Это, как я хотел бы написать PointToLineDistance3D:

static float PointToLineDistance3D(const Vector3 &a, const Vector3 &b, const Vector3 &point){ 
    Vector3 lineDirection = Vector3::normalize(b - a), pointDirection = point - a; 
    float t = Vector3::dot(pointDirection,lineDirection); 
    Vector3 projection = a + (lineDirection * t); 

    float ShortestDistance = (projection - point).length(); 
    return ShortestDistance; 
} 

Я сделал предположение о том, что:

  • Vector3 класс имеет метод length, с очевидным значением,
  • есть * opera то для масштабирования векторов
  • есть функция normalize, которая возвращает ... нормализованный вектор (это также можно сделать методом, позволяющим избежать создания дополнительного объекта).

Идея заключается в том, чтобы вычислить проекцию point на луче, а затем вычислить расстояние между projection и point. Как вы можете видеть, алгоритм немного отличается от вашей реализации, особенно в расчете t. Возможно, в этом и заключается ваша проблема.

Для того, чтобы проверить код, приведенный выше, я написал небольшую программу, в которой построена 3x3 стенка кубов на плоскости XY, а центр стены - <0,0,0>. Мне удалось работать без проблем, даже при перемещении камеры. Единственная проблема была в том, что касается системы координат мыши, которая идет сверху вниз (т. Е. Координата мыши Y увеличивается вниз), что является противоположностью естественной оси OpenGL Y. Для компиляции и запуска необходима библиотека SDL.

#include <iostream> 

#include <GL/gl.h> 
#include <GL/glu.h> 
#include <SDL/SDL.h> 
#include <unistd.h> 

#include <Vector3.h> 

#define WIDTH 800 
#define HEIGHT 600 

GLuint box; 
int highlight[2]; // position of the cube in the wall 
const float cube_width = 5.0; 

Vector3 position(0,0,-25); // camera position 

// build the cube display list 
void setup_cube(){ 

    const float w = cube_width; 

    float w0 = -w, h0 = -w, w1 = w, h1 = w; 

    box = glGenLists(1); 
    glNewList(box, GL_COMPILE); 
    glBegin(GL_QUAD_STRIP); 
     glVertex3f(w0, h1, w0); 
     glVertex3f(w0, h0, w0); 
     glVertex3f(w1, h1, w0); 
     glVertex3f(w1, h0, w0); 
     glVertex3f(w1, h1, w1); 
     glVertex3f(w1, h0, w1); 
     glVertex3f(w0, h1, w1); 
     glVertex3f(w0, h0, w1); 
    glEnd(); 
    glBegin(GL_QUAD_STRIP); 
     glVertex3f(w1, h1, w0); 
     glVertex3f(w1, h1, w1); 
     glVertex3f(w0, h1, w0); 
     glVertex3f(w0, h1, w1); 
     glVertex3f(w0, h0, w0); 
     glVertex3f(w0, h0, w1); 
     glVertex3f(w1, h0, w0); 
     glVertex3f(w1, h0, w1); 
    glEnd(); 
    glEndList(); 
} 

void setup_scene(){ 

    float r = WIDTH/HEIGHT; 

    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    glFrustum(-r, r, -1, 1, 1, 1024); 

    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity(); 

    glTranslatef(position[0],position[1],position[2]); 

    glEnable(GL_CULL_FACE); 
    glEnable(GL_DEPTH_TEST); 
} 

void draw_scene(){ 

    const float w = cube_width; 
    int i = 0, j = 0; 

    for (int i = -1; i < 2; i++) { 
    for (int j = -1; j < 2; j++) { 
     float x = w * 2 * i, y = w * 2 * j; 

     if (highlight[0] == i && highlight[1] == j) 
     glColor3f(0.0, 1.0, 0.0); 
     else 
     glColor3f(1.0, 0.0, 0.0); 

     glPushMatrix(); 
     glTranslatef(x,y,0); 
     glCallList(box); 
     glPopMatrix(); 
    } 
    } 
} 

void aim(float xm, float ym_){ 

    const float w = cube_width; 
    float ym = HEIGHT - ym_; 

    GLdouble model[16]; 
    GLdouble proj[16]; 
    GLint view[16]; 

    glGetDoublev(GL_MODELVIEW_MATRIX, model); 
    glGetDoublev(GL_PROJECTION_MATRIX, proj); 
    glGetIntegerv(GL_VIEWPORT, view); 
    highlight[0] = -5; 
    highlight[1] = -5; 

    for (int i = -1; i < 2; i++) { 
    for (int j = -1; j < 2; j++) { 
     float x = w * 2 * i, y = w * 2 * j; 
     double ox, oy, oz; 
     Vector3 centre(x,y,0); 
     gluUnProject(xm, ym, 0, model, proj, view, &ox, &oy, &oz); 
     Vector3 p0(ox,oy,oz); 
     gluUnProject(xm, ym, 1, model, proj, view, &ox, &oy, &oz); 
     Vector3 p1(ox,oy,oz); 
     float d = PointToLineDistance(p0,p1,centre); 
     if (d < w) { 
     highlight[0] = i; 
     highlight[1] = j; 
     return; 
     } 
    } 
    } 
} 

int main(){ 

    SDL_Surface *screen; 

    SDL_Init(SDL_INIT_VIDEO); 

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); 
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); 
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); 
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); 
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 

    if ((screen=SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_OPENGL)) == NULL) { 
    SDL_Quit(); 
    return -1; 
    } 

    setup_cube(); 

    while (1) { 
    SDL_Event event; 
    setup_scene(); 

    while(SDL_PollEvent(&event)){ 
     switch(event.type){ 
     case SDL_MOUSEMOTION: 
     aim(event.motion.x, event.motion.y); 
     break; 
     case SDL_KEYDOWN: 
     { 
      switch (event.key.keysym.sym){ 
      case SDLK_ESCAPE: 
      SDL_Quit(); 
      exit(1); 
      case SDLK_LEFT: 
      position.add(Vector3(1,0,0)); 
      break; 
      case SDLK_RIGHT: 
      position.sub(Vector3(1,0,0)); 
      break; 
      case SDLK_UP: 
      position.add(Vector3(0,0,1)); 
      break; 
      case SDLK_DOWN: 
      position.sub(Vector3(0,0,1)); 
      break; 
      case SDLK_PAGEDOWN: 
      position.add(Vector3(0,1,0)); 
      break; 
      case SDLK_PAGEUP: 
      position.sub(Vector3(0,1,0)); 
      break; 
      } 
     } 
     default: 
     break; 
     } 
    } 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
    draw_scene(); 
    SDL_GL_SwapBuffers(); 
    usleep(10); 
    } 

    return 0; 
} 

Источник, указанный выше, отображает правильный куб, направленный указателем мыши. Можно перемещаться с помощью стрелок и кнопок вверх/вниз.

+0

позвольте мне попробовать, я скоро вернусь – user667222

+1

В моем классе 'Vector3' есть ваши предположения. но эта модификация в коде не решила проблему. как я уже сказал, я использую матрицу проекции по умолчанию. Разве вы не думаете, что я являюсь причиной проблемы? как я могу использовать его вместо 'glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);' – user667222

+1

хорошее понимание: код, в котором вы используете свою собственную матрицу, не отображается, но вы, вероятно, установите его в конвейере преобразования OpenGL с помощью 'glLoadMatrix'. Если это так, то вам нужно только предоставить эту же матрицу для 'gluUnproject', а не тот, который вы получите с помощью' glGetDouble': это может быть или не быть одинаковой матрицей в зависимости от того, что вы делаете между рендерингом и пользователем код взаимодействия. Я переопределил функцию, чтобы проверить ее, и, возможно, ваш код каким-то образом оптимизирован, я не мог видеть сходство с моим. – didierc

Смежные вопросы