I am rendering a scene with a simple camera in opengl, this is rendering loop:
while(hasFocus)
{
UpdateFrame(GetElapsedTimeInSeconds());
RenderFrame();
SwapBuffers(g_hDC);
}
what I want to do is to select some object in the scene but some transformations act weird.
before rendering the scene a ray went through the scene from view point of camera to mouse,near plane until it reaches far plane. I draw this ray to see whether I am doing the calculations right or not. as you see in the snapshot below the ray is coming from strange position and not from my camera's "eye".
I have my own camera class. this is my initialization:
Camera myCam(0.0f,2.0f,0.0f);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60,(g_windowWidth)/ (g_windowHeight), CAMERA_ZNEAR, CAMERA_ZFAR);
and some other initializations.
in RenderFramefunction I am doing my picking and rendering stuff:
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glDisable(GL_LIGHTING);
glViewport( 0,0,g_windowHeight, g_windowHeight);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
myCam.setView(); // just set the view matrix with GL_MODELVIEW
myCam.rotateLoc(-0.5, 0,1,0);
glColor3f(1.0,1.0,1.0);
Vector3* pointss = GetRayPoints(); // retrieve the two point for my Ray
setIntersectionPoints(pointss[0],pointss[1]);
RenderFloor();
DrawSpheres(); // inside this I test hit of the ray created by points and the spheres
this is GetRayPoints:
POINT mymouse;
GetCursorPos(&mymouse);
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] - mymouse.y);
const GLfloat* mtrx = myCam.getViewMat();
mtrx = myCam.getViewMat();
gluUnProject ((double) mymouse.x, dClickY, 0.0,mtrx,projmatrix, viewport, &dX, &dY, &dZ);
pointsOnLine[0] = Vector3( (float) dX, (float) dY, (float) dZ );
gluUnProject ((double) mymouse.x, dClickY, 1.0,mtrx,projmatrix, viewport, &dX, &dY, &dZ);
pointsOnLine[1] = Vector3( (float) dX, (float) dY, (float) dZ );
return pointsOnLine;
and inside DrawSpheres I am checking hit or miss by point-line intersection mathematics and it works well and actually I am selecting the spheres but not in the pixel under the mouse as you saw in snapshot. the problem is just the wrong transformation and therefore position of Ray !
any insight ? is this Ray in camera coordinates and I should bring it into world space by inverse of viewmatrix ?!
Related
I am drawing a texture with z=0 as visible in the following image:
My near plane and far plane settings are 0.001 and 120.0. I can move around and zoom in and out using my keyboard.
What I want is to identify the world position of a mouse cursor position (e.g. red in the image) while using gluunproject.
Problem: The results posX, posY and posZ are somewhat close but not correct. E.g. for the center of the visible view (blue) I get posX=8.90000 and posY=53.80000. However if I move the mouse to the left I get posX=8.89894 (should be 3.xx). If I move the cursor to the right I get 8.90103 instead of approx. 14-16.
Here is my code:
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
perspectiveGL(65.0,(GLdouble)width()/(GLdouble)height(), 0.001, 120.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Go to current World-Position
glTranslated(-m_posWorld.x(), -m_posWorld.y(), -m_posWorld.z());
// Try to project Screen(Mouse)-Coordinates to World Coordinates
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
winX = (float)m_lastPos.x(); // m_lastPos is a Point storing the last Mouse Cursor Position within the OpenGL-Widget (without borders)
winY = (float)viewport[3]-(float)m_lastPos.y();
winZ = -m_posWorld.z();
GLdouble posX, posY, posZ;
int success = glhUnProjectf( winX, winY, -m_posWorld.z(), modelview, projection, viewport, &posX, &posY, &posZ);
What might be the problem here?
To "unproject" something, you need window-space x,y and z.
m_posWorld.z almost certainly is not in window-space.
Window-space z is what the depth buffer stores, it is a value between [0.0, 1.0] (assuming default depth range). Because the depth buffer gives you this value, you can easily figure it out by reading the depth buffer back at (winX, winY) (though performance will suffer).
You can transform your world-space coordinate into window-space:
vec4 posClip = projectionMatrix * viewMatrix * m_posWorld; // Project to clip-space
vec4 posNDC = posClip / posClip.w; // Perspective Divide
float winZ = (posNDC * vec4 (0.5) + vec4 (0.5)).z; // Adjust for Depth Range
The approach discussed above will work much quicker, but requires some matrix math. You are currently relying completely on OpenGL itself to do your matrix math for you, and if you want to keep doing that your only option will be to read the depth buffer back:
GLfloat winZ;
glReadPixels (winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
I'm working on a 2D image viewer, I want to retrieve openGL mouse position on texture but I can't get it to work if glTranslatef() or glScalef() calls are made on the modelview matrix.
I'm using a QGLWidget , of the famous Qt library.
Here are the important calls :
Resize function :
void ViewerGL::resizeGL(int width, int height){
glViewport (0, 0, width, height);
Display function :
void ViewerGL::paintGL()
{ int w = width();
int h = height();
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
//transX,transY are for panning around the image in the viewer
float left = (0.f+transX) ;
float right = (w+transX) ;
float bottom = (h-transY);
float top = (0.f-transY) ;
glOrtho(left, right, top, bottom, -1, 1);
... later in paintGL:
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
//padx,pady are used to translate the image from the bottom left corner
// to the center of the viewer
float padx,pady;
padx= ((float)width() - _dw.w()*zoomFactor)/2.f; // _dw.w is the width of the texture
pady =((float)height() - _dw.h()*zoomFactor)/2.f ;// _dw.h is the height of the texture
glTranslatef( padx , pady, 0);
//zoomX,zoomY are the position at which the user required a zoom
glTranslatef(-zoomX,-zoomY, 0.f);
glScalef(zoomFactor, zoomFactor,0.f);
glTranslatef(zoomX ,zoomY, 0.f);
Now here is my function to retrieve the openGL coordinates :
QPoint ViewerGL::openGLpos(int x,int y){
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX=0, winY=0, winZ=0;
GLdouble posX=0, posY=0, posZ=0;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
winX = (float)x;
winY = height()- y;
if(winY == 0) winY =1.f;
glReadPixels( x, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
return QPoint(posX,posY);
}
So far , here is what I noticed:
The code like this returns always (0,0) and GLU_FALSE is returned from gluUnproject. I read somewhere on a forum that it could be because of the modelview matrix, so I put the identity matrix instead, but,if I do it, I get exactly the coordinates of the mouse in the window...
Before , I dealt with the zoom using the orthographic projection, but I couldn't make it work perfectly, so to make it simpler I decided to retrieve openGL position, and use glTranslatef/glScalef instead .
If I remove all the translating / scaling stuff in the paintGL function, everything is working...but the zoom doesn't work :x)
I'm requesting your help to make this damned zoom to point working, using the gluUnProject solution;)
Aigth , nevermind, I found the solution : I was zeroing out the z in glScalef(x,y,z)
so it made the matrix non-invertible...
I have a 2D application which is an image viewer.
I have the ability to pan around the image in the window and I have a basic zoom.
Now I'd like to improve the zoom so it zoom to the point under the mouse.
So far I've read everything possible on google and i've come close to something working,
but still can't find something working, here is what I got:
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
// w and h are respectivly the width and height of the window
// newzoom_pos is the point in openGL coordinates where the user requested the zoom
// zoomFactor is between [0.1,10.0] , 0.1 -> 1.0 means downscale, 1.0 -> 10.0 means upscale
// transX and transY are used to pan around the image
float left = (0.f+transX -newzoom_pos.x())/zoomFactor +newzoom_pos.x();
float right = (w+transX -newzoom_pos.x())/zoomFactor +newzoom_pos.x();
float bottom = (h-transY-newzoom_pos.y())/zoomFactor +newzoom_pos.y();
float top = (0.f-transY -newzoom_pos.y())/zoomFactor +newzoom_pos.y();
glOrtho(left, right, top, bottom, -1, 1);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
It almost works, but it is not okay, it doesn't zoom exactly to the requested point and when the image is small and does not fit yet in the viewer entirely ( there's black around), well it is messy and does not work...
btw, here is the code when the user zoom-in, in the mouse wheel event handler:
zoomFactor+=0.1;
QPoint oglpos = openGLpos(new_zoomPoint.x(), new_zoomPoint.y());
this->newzoom_pos = oglpos;
the openGLpos function is the following:
QPoint ViewerGL::openGLpos(int x,int y){
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX=0, winY=0, winZ=0;
GLdouble posX=0, posY=0, posZ=0;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
winX = (float)x;
winY = viewport[3]- y;
if(winY == 0) winY =1.f;
glReadPixels( x, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
GLint b=gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
if(b==GLU_FALSE) cout << "failed unprojection" << endl;
return QPoint(posX,posY);
}
Could anybody help me through this?
I am trying to use gluUnProject to get my mouse coordinates into world coordinates, however it seems to not be working, or I am just misunderstanding the functionality of the glUnProject function, here is the code I am working with, my matrices all check out fine and as for the -300 on the mouse x coordinate, I am using a C++ Win32 Dialog and the ScreenToClient is giving me funky results.
int appWidth = CApplication::GetInstance()->GetWidth();
int appHeight = CApplication::GetInstance()->GetHeight();
float fAspect = (float)appWidth / (float)appHeight;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f, fAspect, 0.1f, 100000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(m_vecCamera.x, -m_vecCamera.y, m_vecCamera.z);
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glEnable(GL_DEPTH);
//Retrieve the Model/View, Projection, and Viewport Matrices
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
//Retrieve the Mouse X and the flipped Mouse Y
winX = (float)pInput->msg.param1-300.0f;
winY = (float)viewport[3]-(float)pInput->msg.param2;
glReadPixels( int(winX), int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
This is however giving me coordinates relative to the center of my screen, and I am assuming is relative to my camera, I also tried implementing my own function
Vector2f MouseUnProject(int x, int y)
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glEnable(GL_DEPTH);
//Retrieve the Model/View, Projection, and Viewport Matrices
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
//Retrieve the Mouse X and the flipped Mouse Y
winX = (float)x;
winY = (float)viewport[3]-y;
glReadPixels( int(winX), int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
double projectionX, projectionY;
double viewX, viewY;
double worldX, worldY;
//Convert from Screen Coords to Projection Coords
projectionX = (double)winX / ((double)viewport[2]/2.0) - 1.0;
projectionY = (double)winY / ((double)viewport[3]/2.0) + 1.0;
//Convert from projection Coords to View Coords
viewX = projectionX * modelview[14];
viewY = projectionY * modelview[14];
//Convert from View Coords to World Coords
worldX = viewX + modelview[12];
worldY = viewY - modelview[13];
return Vector2f(worldX, worldY);
}
It works to a certain mount, but when moving the camera, the numbers instantly go off a bit, the conversion from projection to view coords 'seems' to be ok, and the projection coords are definitely good.
I would really prefer to use glUnProject rather then my own function, but I can't get it to work for the life of me and all of the google searches I found don't seem to answer my question. What exactly does the GL documentation mean by 'object space' perhaps my understanding of that is wrong, and if so what do I additionally have to do to get my coordinates in the right space?
was posted a year ago,but anyways....so you are getting coordinates relative the the screen because you made a call to gluPerspective. this call internally calls glfrustum which will generate normalized coordinates in the range {-1, 1}. However if you called glfrustum directly with your near/far values you would have got the result from gluUnproject in that range.
To get back to your map editor coordinates, simply take the result from gluUnproject and manually range convert back to your editor coordinate system, ie{-1,1} => {0, max}
To get started you should test gluUnProject by inputting (0,0), (midX, midY), (maxX, maxY) and the result from gluUnProject should be (-1, -1, depth), (0, 0, depth) and (1, 1, depth). If you setup the projection matrix using glFrustum then the above result will be returned in the near/far range.
how to handle clik on specific 3d or 2d object in opengl, for example i have the following code
void Widget::drawCircle(float radius) {
glBegin(GL_TRIANGLE_FAN);
for (int i = 0; i < 360; i++) {
float degInRad = i*DEG2RAD;
glVertex2f(cos(degInRad) * radius, sin(degInRad) * radius);
}
glEnd();
}
So i need to handle click on this circle, is there any solutions for this problem?
When I need to detect clicks, I usually do my ordinary draw loop, but instead of drawing the objects with texturing, lighting and other effects enabled, I draw each of them with flat/no shading, each in a different color. I then check the color on the pixel the mouse is on, and map backwards from the color returned from the framebuffer to the object that I drew with that color.
Perhaps this technique is useful for you, too.
Take a look into this nehe tutorial item. It is very complex, but it shows how opengl picking works. In my opinion, if you need it, you are better with some game engine then with opengl.
Here is another (similar) way of selecting items in opengl.
opengl mouse raytracing will provide you with all details how to select items in opengl. This thread even provides the code how it is done :
Vector3 World::projectedMouse(float mx, float my){
GLdouble model_view[16];
GLint viewport[4];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble dx, dy, dz;
glGetDoublev(GL_MODELVIEW_MATRIX, model_view);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
winX = (float)mx;
winY = (float)viewport[3] - (float)my;
glReadPixels ((int)mx, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, 0, model_view, projection, viewport, &bx, &by, &bz);
Vector3 pr2 = Vector3(bx, by, bz);
glColor3f(1,0,0);
glBegin (GL_LINE_LOOP);
glVertex3f(player->getPosition().x, player->getPosition().y + 100, player->getPosition().z); // 0
glVertex3f(pr.x,pr.y,pr.z); // 1
glVertex3f(player->getPosition().x, player->getPosition().y, player->getPosition().z); // 0
glEnd();
return pr;
}