Im using gluUnProject() to get the screen 2d coordinate in 3d world coordinate. I take 4 positions from each corner of the screen to get the area of visible objects.
How to check which points are inside that "rectangle" ?, i have no idea about the terms or anything. The image below shows what that "rectangle" looks like:
Are you trying to find which 3D point are visible by a camera? If so, you might find some interesting informations on this website: http://www.lighthouse3d.com/opengl/viewfrustum/.
In the following image, we can see the view frustum and your selection frustum (in red). Applying frustum visibility checks to your selection frustum should the same algorithm as the one used for frustum culling.
If you want a quick and non optimized solution:
GLdouble model_view[16];
glGetDoublev(GL_MODELVIEW_MATRIX, model_view);
GLdouble projection[16];
glGetDoublev(GL_PROJECTION_MATRIX, projection);
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
for(unsigned i=0; i<points.size(); ++i){
GLdouble winX, winY, winZ;
gluProject(points[i].x, points[i].y, points[i].z, model_view, projection, viewport, &winX, &winY, &winZ);
if(selectionMinX <= winX && winX <= selectionMaxX && selectionMinY <= winY && winY <= selectionMaxY && winZ>=0 && winZ<=1){
/// point is selected
}
}
Related
I am trying to write a OpenGL application where an object is fired from a point( In this case 0,0,0 ) and flies to the x,y position of the mouse. This is the system I am currently using:
Create an object at the point 0,0,0
Get the x,y position of the mouse.
Convert the mouse to 3d co-ordinates.
Get a line between the start point and (mouse-X,m mouse-Y, far plane ) where the far plane is set to 4,294,967,295.
Use the parametric line equation to move the object along this line.
The problem is that the x,y on the far plane does not seem to correspond to the x,y mouse position so the object flies on the wrong line. I am pretty sure that the line/para,etric equation part is working ok, but the conversion between 2d and 3-d space may not. Here is what I have tried:
First convert to window co-ordinates:
POINT *mouse = new POINT();
mouse->x = mousePosition3D.x;
mouse->y = mousePosition3D.y;
ScreenToClient( windowHandle, mouse );
Then to 3-d co-ordinates
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
OBJ_TriCo returnMe;
//All matrices in use need to be retrived
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
//Set the co-ords to lookup, based on the mouse position passed to this
winX = (float)x;
winY = (float)viewport[3] - (float)y;
//Set the z to 0.99, for some reason the object will fly totally incorrectly otherwise
winZ = 0.999; //Get a point on the bettween FAR and NEAR-Clipping planes
//Convert the co-ords
gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
//Return there values
returnMe.x = posX;
returnMe.y = posY;
returnMe.z = posZ;
return returnMe;
I am wondering has any body do anything similar to this or what maths i may need to do to get the objects to fly along the correct line.
You can calculate world space ray direction from screen-space coordinates (normalized to range [-1, 1]) like this:
vec4f r = projection_to_view_matrix * vec4f(screen_x, screen_y, 0, 1);
vec3f rdir = transpose(world_to_view_rotation_matrix) * vec3f(r.x, r.y, r.z);
I am trying to have a ray be traced from a mouse click on the screen into 3D space. From what I understand, the default position and orientation looks like this:
from this code:
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY;
GLdouble near_x, near_y, near_z;
GLdouble far_x, far_y, far_z;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
winX = (float)mouse_x;
winY = (float)viewport[3] - (float)mouse_y;
gluUnProject(winX, winY, 0.0, modelview, projection, viewport, &near_x, &near_y, &near_z);
gluUnProject(winX, winY, 1.0, modelview, projection, viewport, &far_x, &far_y, &far_z);
My question is how do you change the position and the angle of the near and far planes so that all mouse clicks will appear to be from the camera's position and angle in 3D space?
Specifically, I am looking for a way to position both the projection planes' center at 65° below the Z axis along the line (0,0,1) -> (0,0,-tan(65°)) [-tan(65°) ~= -2.145] with both the near and far planes perpendicular to the view line through those points.
My question is how do you change the position and the angle of the near and far planes so that all mouse clicks will appear to be from the camera's position and angle in 3D space?
Feeding the projection and modelview matrices into gluUnProject takes care of that. Specifically you pass the projection and modelview matrix used for rendering into gluUnProject so that the unprojected coordinates match those of the local coordinate system described by those matrices.
I need to detect mouse motion and draw a ball at the mouse's position. I need the ball to be in world coordinate. So I'm trying to use glUnProject for this task and seems I'm not succeeding till now. This is my motionFunc:
void motionFunc( int x, int y)
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
winX = (float)x;
winY = (float)viewport[3] - (float)y;
glReadPixels( x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ); //printf("winz: %f posz: %f\n",winZ,posZ);
Ball.x=posX;
Ball.y=posY;
Ball.z=posZ;
//printf("%f %f %f\n",Ball.x,Ball.y,posZ);
glutPostRedisplay();
}
now, I added a breakpoint on glutPostRedisplay. Turns out when I click-drag mouse, the Ball's coordinates(Ball.x,Ball.y,Ball.z) are something like:
(Ball).x -727.175354
(Ball).y 407.310242
(Ball).z -865.000610
why is the z coordinate so far? My camera is at z=+135. And other objects in my model are like, at z= -3 to +3. I need the ball's z coordinate to be in the same range.
now, what exactly is winZ? Here, I checked it always turns out to be 1.00. I tried to hardcode winZ and I found at winZ=0.85, the ball seems like to be always under the mouse(I can drag the ball with my mouse and the ball is always under the pointer). But then the Ball's coordinates are like:
(Ball).x -4.67813921
(Ball).y 2.57806134
(Ball).z 128.370895
which is so close to the camera but x and y coordinates are not good for me. they always come out to be near the origin. which is not what I want. My other objects' x and y coordinates have a wider range.
Finally, my question is, what is the correct way to do glUnproject or something of the same sort?
A 2D mouse coordinate cannot unambiguously unmapped to a 3D world coordinate. The 2D coordinate corresponds with a line in 3D space. The winz influences which point on this line is returned. When you use 0.0 for winz you will get the point at the near clipping plane. When you use 1.0 you will get the point at the far clipping plane.
If you are using a depth buffer you could retrieve the value from the depth buffer using the glReadPixels function and use that as winz. Here a piece of code from a toy project of mine in Java
FloatBuffer depth = BufferUtils.createFloatBuffer(1);
glReadPixels(mouse_x, mouse_y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, depth);
depth.rewind();
FloatBuffer farPos = BufferUtils.createFloatBuffer(3);
GLU.gluUnProject(mouse_x, mouse_y, depth.get(),
mainContext.getModelviewMatrix(),
mainContext.getProjectionMatrix(), viewport, farPos);
Most is the same in C/C++ except for the weird NIO buffers.
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;
}
To zoom into the mouse position I was using:
glTranslatef(current.ScalePoint.x,current.ScalePoint.y,0);
glScalef(current.ScaleFactor,current.ScaleFactor,current.ScaleFactor);
glTranslatef(-current.ScalePoint.x,-current.ScalePoint.y,0);
so basically I translate to the new origin (the mouse position) then scale by the current scale factor, then translate back.
This kind of works generally well, but it can be a bit buggy. My issue is really that now I'v introduced a camera offset so I tried something like this:
glTranslatef(controls.MainGlFrame.GetCameraX(),
controls.MainGlFrame.GetCameraY(),0);
glTranslatef(current.ScalePoint.x,current.ScalePoint.y,0);
glScalef(current.ScaleFactor,current.ScaleFactor,current.ScaleFactor);
glTranslatef(-current.ScalePoint.x,-current.ScalePoint.y,0);
But this did not work as I intended. How could I properly do this knowing that:
The matrix's origin is the top left corner (0,0)
1 unit == 1 pixel
My scale factor
My camera's position relative to how far it is from (0,0) (the origin) and
the mouse position (screen to client).
Thanks
It is more safe (and also for code reuse) to un-project the mouse coordinate point (from window coordinates to model coordinates) first even though you know how projection is done.
You can use the following function:
void unProject(int ix, int iy, int &ox, int &oy)
{
// First, ensure that your OpenGL context is the selected one
GLint viewport[4];
GLdouble projection[16];
GLdouble modelview[16];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
int xx = ix;
int yy = viewport[3] - iy;
GLdouble x, y, z;
gluUnProject(xx, yy, 0 /*check*/, modelview, projection, viewport, &x, &y, &z);
ox = (int) x;
oy = (int) y;
}
The output then is the correct point on the model coordinates for your zooming