# Ray from screen coord (in worldspace) to farplane

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

## 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)

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

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 on other sites
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 on other sites

I am not sure how i can apply that to a custom ray.

i just dont see how can i get ray start and ray end points from such thing

##### 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 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 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 on other sites

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 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 Vector3(Single, Single, Single)

Initializes a new instance of the Vector3 class.
Initializes a new instance of the Vector3 class.
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 on other sites

Use new Vector3( 0, 0, 0 ) if you want to a zero vector

##### 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

anyway even direction is wrong.

Edited by WiredCat

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 10
• 11
• 13
• 9
• 11
• ### Forum Statistics

• Total Topics
634090
• Total Posts
3015434
×