• Advertisement
Sign in to follow this  

Ray from screen coord (in worldspace) to farplane

This topic is 931 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

So basically i have a near plane in distance 2.0 and far plane in distance 28.0 * 1000.0

 

Now when i click a screen i want to make a ray from mouse position to far plane  (perspective projection)

 

i just tested 4 ways of doing that, none worked  i am not sure wheres bug maybei shouldn't apply aspect ratio to screen height when calculating the size of near and fra plane?

 

 

so from start: (the idea top view)

 

znf.png

 

 

HERES ANOTHER IMAGE SEE ctg(B) -<<< this is what i calc

 

 

397.gif

 

 

 

 

to calc screen_w i use contanget function (with the fov angle divided by 2)

 

 
float a = fov / 2.0; 
float cotangent = 1.0 / tan( a * imopi ); (since a is in degrees i must convert it by multiplying (pi/180.0) and ctg(x) is 1.0 / tg(x)
 
float ax = z_near / cotangent;
 
float screen_w = 2.0*ax; //multiple by two to get plane width
 
float yratio = 1.0 / aspect; //then use aspect ratio to find the plane height
 
float screen_h = screen_w * yratio;
 
 
//this is from the input (i calculate the percentage of the coord on screen)
float scr_coord_x = float(x) / float(screen_width);
float scr_coord_y = float(y) / float(screen_height);
 
 
//now to calculate the 
FPP_CAM is a class that stores position of camera, and direction vectors such front, right and up
dir is froward direction
dirX is right direction
dirY is up direction;
 
vec3 start_pos = (FPP_CAM->pos + dir * z_near) + (-dirX * (screen_w / 2.0)) + (-dirY * (screen_h/2.0)); //basically here i am going to center of near plane and then go to the bottom left corner
 
ray.start = start_pos + (dirX * (screen_w * scr_coord_x)) + (dirY * (screen_h * scr_coord_y)); //then after calcing the (near plane in this case dimensions) i scale direction (right and up) by coordinate on the screen multiplied by corresponding near plane dimension.
 
 
do the analog for far plane
 
 
 
 
seems that only when i hit center of the screen i see proper result....
Edited by WiredCat

Share this post


Link to post
Share on other sites
Advertisement
The best way to do this is to use your view and projection matrix. That way it doesn't matter if you are using a orthogonal or perspective projection.

If you transform you vertices like this.

clippingSpace = (viewMat * projectionMat) * worldPos
normalizedSpace = clippingSpace / clippingSpace.w
Then you transform points to the world using this equation
worldSpace = inverse(viewMat * projectionMat) * normalizedSpace
worldSpace /= worldSpace.w
However, you can transpose planes from normalizedSpace to worldSpace much easier
// To transform a plane, you multiply it as a 4d vector by the inverse transpose of the matrix
worldPlane = transpose(inverse(inverse(viewMat * projectionMat))) * Vector4(plane.normal.x, plane.normal.y, plane.normal.z, plane.distance)


// however, since you have double inverse you can simplify it to this
worldPlane = transpose(viewMat * projectionMat) * Vector4(plane.normal.x, plane.normal.y, plane.normal.z, plane.distance)
So to generate a ray you can transform two planes, one vertical one hoizontal forming a crosshair at the point on your screen. You then transform those planes to world space using the above code then find the cross product of the normals from the two world space planes to find the direction of the ray. The origin of the ray is the camera position.

To create the two planes you simply do the following

verticalPlane.normal = Vector3(1, 0, 0)
verticalPlane.distance = -(screenPos.x * 2) / screenWidth + 1;

horizontalPlane.normal = Vector3(0, 1, 0)
horizontalPlane.distance = (screenPos.y * 2) / screenHeight + 1;

Share this post


Link to post
Share on other sites

I always used gluProject() with WinZ = 0 and WinZ =1 for this. This gives you two points on the near and far plane and from there you can build your ray easily. Googling for this resulted in these two links. 

 

https://www.opengl.org/sdk/docs/man2/xhtml/gluUnProject.xml

http://myweb.lmu.edu/dondi/share/cg/unproject-explained.pdf

 

Sadly this might not work anymore with modern OpenGL so I need to implement this myself soon I guess.

Share this post


Link to post
Share on other sites
A ray is simply a start point and a direction, you want to to transform a line segment then you can simply transform two endpoints

using this code
worldSpace = inverse(viewMat * projectionMat) * normalizedSpace
worldSpace /= worldSpace.w
If you are going to be transforming a lot of points then I would only calculate the inverse once, because it is expensive, and reuse it for all the points.

The two points you want to transform in normalizeSpace you can make using

x = (screenPos.x * 2) / screenWidth + 1;
y = -(screenPos.y * 2) / screenHeight + 1;

normalizedA = Vector3(x, y, -1) // replace the -1 with 0 for directx
normalizedB = Vector3(x, y, 1)

Share this post


Link to post
Share on other sites

Dirk Gregorius   Members   -  Reputation: 1708

 

Posted Today, 07:21 PM

I always used gluProject() with WinZ = 0 and WinZ =1 for this. This gives you two points on the near and far plane and from there you can build your ray easily. Googling for this resulted in these two links. 

 

 

I tried to use code from:

https://www.opengl.org/wiki/GluProject_and_gluUnProject_code

 

but it returns 0,0,0 point (sometimes does not but mostly 0,0,0)

when calling

glhUnProjectf(x, SCREEN_HEIGHT - y, 1.0, modelview.m, projection.m, vp, &ray.end);

 

 

 

please note that i use DirectX matrix order not opengl one( even when i am using opengl) because the standard math is used for that order and opengl uses transposed matrices no wonder why.

 

 

 

Now 

 

that seems that for dx its

worldSpace = inverse(viewMat * projectionMat) * normalizedSpace
 

 

and for opengl it would be

worldSpace = inverse(projectionMat * viewMat) * normalizedSpace

 

 

 

i obviously use that first one

 

so i think 

normalizedA = Vector3(x, y, -1) // replace the -1 with 0 for directx ill use 0 there anyway

 

but still i miss the thing about  converting

 

normalizedA and normalizedB to normalizedSpace 

 

 

sorry to be sun an asshole but HappyCodin' i really dont get that way of thinking :X

Edited by WiredCat

Share this post


Link to post
Share on other sites

How about this:

http://richardssoftware.net/Home/Post/23

https://www.mvps.org/directx/articles/improved_ray_picking.htm

 

This is a problem that must have been solved may times. If you don't want to derive this as an exercise you should be able to find functional code for this in minutes using a search engine of your choice. It seems that there is even an SDK sample.

Edited by Dirk Gregorius

Share this post


Link to post
Share on other sites

im trying to convert that c# code to c++ but theres a problem i cant get rid of

 

var ray = new Ray(new Vector3(), new Vector3(vx, vy, 1.0f));
var v = View;
var invView = Matrix.Invert(v);


var toWorld = invView;

ray = new Ray(Vector3.TransformCoordinate(ray.Position, toWorld),

 

 

Vector3.TransformCoordinate(ray.Position, toWorld) ->> ray position isnt even set to anything so i consider its 0,0,0 point so theres no use of doing that yet guy does that so i am confused  since constructor like var ray = new Ray(new Vector3(), new Vector3(vx, vy, 1.0f)); (i mean new Vector3() isnt even defined in slimDX specs)

 

theres only

 

Icon Member Description pubmethod.gifVector3(Single, Single, Single)

Initializes a new instance of the Vector3 class.

pubmethod.gifVector3(Vector2, Single)

Initializes a new instance of the Vector3 class.

pubmethod.gifVector3(Single)

Initializes a new instance of the Vector3 class.
 

 

 

that doesnt even make any sense...

 

but more over i am more confised why my solution doesn't want to work

Edited by WiredCat

Share this post


Link to post
Share on other sites

i think you got me wrong:

    float vx = (2.0f * float(x) / float(sw) - 1.0f) / ACTUAL_PROJECTION.m[0];
	    float vy = (-2.0f * float(y) / float(sh) + 1.0f) / ACTUAL_PROJECTION.m[5];

	    vec3 direction = vec3(vx, vy, 1.0f); //sorry but such direction NOPE
	    mat4 invView = ACTUAL_VIEW;
	    invView.Inverse();


	    mat4 toWorld = invView;
vec3 rstart = invView * vec3(0.0, 0.0, 0.0);
vec3 rdir = invView * direction;
//ray = new Ray(Vector3.TransformCoordinate(ray.Position, toWorld), Vector3.TransformNormal(ray.Direction, toWorld));

	rdir = Normalize(rdir);//    ray.Direction.Normalize();

	ray_vb[0].v = rstart; // NOPE
	ray_vb[1].v = rstart + direction*5000.0;

vec3 rstart = invView * vec3(0.0, 0.0, 0.0); <-- this is hopeless  i will never get anything from that so //Vector3.TransformCoordinate(ray.Position, toWorld) looks like the most useless code in the world smile.png

 

 

 

anyway even direction is wrong.

Edited by WiredCat

Share this post


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

  • Advertisement