Jump to content

  • Log In with Google      Sign In   
  • Create Account

Why does my Unproject() method insist on Unprojecting towards 0,0,0?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
4 replies to this topic

#1 sebjf   Members   -  Reputation: 116

Like
0Likes
Like

Posted 29 January 2012 - 06:32 AM

Hello,

I am trying to implement an Unproject() method:


public static Ray Unproject(Vector2 Cursor, Vector2 Window, Matrix4 View, Matrix4 Projection) //Cursor is relative to window, Window is window dimensions
{
float clipspacex = ((Cursor.x / Window.x) * 2) - 1;
float clipspacey = ((Cursor.y / Window.y) * 2) - 1;

Vector2 ClipSpaceCursor = new Vector2(clipspacex, -clipspacey);

Vector3 v1 = Unproject(ClipSpaceCursor, 0, View, Projection);
Vector3 v2 = Unproject(ClipSpaceCursor, 1, View, Projection);

return new Ray(v1, v2 - v1);
}

public static Vector3 Unproject(Vector2 ClipSpaceCursor, float z, Matrix4 View, Matrix4 Projection)
{
return (View.Inverse() * (Projection.Inverse() * new Vector4(ClipSpaceCursor, z, 1))).xyz; //Math library uses column vectors
}

However it appears to always project towards (0,0,0), as opposed to into the screen.

Untitled.png

The point v1 always sits behind the cursor regardless of the position and orientation of the camera, so that at least is being resolved correctly, but I cannot see why v2 is always resolved closer to (0,0,0); its like (0,0,0) is the vanishing point and its trying to project out of it.

Can anyone see what is wrong with it?

Sponsor:

#2 clb   Members   -  Reputation: 1790

Like
0Likes
Like

Posted 29 January 2012 - 06:55 AM

You will need to divide by w after multiplying Projection.Inverse() * new Vector4(ClipSpaceCursor, z, 1), unless w==1 after that multiplication. (I can't remember if Projection.Inverse() will produce w=1 with those values, so try checking that.)
Me+PC=clb.demon.fi | C++ Math and Geometry library: MathGeoLib, test it live! | C++ Game Networking: kNet | 2D Bin Packing: RectangleBinPack | Use gcc/clang/emcc from VS: vs-tool | Resume+Portfolio | gfxapi, test it live!

#3 sebjf   Members   -  Reputation: 116

Like
0Likes
Like

Posted 29 January 2012 - 08:03 AM

Thank you very much clb! I added the divide by w operation and the (un)projection is now working perfectly. I can say surely it would have been a Long time before I figured that one out.

As I understand it, w is used to control projection, by 'standing in' for z - when the final position is calculated z is moved into w by the projection matrix, and the divide is performed. So is it that, when the inverse of this matrix is taken, that operation is no longer performed and so needs to be done manually/explicitly as it is in my method now?

#4 clb   Members   -  Reputation: 1790

Like
0Likes
Like

Posted 29 January 2012 - 08:32 AM

When you multiply a vector by a projection matrix (not only its inverse), you will also have to divide the result by w. This projects the homogeneous coordinates back to the euclidean 3D space, and the step needs to be always done when using 4x4 matrices for transforms (such as the perspective projection) in homogeneous coordinate spaces. This property can explained by mathematical theory of homogeneous coordinates, but if you don't feel like waddling through the theory, one can look at these operations also in more of a "programmer's" terms:

in a 3x3 matrix we can encode operations acting on a vector (x,y,z) that
  • multiply any element by a constant
  • divide any element by a constant
  • add (subtract) one of the inputs x/y/z to any element (+multiples/fractions of x/y/z)
That is quite a limited set of arithmetic operations we can do, so we invent the (x,y,z,1) trick and convert to using 3x4 matrices, so that we can also
  • add (subtract) a constant to any element
so now we can multiply/divide by a constant, add/subtract by a constant, and add/subtract by any of the inputs. But we cannot yet multiply/divide by any of the inputs (which is what perspective projection math needs, to divide by depth). So, to be able to represent that operation with a matrix, (now needs a 4x4 one) we invent the divide by w trick that enables us to also
  • multiply (divide) all elements by any of the inputs.
After the matrix multiplication, the divider is encoded into the w component of the vector, and we take it as a "convention" to divide the vector by w afterwards, to perform that division.

So, in short, one can think of this "divide by w" step as a mechanism which enables us to use matrices to divide(multiply) vectors by one of the elements of that vector itself, a "trick" just like setting w=1 for points to be able to use matrices to add constants to a vector.
Me+PC=clb.demon.fi | C++ Math and Geometry library: MathGeoLib, test it live! | C++ Game Networking: kNet | 2D Bin Packing: RectangleBinPack | Use gcc/clang/emcc from VS: vs-tool | Resume+Portfolio | gfxapi, test it live!

#5 sebjf   Members   -  Reputation: 116

Like
0Likes
Like

Posted 29 January 2012 - 09:01 AM

I see, I misunderstood it as a feature of the makeup of a projection matrix, as opposed to an agreed implementation.
Thank you very much clb.
The way you explained it, as the capabilities of the are 'built up' with the dimensions of the matrices, is very clear. I understand this convention now, as opposed to simply trying to remember it!




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS