Jump to content
  • Advertisement
Sign in to follow this  
fazekaim

how to compute a 3d ray from mouse cursor's 2d points?

This topic is 4887 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I would like to click on a terrain (heightmap), so i qould like to compute a ray from the camera to the point clicked with the mouse. How can I do this? ( it's a little bit amateur problem, i know) Thanks.

Share this post


Link to post
Share on other sites
Advertisement
This code is pasted from my own project.
Assuming you have vector and matrix classes, the code would be:

// get the current frustum:
afrustum f = camera->Frustum();

// transform mouse coordinates to coordinates on the near plane
float fr_x = ((float) mouse_x / (float) screen_width) * (f.right - f.left) + f.left;
float fr_y = ((float) mouse_y / (float) screen_height) * (f.top - f.bottom) + f.bottom;

// get the origin of the ray on the near plane (NB: nr = near, near is reserved):
selection_ray->origin = avector(fr_x, fr_y, -f.nr);
// get the direction of the ray (coincidently the same value):
selection_ray->direction = avector(fr_x, fr_y, -f.nr);
// transform the ray to world space:
selection_ray->transform(camera->ViewMatrix(), camera->InverseViewMatrix());







You asked for the vector from eye to projection plane, in which case

selection_ray->origin = avector(0, 0, 0);

would do. But for selection, you don't want to be able to select the (invisible) triangles between viewpoint and near. Hence, the origin on the near plane...

For your purposes, you can assume the frustum class to just containt left, right , top, bottom, near (nr) and far values of the projection. I do a lot more with it but that's not relevant here.

The ray class is just two vectors. Transforming a ray requires a matrix and it's inverse.

class imex aray
{
public:
avector origin, direction;

/**
* create aray from another ray
*
* @param other other ray
**/

inline aray(aray &other)
{
origin = other.origin;
direction = other.direction;
};

/**
* create aray from two vectors
*
* @param orig point of origin
* @param dir direction vector
**/

inline aray(avector orig = avector(), avector dir = avector())
{
origin = orig;
direction = dir;
direction.normalize();
};

/**
* create aray from 6 direct coordinates
*
* @param ax x-coordinate of origin
* @param ay y-coordinate of origin
* @param az z-coordinate of origin
* @param bx x-coordinate of direction
* @param by y-coordinate of direction
* @param bz z-coordinate of direction
**/

aray(float ax,float ay,float az,float bx,float by,float bz)
{
origin = avector(ax,ay,az);
direction = avector(bx,by,bz);
direction.normalize();
};

/**
* apply a matrix to this ray
*
* @param mat the matrix to apply
* @param inv the inverse of that matrix
**/

inline void transform(amatrix mat, amatrix inv)
{
origin = mat * origin;
direction = inv.transposed() * direction;
direction.normalize();
};

/**
* apply a matrix to this plane
* uses expensive inverse calculation
*
* @param mat the matrix to apply
**/

inline void transform(amatrix mat)
{
transform(mat, mat.inverse());
};
};







Tom

Share this post


Link to post
Share on other sites
what about using gluUnProject with different depth?
Why have you created own ray computation? using gluUnProject isn't exact enough?

Share this post


Link to post
Share on other sites
dimebolt, your code computes a ray from the origin to a point that lies on the near plane, not to a point that lies in the terrain.

You can use glReadPixels() to read the depth value of the pixel, and use that value with gluUnProject to get the 3D coordinates of the point.

Share this post


Link to post
Share on other sites
Here you go, using unproject...

only condition why I let you have it, (besides being nice. :) is that you try learning from it... peace!



vertex3f GetMouseClick()
{
vertex3f Mpos;
//Grab Matrices and Viewport Values

GLdouble model[16];
glGetDoublev ( GL_MODELVIEW_MATRIX, model);

GLdouble projection[16];
glGetDoublev ( GL_PROJECTION_MATRIX, projection);

GLint viewport [4];
glGetIntegerv (GL_VIEWPORT, viewport);

//Read the depth buffer
GLfloat depth;
glReadPixels(MouseX, ScreenHeight-MouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);

//Unproject
gluUnProject (MouseX, ScreenHeight-MouseY, depth, model, projection, viewport,
&Mpos.x, &Mpos.y, &Mpos.z);
return Mpos;
};





vertex3f is a simple struct with x,y,z doubles...



simply call GetMouseClick() to find out where the cursor is located at 3d depth... MouseX and MouseY are obvious, right? :)
cheers!

Share this post


Link to post
Share on other sites
Quote:
Original post by mikeman
dimebolt, your code computes a ray from the origin to a point that lies on the near plane, not to a point that lies in the terrain.

You can use glReadPixels() to read the depth value of the pixel, and use that value with gluUnProject to get the 3D coordinates of the point.


Ahh, sorry I misread the question. If still interested, I can post the code for calculating triangle/ray, bbox/ray and bsphere/ray intersection.

I don't use gluUnproject because my scene graph is not tied to OpenGL. Besides, as you can see in the solution by Rasmadrak, it only gives the position in world space. I need to know exactly which object and which triangle was hit, to create the proper callbacks for the scene graph objects. However, I guess Resmadraks solution is good enough for your purposes...

Tom

Share this post


Link to post
Share on other sites
This is the way I handle the exact same problem:


gluUnProject((GLdouble)mouse_x,(GLdouble)viewport[3]-mouse_y,0,modelview,project,viewport,&nx,&ny,&nz); //mouse world position at near plane
gluUnProject((GLdouble)mouse_x,(GLdouble)viewport[3]-mouse_y,1,modelview,project,viewport,&fx,&fy,&fz); //mouse world position at far plane





This generates a ray from near to farplane at the mouse position.
After that I do a simple Ray/Triangle collision test with my terrain, which tells me what triangle the mouse is floating over, and how far away the collision point is.

I also took notize, that this is way more precise than glReadPixels

Share this post


Link to post
Share on other sites
Quote:
Original post by Hydrael
I also took notize, that this is way more precise than glReadPixels


It's also much faster. glReadPixels tends to severely limit the framerate. This is because reading from video-memory tends to be very slow.

EDIT: obviously this is not true for all cases. If you have a scene with an extremely high number of objects, or an extremely high poly-count, the triangle/ray intersection method will eventually be slower. But at least it can be optimized by programming. For glReadPixels performance you depend on your hardware.

Tom

Share this post


Link to post
Share on other sites
Thanks.

gluUnProject is working fine. glReadPixels is a little bit slower to me.

Only one question: the ray is the vector computed by: fx-nx, fy-ny, fz-nz
or camera.dir + (fx-nx, fy-ny, fz-nz) ?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!