2014-11-07 4 views
1

В Qt Forums я нашел this question и искрился в моем любопытстве.Перевод координат мыши в координаты модели в OpenGL при поворотах

Я нашел очень простой пример в Qt для отображения куба и изменения логики куба для создания куба с длиной стороны 1 единицы.

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

Если нет никаких поворотов, кажется, что сборщик отлично работает. Но! Если я не буду комментировать вращения (или один из них) в paintGL, я получаю «неправильные» значения.

Например:

  • Нет вращений и нажмите почти на крайней левой границе куба: метод gluUnProject говорит, что я нажал на точку { -0.49075, 0.234, 0.5 }, которая, кажется, хорошо.

Cube without rotations

  • Повороты включен и нажмите почти на крайней левой границе куба: Я получаю точку { -0.501456, 0.157555, -0.482942 }, что кажется неправильным. Координата x находится в диапазоне от -0.5, 0.5.

Cube with the two rotations enabled

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

Итак, может ли кто-нибудь сказать мне, почему при повороте я получаю неправильные координаты? Я думаю, что у меня нет базового понимания в 3D, но я не могу понять, где это.

Вот код:

main.cpp:

#include <QApplication> 
#include "GLCube.h" 

int main(int argc, char *argv[]) 
{ 
    QApplication a(argc, argv); 

    GLCube w; 
    w.resize(800,600); 
    w.show(); 

    return a.exec(); 
} 

GLCube.h

#ifndef GLCUBE_H 
#define GLCUBE_H 

#include <QtOpenGL> 
#include <QGLWidget> 

class GLCube : public QGLWidget{ 
    Q_OBJECT // must include this if you use Qt signals/slots 
public: 
    GLCube(QWidget *parent = NULL) 
     : QGLWidget(parent) { 
    } 
protected: 
    // Set up the rendering context, define display lists etc.: 
    void initializeGL(); 
    // draw the scene: 
    void paintGL(); 
    // setup viewport, projection etc.: 
    void resizeGL (int width, int height); 
    virtual void mousePressEvent(QMouseEvent *pme); 
}; 

#endif 

GLCube.cpp:

#include "GLCube.h" 

#include <cmath> 
#include <iostream> 
#include <iomanip> 

#include "GL/glu.h" 

namespace { 
    float ver[8][3] = 
    { 
     { 0.5, -0.5, 0.5 }, 
     { -0.5, -0.5, 0.5 }, 
     { -0.5, -0.5, -0.5 }, 
     { 0.5, -0.5, -0.5 }, 
     { 0.5, 0.5, 0.5 }, 
     { -0.5, 0.5, 0.5 }, 
     { -0.5, 0.5, -0.5 }, 
     { +0.5, 0.5, -0.5 } 
    }; 
    GLfloat color[8][4] = 
    { 
     {0.0,0.0,0.0, 1.0}, 
     {1.0,0.0,0.0, 1.0 }, 
     {1.0,1.0,0.0, 1.0 }, 
     {0.0,1.0,0.0, 1.0 }, 
     {0.0,0.0,1.0, 1.0 }, 
     {1.0,0.0,1.0, 1.0 }, 
     {1.0,1.0,1.0, 1.0 }, 
     {0.0,1.0,1.0, 1.0 }, 
    }; 

    void quad(int a,int b,int c,int d, int col) 
    { 
     glPointSize(5); 
     glBegin(GL_POINTS); 
      glColor4fv(color[1]); 
      glVertex3fv(ver[a]); 

      glColor4fv(color[2]); 
      glVertex3fv(ver[b]); 

      glColor4fv(color[3]); 
      glVertex3fv(ver[c]); 

      glColor4fv(color[4]); 
      glVertex3fv(ver[d]); 
     glEnd(); 

     glBegin(GL_LINES); 
      glColor4fv(color[1]); 
      glVertex3fv(ver[a]); 
      glVertex3fv(ver[b]); 

      glColor4fv(color[1]); 
      glVertex3fv(ver[b]); 
      glVertex3fv(ver[c]); 

      glColor4fv(color[1]); 
      glVertex3fv(ver[c]); 
      glVertex3fv(ver[d]); 

      glColor4fv(color[1]); 
      glVertex3fv(ver[d]); 
      glVertex3fv(ver[a]); 
     glEnd(); 

     glBegin(GL_QUADS); 
     glColor4fv(/*color[a]*/ color[col]); 
     glVertex3fv(ver[a]); 

    // glColor3fv(color[b]); 
     glVertex3fv(ver[b]); 

    // glColor3fv(color[c]); 
     glVertex3fv(ver[c]); 

    // glColor3fv(color[d]); 
     glVertex3fv(ver[d]); 
     glEnd(); 
    } 

    void colorcube() 
    { 
//  glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 
     quad(3, 2, 1, 0, 1); // bottom 
     quad(7, 6, 5, 4, 2); // top 
     quad(6, 2, 1, 5, 3); // front 
     quad(7, 3, 0, 4, 5); // back 
     quad(7, 6, 2, 3, 6); // left 
     quad(4, 5, 1, 0, 7); // right 
    } 
} 
/* 
* Sets up the OpenGL rendering context, defines display lists, etc. 
* Gets called once before the first time resizeGL() or paintGL() is called. 
*/ 
void GLCube::initializeGL(){ 
    //activate the depth buffer 
    glEnable(GL_DEPTH_TEST); 
    qglClearColor(Qt::black); 
    glEnable(GL_CULL_FACE); 
} 


/* 
* Sets up the OpenGL viewport, projection, etc. Gets called whenever the widget has been resized 
* (and also when it is shown for the first time because all newly created widgets get a resize event automatically). 
*/ 
void GLCube::resizeGL (int width, int height){ 
    glViewport(0, 0, (GLint)width, (GLint)height); 
    /* create viewing cone with near and far clipping planes */ 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    glFrustum(-1.0, 1.0, -1.0, 1.0, 15.0, 30.0); 

    glMatrixMode(GL_MODELVIEW); 
} 

/* 
* Renders the OpenGL scene. Gets called whenever the widget needs to be updated. 
*/ 
void GLCube::paintGL(){ 

    //delete color and depth buffer 
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); 

    glMatrixMode(GL_MODELVIEW); 
     glLoadIdentity(); 
     glTranslatef(0.0f,0.0f,-20.0f); //move along z-axis 
     glRotatef(30.0,0.0,1.0,0.0); //rotate 30 degress around y-axis 
     glRotatef(15.0,1.0,0.0,0.0); //rotate 15 degress around x-axis 

    colorcube(); 
// originalcube(); 
} 

void GLCube::mousePressEvent(QMouseEvent *pme) { 
    GLint viewport[4]; 
    GLdouble modelview[16]; 
    GLdouble projection[16]; 
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview); 
    glGetDoublev(GL_PROJECTION_MATRIX, projection); 
    glGetIntegerv(GL_VIEWPORT, viewport); 

    const int x = pme->x(); 
    const int y = viewport[3] - pme->y(); 

    qDebug() << "HERE: " << x << y; 

    GLfloat color[4]; 
    glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, color); 
    GLenum error = glGetError(); 
    std::cout << "RETRIEVED COLOR:" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << std::endl; 
    printf("\tERROR: %s (Code: %u)\n", gluErrorString(error), error); 
    if(GL_NO_ERROR != error) throw; 

    GLdouble depthScale; 
    glGetDoublev(GL_DEPTH_SCALE, &depthScale); 
    std::cout << "DEPTH SCALE: " << depthScale << std::endl; 
    GLfloat z; 
    glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z); 
    error = glGetError(); 
    std::cout << "X: " << x << ", Y: " << y << ", RETRIEVED Z: " << z << std::endl; 
    printf("\tERROR: %s (Code: %u)\n", gluErrorString(error), error); 
    if(GL_NO_ERROR != error) throw; 
    std::cout << std::endl << std::endl; 

    GLdouble posX, posY, posZ; 
    GLint result; 
    result = gluUnProject(x, y, z, modelview, projection, viewport, &posX, &posY, &posZ); 
    error = glGetError(); 
    std::cout << "3D point with POS: " << posX << " " << posY << " " << posZ << std::endl; 
    printf("\tERROR: %s (Code: %u)\n", gluErrorString(error), error); 
    std::cout << "\tglUnProject: " << ((GL_FALSE == result) ? "FALSE" : "TRUE") << std::endl; 
    if(GL_NO_ERROR != error) throw; 
} 

Примечание:

Я бегу это в Windows, 8,1 64 бит, Qt 4.8 и MinGW.

Кроме того, как glReadPixels и gluUnProject выход без ошибок (ERROR CODE = GL_NO_ERROR)

И glut не доступен. Только то, что OpenGL, QtOpenGL и/или glu предложение.

ответ

1

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

Проблема в том, что в буфере глубины имеется ограниченная точность, и это приводит к некоторой неточности. В действительности вы не можете ожидать, что диапазон непроектированных значений будет идеально [-0,5, 0,5]. Вам нужно будет ввести небольшой эпсилон здесь, поэтому ваш эффективный диапазон тогда будет чем-то вроде [-0,5015, 0,5015].

Возможно, вы могли бы уменьшить воздействие, увеличив точность буфера глубины и/или уменьшив диапазон между плоскостями ближнего и дальнего клипа. По умолчанию буфер глубины обычно является 24-битной фиксированной точкой, но 32-битный буфер глубины с фиксированной точкой или с плавающей запятой может немного улучшить вашу ситуацию. Однако вы никогда не решите полностью устранить эту проблему.

+0

Oh! Благодаря! Я никогда не думал о неточности. Я попробую ваше предложение! –

+0

Кроме того, я заметил, что при нажатии на обод модели я получаю разумные значения (с эпсилон, как вы любезно объяснили). Но если я нажимаю на обод, но снаружи, на черном фоне, я получаю что-то вроде '4.66933, -1.30734, -8.8131'. Есть ли способ «отключить» это или мне нужно проверить возвращенную точку и посмотреть, есть ли у нее разумные границы? –

+1

@ AdriC.S .: Что происходит, вы читаете очищенную часть буфера глубины. Глубина по умолчанию - ** 1.0 **. Так как маловероятно, что вы когда-либо нажмете на что-то с глубиной, точно равной ** 1.0 ** (это будет что-то, что лежит _exactly_ на далекой плоскости отсечения), вы, вероятно, можете просто предположить, что если вы читаете ** 1.0 ** для глубины, что под курсором ничего не было. –

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