Ray from screen coord (in worldspace) to farplane

Started by
24 comments, last by _WeirdCat_ 8 years, 6 months ago

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....
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;
My current game project Platform RPG

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

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.

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)

My current game project Platform RPG

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

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.

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

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

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.

This topic is closed to new replies.

Advertisement