В Qt Forums я нашел this question и искрился в моем любопытстве.Перевод координат мыши в координаты модели в OpenGL при поворотах
Я нашел очень простой пример в Qt для отображения куба и изменения логики куба для создания куба с длиной стороны 1 единицы.
Затем я попытался щелкнуть по модели и показать координаты области, в которой я нажал.
Если нет никаких поворотов, кажется, что сборщик отлично работает. Но! Если я не буду комментировать вращения (или один из них) в paintGL
, я получаю «неправильные» значения.
Например:
- Нет вращений и нажмите почти на крайней левой границе куба: метод
gluUnProject
говорит, что я нажал на точку{ -0.49075, 0.234, 0.5 }
, которая, кажется, хорошо.
- Повороты включен и нажмите почти на крайней левой границе куба: Я получаю точку
{ -0.501456, 0.157555, -0.482942 }
, что кажется неправильным. Координатаx
находится в диапазоне от-0.5, 0.5
.
Я думаю координаты преобразование нормально. Я изучаю 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
предложение.
Oh! Благодаря! Я никогда не думал о неточности. Я попробую ваше предложение! –
Кроме того, я заметил, что при нажатии на обод модели я получаю разумные значения (с эпсилон, как вы любезно объяснили). Но если я нажимаю на обод, но снаружи, на черном фоне, я получаю что-то вроде '4.66933, -1.30734, -8.8131'. Есть ли способ «отключить» это или мне нужно проверить возвращенную точку и посмотреть, есть ли у нее разумные границы? –
@ AdriC.S .: Что происходит, вы читаете очищенную часть буфера глубины. Глубина по умолчанию - ** 1.0 **. Так как маловероятно, что вы когда-либо нажмете на что-то с глубиной, точно равной ** 1.0 ** (это будет что-то, что лежит _exactly_ на далекой плоскости отсечения), вы, вероятно, можете просто предположить, что если вы читаете ** 1.0 ** для глубины, что под курсором ничего не было. –