I am doing a vtk progarm,In that I need to map window coordinates into object coordinates using vtk
I have the OpenGL code as:
winX = 0.2;//some float values
winY = 0.43;//some float values
double posX, posY, posZ;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
glReadPixels(winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ)
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
I don't know how to do this using vtk?Any help will be highly appreciated.I also googled and find out a solution for getting model view matrix like this
renderWindow->GetRenderers()->GetFirstRenderer()->GetActiveCamera()->GetViewTransformMatrix();
but I have no idea how to map window coordinates into object coordinates in vtk
Yes, VTK can map screen coordinates into world coordinates.
You can adapt the following code to your needs (below 2D case only):
// 1. Get the position in the widget.
int* clickPosition = this->GetInteractor()->GetEventPosition();
const int x = clickPosition[0];
const int y = clickPosition[1];
// 2. Transform screen coordinates to "world coordinates" i.e. into real coordinates.
vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
coordinate->SetCoordinateSystemToDisplay();
coordinate->SetValue(x, y, 0);
double* worldCoordinates = coordinate->GetComputedWorldValue(widget->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
double worldX(worldCoordinates[0]), worldY(worldCoordinates[1]);
This is an ill-posed problem because you can't find the depth information from a single 2D position. In the general case there isn't an unique solution.
But there exist some options :
You have done a projection from the object coordinates to the screen coordinates and you can keep the depth information somewhere to do the back projection.
You want to get the 3D position of an object on your screen. So you use video games technologies such as ray-tracing. The idea is to send a ray from the camera and to take the intersection between the ray and the object as the object position. It's implemented in vtk https://blog.kitware.com/ray-casting-ray-tracing-with-vtk/.
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 have a function to create a vector with the mouse coordinates in 3d space.
Here is my code:
void myMouse(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; // Subtract The Current Mouse Y Coordinate
glReadPixels( x, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );//Reads the depth buffer
gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
vector.x=posX;
vector.y=posY;
vector.z=posZ;
}
The vector is then used as an attractor for some points that I'draw. The code works very well if i put the z position to 0, but then the points does not follow the cursor of the mouse when I rotate the scene. But if I set vector.z as the code above, the particles begin to move toward me and then they disappear (i.e. they go behind the viewpoint).
I'd like my points to follow the mouse only on the x/y axis when the z axis is pointed toward me, on the x/z axis when the y axis is pointed toward me and on the y/z axis when the x axis is pointed toward me.
Alternatively, how can I set the z position at a fixed distance from my point of view?
Most likely your problem lies in those lines:
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
OpenGL is a state machine and what you query there could be anything. While drawing you normally manipulate the contents of those matrices in various ways, so what you query there may likely not be the matrices used initially for drawing.
To solve your problem you should make copies of the projection and modelview and viewport settings at the time of drawing right after you'Ve setup your initial view. You use then these copies instead of queried values, which may be very different from what you actually need.
I would like to display a 2D image at a 2D point calculated from a 3D point using gluProject().
So I have my 3D point, I use gluProject to get its 2D coordinates, then I display my image at this point.
It works well but I have a problem with Z coordinate which makes my image appear two times on the screen : where it should really appear and at "the opposite".
Let's take an example : the camera is at (0,0,0) and I look at (0,0,-1) so in direction of negative Z.
I use 3D point (0,0,-1) for my object, gluProject gives me as 2D point the center of my window which is the good point.
So when I look in direction of (0,0,-1) my 2D image appears, when I rotate, it moves well until the point (0,0,-1) is not visible, which makes the 2D image go out of screen so not displayed.
But when I look at (0,0,1), it also appears. Consequently, I get the same result (for the display of my 2D image) if I use 3D point (0,0,-1) and (0,0,1) for example. I assume there is something to do with the Z coordinate that gluProject returns but I don't know what.
Here is my code : my zNear=0.1 and zFar=1000
GLint viewport[4];
GLdouble modelview[16];
GLdouble viewVector[3];
GLdouble projection[16];
GLdouble winX, winY, winZ;//2D point
GLdouble posX, posY, posZ;//3D point
posX=0.0;
posY=0.0;
posZ=-1.0;//the display is the same if posZ=1 which should not be the case
//get the matrices
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
viewVector[0]=modelview[8];
viewVector[1]=modelview[9];
viewVector[2]=modelview[10];
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
int res=gluProject(posX,posY,posZ,modelview,projection,viewport,&winX,&winY,&winZ);
if(viewVector[0]*posX+viewVector[1]*posY+viewVector[2]*posZ<0){
displayMyImageAt(winX,windowHeight-winY);
}
So, what do I need to do to get the good display of my 2D image, that's to say to take Z into account?
gluProject works correctly, you projection matrix projects points on the screen plane, you should check whether point is behind, you can achieve this by calculating dot product of your view vector and vector to point, if it is less then 0 then point is behind.
what is your DisplayImageAt function?
is it similar to this display function given in my code?
I am trying to as well get 2D coordinate of a point selected 3d coordinates.
here is my display pretty much all..
`void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glMultMatrixd(_matrix);
glColor3f(0.5,0.5,0.5);
glPushMatrix(); //draw terrain
glColor3f(0.7,0.7,0.7);
glBegin(GL_QUADS);
glVertex3f(-3,-0.85,3);
glVertex3f(3,-0.85,3);
glVertex3f(3,-0.85,-3);
glVertex3f(-3,-0.85,-3);
glEnd();
glPopMatrix();
glPushMatrix();
myDefMesh.glDraw(meshModel);
glPopMatrix();
glutSwapBuffers();
}'
I'm writing a MFC c++ application that uses OpenGL. The program allows for drawing and manipulating of objects in 3D. Right now I want to find the coordinates, in the same coordinate space that my objects are drawn in, anywhere I click my mouse on the screen.
So far I've been using a combination of glReadPixels and gluUnProject and it has been working but only when I click my mouse somewhere where an object has already been drawn. If I click anywhere outside my object the coordinates obtained are completely off.
So I was wondering how to change my code so that I can find the coordinates in the coordinate space my objects are in anywhere on the screen. Here's the code I've been using:
GLint viewport[4];
GLdouble ox, oy, oz;//the coordinates I need
GLfloat winZ = 0.0;
::glGetIntegerv(GL_VIEWPORT, viewport);
::glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);
::glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix);
GLfloat winX = (float)point.x;//point.x and point.y are the mouse coordinates
GLfloat winY = (float)viewport[3] - (float)point.y;
::glReadPixels( winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject((GLdouble)winX, (GLdouble)winY, (GLdouble)winZ, modelviewMatrix, projectionMatrix, viewport, &ox, &oy, &oz);
gluUnProject takes window space coordinates and un-projects them with the inverse of the world-view-projection-viewport transformations. It has no clue if the coordinates correspond to an existing object or not.
When you clear the depth buffer, it is initialized everywhere with a value that is read as 1.0 with glReadPixels.
When visible fragments of an object are drawn they will pass the depth test and will override the depth value with a smaller value for every pixel intersecting these fragments.
This means that every time you read a pixel in the depth buffer with a value of 1.0, this means that nothing visible has been drawn in that pixel and this is where the result you obtain is completely off.
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.