I have an OpenGL program that basically just renders a bunch of points. I need to find the world-space coordinates from an arbitrary cursor position, which I do like this:
glm::mat4 modelview = graphics.view*graphics.model;
glm::vec4 viewport = { 0.0, 0.0, windowWidth, windowHeight };
float winX = cursorX;
float winY = viewport[3] - cursorY;
float winZ;
glReadPixels(winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
glm::vec3 screenCoords = { winX, winY, winZ };
glm::vec3 cursorPosition = glm::unProject(screenCoords, modelview, graphics.projection, viewport);
This works fine if the cursor is on an object, but if it's on an empty part of the screen (i.e. most of it), it's assumed to be on the far clipping plane (winZ = 1), and glm::unProject returns inf values. Ideally, I'd like to pass it a different winZ corresponding to the xy plane at z=0 in world space coordinates, but I can't figure out how to get that value.
As there is a glm::unProject function, the is a glm::project function too.
If you want to know the depth of a point, which is located on parallel plane to the view space, with a z-coordinate of 0, then you have to glm::project a point on this plane. It can be any point on the plane, because you are only interested in its z-coordinate. You can use the origin of the world for this:
glm::vec3 world_origin{ 0.0f, 0.0f, 0.0f };
glm::vec3 origin_ndc = glm::project(screenCoords, view, graphics.projection, viewport);
float depth0 = world_origin[2];
where view is the view matrix, which transforms from world space to camera space (got from glm::lookAt).
So the implementation may look like this:
glm::mat4 modelview = graphics.view * graphics.model;
glm::vec4 viewport = { 0.0, 0.0, windowWidth, windowHeight };
float winX = cursorX;
float winY = viewport[3] - cursorY;
float depth;
glReadPixels(winX, winY- cursorY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
const float epsi = 0.00001f;
if ( depth > 1.0f-epsi )
{
glm::vec3 world_origin{ 0.0f, 0.0f, 0.0f };
glm::vec3 origin_ndc = glm::project(world_origin, graphics.view, graphics.projection, viewport);
depth = origin_ndc[2];
}
glm::vec3 screenCoords{ winX, winY, depth };
glm::vec3 cursorPosition = glm::unProject(screenCoords, modelview, graphics.projection, viewport);
Related
I'm trying to get object space coordinates from the mouse position. I have some standard rendering code, which works well.
The problem is with the mouse picking code. I have tried lots of things and gone through similar questions but I can't seem to understand why it's not working.
I expect the result to return a x, y coordinates within [-1, 1] based on the position of the mouse over the object. I do get points within [-1, 1], but they are extremely skewed, such as (2.63813e-012, -1, 300).
Unproject code:
int z;
glReadPixels(mouse_pos_[0], int( navWidget->height() - mouse_pos_[1]), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);
glm::vec3 win(mouse_pos_[0], navWidget->height() - mouse_pos_[1], z);
glm::vec4 viewport(0, 0, navWidget->width(), navWidget->height());
auto result_vec3 = glm::unProject(win, view * model1, proj, viewport);
auto result = glm::normalize(glm::vec2(result_vec3.x, result_vec3.y)); // < -- I normalize here since that gave good results without the translate
bool left_image = true;
if (!(result.x <= length_per_side && result.x >= -length_per_side &&
result.y <= length_per_side && result.y >= -length_per_side)) {
// do stuff
}
}
Rendering code:
float fov = 2*(atan((camProjModule->camResY()/2*camProjModule->camPixSizeY()) /
camProjModule->camFocalLength()) / M_PI * 180.0);
float znear = 1.0f;
float zfar = 6000.0f;
//float aspect = 1024.f / 683.f;
float aspect = navWidget->width() / navWidget->height();
glm::mat4 proj = glm::perspective(fov, aspect, znear, zfar);
float required_height =(float)( znear * tan((fov / 2.f) * M_PI / 180.f));
float eye_distance = znear / required_height * ((float)(navWidget->height()) / 2.f);
eye_distance = 300.f;
glm::mat4 view = glm::lookAt(glm::vec3(0.f, 0.f, 1.f * eye_distance), glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 1.f, 0.f));
glUseProgram(correspond_shader_);
glBindVertexArray(quad_vao_);
glUniform3f(colorLoc, 1.0f, 1.0f, 1.0f);
// draw left
if (left_correspond_texture_) {
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, left_correspond_texture_);
glUniform1i(drawTexLoc, left_correspond_texture_);
}
GLint proj_loc = glGetUniformLocation(correspond_shader_, "proj");
GLint view_loc = glGetUniformLocation(correspond_shader_, "view");
GLint draw_tex_loc = glGetUniformLocation(correspond_shader_, "drawTex");
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, glm::value_ptr(proj));
glUniformMatrix4fv(view_loc, 1, GL_FALSE, glm::value_ptr(view));
float ratio = 1024.f / 683.f;
float height = navWidget->height() / 2.f;
float ratio_to_multiply = height / 2.f;
glm::vec3 translation_vector = glm::vec3(0.f, height / 2.f, 0.f); // < --- If I remove this translation I get results that seem to be correct, and can be used after normalizing the x and y
glm::mat4 left_model = glm::scale(glm::translate(glm::mat4(1.f), translation_vector), glm::vec3(ratio * ratio_to_multiply, ratio_to_multiply, 1.f));
glm::mat4 right_model = glm::scale(glm::translate(glm::mat4(1.f), -1.f * translation_vector), glm::vec3(ratio * ratio_to_multiply, ratio_to_multiply, 1.f));
glUniformMatrix4fv(glGetUniformLocation(correspond_shader_, "model"), 1, GL_FALSE, glm::value_ptr(left_model));
glDrawArrays(GL_TRIANGLES, 0, 6); //, GL_UNSIGNED_INT, NULL);
EDIT: I think my question needs to be improved. I'm drawing two quads and rendering separate textures to it. What I want to do is get the mouse coordinates as normalized texture coordinates depending on which quad it is.
I see that you are using glm library. You can get mouse coordinate/ray direction using unprojection method.
glm::vec2 screenPos(mousePos.x, mousePos.y);
screenPos.y = height - screenPos.y;
float aspect = width / height;
glm::vec4 viewport = glm::vec4(0.0f, 0.0f, width , height);
glm::mat4 proj = glm::perspective(75.0f, aspect, 0.1f, 10000.0f);
glm::vec3 a (screenPos.x, screenPos.y, 0);
glm::vec3 b (screenPos.x, screenPos.y, 1);
glm::vec3 result = glm::unProject(a, viewMatrix, proj, viewport);
glm::vec3 result2 = glm::unProject(b, viewMatrix, proj, viewport);
glm::vec3 pickingPos = result;
glm::vec3 pickingDir = result2 - result;
After that you can use direction and position to check for collisions
I think CrSe's answer is right too. I have done this and I can pick any point on model:
I shoot a ray from these two points (p1 and p2):
Glu.gluUnProject(tempx, viewport[3] - tempy, 0, modelMatrix, projMatrix, viewport, out x1, out y1, out z1);
p = new Point(x1, y1, z1);
Glu.gluUnProject(tempx, viewport[3] - tempy, 1, modelMatrix, projMatrix, viewport, out x1, out y1, out z1);
p1 = new Point(x1, y1, z1);
if the distance btw this ray and a vertex is less than a threshold, I pick that point. I hope it is useful.
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 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 ?!
I have a plane in 3D space, which is facing the camera that I want to be able to place at the same position as where I click. However, the position of the plane overshoots the mouse cursor. This is for a dynamic GUI that I want to be able to move about and interact with the widgets on the UI.
void mouse::unProjectMouse(float width, float height, camera* viewportCamera)
{
if(NULL == viewportCamera)
{
std::cout<<CNTRLCALL<<"camera failed! failed to un-project mouse";
} else {
glm::vec4 viewport = glm::vec4(0, 0, width, height);
glm::mat4 tmpView = viewportCamera->updateView();
glm::mat4 tmpProj = viewportCamera->updateProjection();
glm::vec3 screenPos = glm::vec3(mouseX, height-mouseY - 1.0f, 1.0f);
glm::vec3 worldPos = glm::unProject(screenPos, tmpView, tmpProj, viewport);
worldPos = worldPos / (worldPos.z * -1.0f);
mouseWorldX = worldPos.x;
mouseWorldY = worldPos.y;
mouseWorldZ = worldPos.z;
}
}
I am at a loss here as to why the plane is not aligning correctly with the mouse.
This is the fix:
void mouse::unProjectMouse(float width, float height, camera* viewportCamera)
{
if(NULL == viewportCamera)
{
std::cout<<CNTRLCALL<<"camera failed! failed to un-project mouse";
} else {
glm::vec4 viewport = glm::vec4(0.0f, 0.0f, width, height);
glm::mat4 tmpView = glm::lookAt(glm::vec3(viewportCamera->getCameraPosX(),viewportCamera->getCameraPosY(),viewportCamera->getCameraPosZ()),
glm::vec3(viewportCamera->getForward().x,viewportCamera->getForward().y,viewportCamera->getForward().z),glm::vec3(0,1,0));
glm::mat4 tmpProj = glm::perspective( 90.0f, width/height, 0.1f, 1000.0f);
glm::vec3 screenPos = glm::vec3(mouseX, height-mouseY - 1, 0.0f);
glm::vec3 worldPos = glm::unProject(screenPos, tmpView, tmpProj, viewport);
mouseWorldX = worldPos.x;
mouseWorldY = worldPos.y;
mouseWorldZ = worldPos.z;
}
}
And then to align the object to the cursors positon, the Z element of the camera had to be exact:
UIcamera->translateCameraX(0.0f);
UIcamera->translateCameraY(0.0f);
UIcamera->translateCameraZ(0.744f);
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.