reverse perspective projection - math

I'm using
worldview_inverse * (projection_inverse * vector)
to transform screen space coordinates into world space coordinates.
I assumed that
(x,y,1,1)
would transform to a point on the far plane, while
(x,y,-1,1)
transforms to a point on the near plane, and connecting the line I can query all objects in the view frustum that intersect the line.
After the transformation I divide the resulting points by their respective .w component.
This works for the far-plane, but the point on the near plane somehow gets transformed to the world space origin.
I think this has to do with the w components of 1 I'm feeding into the inverse projection, because usually it is 1 before projection, not after, and I'm doing the reverse projection. What am I doing wrong?

I know this is only a workaround, but you can deduce the near plane point by only using the far point and the viewing position.
near_point = view_position
+ (far_point - view_position) * (near_distance / far_distance)
As for you real problem. First, don't forget to divide by W! Also, depending on your projection matrix, have you tried (x,y,0,1) as opposed to z=-1.
near_point = worldview_inverse * (projection_inverse * vector)
near_point /= near_point.W

Related

Align IMU orientations and then get relative rotations

I am using two IMUs of the same type (BHI160, i.e. orientation is relative to the north and on alignment with north, the IMU's local y-axis points into the north direction) on two objects, let's say pens, with the added difficulty that if I place the two objects in parallel, both IMUs' z-axes point upwards, but one IMU is rotated 180° around the z-axis relative to the other.
Now, if I understand the math here correctly, the quaternion data I receive from an IMU is the half-angle-rotation relative to the north direction, so that q * north_dir * q_inv = IMU_y_axis (with north_dir and IMU_y_axis being 3D vectors in global space, or pure quaternions for the sake of this computation).
Due to the rotation of the IMUs, I would assume that when both pens are pointing in the same direction, I should be able to compute the second pen's orientation as q_2 = q_rot_z * q_1, where q_rot_z equals a 90° rotation around the z-axis -- following the intuition that if I point both pens towards the north, I would obtain the global direction of pen 2's y-axis (i.e. pen 1's y-axis rotated around the z-axis by 180°) by computing q_rot_z * north_dir * q_rot_z_inv
Is it thus correct that if I want to know the relative rotation of the pen tips (say, the rotation I need to go from the first pen's tip to the tip of the second one), I need to compute q_r = q_2 * q_rot_z_inv * q_1_inv in order to get from tip 1 to tip 2 by computing q_r * q_1? Or does the "prior" rotation around the z-axis not matter in this case and I only need to compute q_r = q_2 * q_1_inv as usual?
Edit:
this is basically an extension of this question, but I would like to know if the same answer also applies in my case or whether the known relative IMU rotation would in my case need to be included as well
Let's go through this step by step. You have a global coordinate system G, which is aligned to the north direction. It does not really matter how it is aligned or if it is aligned at all.
Then we have to IMUs with their respective coordinate systems I1 and I2. The coordinate systems are given as the rotation from the global system to the local systems. In the following, we will use the notation R[G->I1] for that. This represents a rotation from G to I1. If you transform any vector in G with this rotation, you will get the same vector in I1 expressed in the coordinate system G. Let's denote the transformation of a vector v with transform T by T ° v. The following figure illustrates this:
In this figure, I have added a translation to the transform (which quaternions can of course not represent). This is just meant to make the point clearer. So, we have a vector v. The same vector can lie in either coordinate system G or I. And the transformed vector R[G->I] ° v represents the v of I in the coordinate system of G. Please make sure that this is actually the rotation that you get from the IMUs. It is also possible that you get the inverse transform (this would be the system transform view, whereas we use the model transform view). This changes little in the following derivations. Therefore, I will stick to this first assumption. If you need the inverse, just adjust the formulas accordingly.
As you already know, the operation R ° v can be done by turning v into a pure quaternion, calculating R * v * conjugate(R), and turning it into a vector again (or work with pure quaternions throughout the process).
Now the pens come into play. The pen has an intrinsic coordinate system, which you can define arbitrarily. From your descriptions, it seems as if you want to define it such that the pen's local y-axis points towards the tip. So, we have an additional coordinate system per pen with the according rotation R[I1->P1] and R[I2->P2]. We can concatenate the rotations to find the global orientations (* is quaternion multiplication):
R[G->P1] = R[G->I1] * R[I1->P1]
R[G->P2] = R[G->I2] * R[I2->P2]
In the way that you defined the pen's local coordinate system, we know that R[I1->P1] is the identity (the local coordinate system is aligned with the IMU) and that R[I2->P2] is a rotation of 180° about the z-axis. So, this simplifies to:
R[G->P1] = R[G->I1]
R[G->P2] = R[G->I2] * RotateZ(180°)
Note that the z-rotation is performed in the local coordinate system of the IMU (it is multiplied at the right side). I don't know why you think that it should be 90°. It is really a rotation of 180°.
If you want to find the relative rotation between the tips, you first need to define in which coordinate system the rotation should be expressed. Let's say we want to express the rotation in the coordinate system of P1. Then, what you want to find is a rotation R[P1->P2], such that
R[G->P1] * R[P1->P2] = R[G->P2]
This solves to
R[P1->P2] = conjugate(R[G->P1]) * R[G->P2]
If you plug the above definitions in, you would get:
R[P1->P2] = conjugate(R[G->I1]) * R[G->I2] * RotateZ(180°)
And that's it.
It is pretty likely that you want something slightly different. That's why I explained it in such detail, so you will be able to modify the calculations accordingly.

Calculating modelview matrix for 2D camera using Eigen

I'm trying to calculate modelview matrix of my 2D camera but I can't get the formula right. I use the Affine3f transform class so the matrix is compatible with OpenGL. This is closest that I did get by trial and error. This code rotates and scales the camera ok, but if I apply translation and rotation at same time the camera movement gets messed up: camera moves in rotated fashion, which is not what I want. (And this probaly due to fact I first apply the rotation matrix and then translation)
Eigen::Affine3f modelview;
modelview.setIdentity();
modelview.translate(Eigen::Vector3f(camera_offset_x, camera_offset_y, 0.0f));
modelview.scale(Eigen::Vector3f(camera_zoom_x, camera_zoom_y, 0.0f));
modelview.rotate(Eigen::AngleAxisf(camera_angle, Eigen::Vector3f::UnitZ()));
modelview.translate(Eigen::Vector3f(camera_x, camera_y, 0.0f));
[loadmatrix_to_gl]
What I want is that camera would rotate and scale around offset position in screenspace {(0,0) is middle of the screen in this case} and then be positioned along the global xy-axes in worldspace {(0,0) is also initialy at middle of the screen} to the final position. How would I do this?
Note that I have set up also an orthographic projection matrix, which may affect this problem.
If you want a 2D image, rendered in the XY plane with OpenGL, to (1) rotate counter-clockwise by a around point P, (2) scale by S, and then (3) translate so that pixels at C (in the newly scaled and rotated image) are at the origin, you would use this transformation:
translate by -P (this moves the pixels at P to the origin)
rotate by a
translate by P (this moves the origin back to where it was)
scale by S (if you did this earlier, your rotation would be messed up)
translate by -C
If the 2D image we being rendered at the origin, you'd also need to end by translate by some value along the negative z axis to be able to see it.
Normally, you'd just do this with OpenGL basics (glTranslatef, glScalef, glRotatef, etc.). And you would do them in the reverse order that I've listed them. Since you want to use glLoadMatrix, you'd do things in the order I described with Eigen. It's important to remember that OpenGL is expecting a Column Major matrix (but that seems to be the default for Eigen; so that's probably not a problem).
JCooper did great explaining the steps to construct the initial matrix.
However I eventually solved the problem bit differently. There was few additional things and steps that were not obvious for me at the time. See JCooper answer's comments. First is to realize all matrix operations are relative.
Thus if you want to position or move the camera with absolute xy-axes, you must first decompose the matrix to extract its absolute position with unchanged axes. Then you translate the matrix by the difference of the old and new position.
Here is way to do this with Eigen:
First compute Affine2f matrix cmat scalar determinant D. With Eigen this is done with D = cmat.linear().determinant();. Next compute 'reverse' matrix matrev of the current rotation+scale matrix R using the D. matrev = (RS.array() / (1.0f / determ)).matrix()); where RS is cmat.matrix().topLeftCorner(2,2)
The absolute camera position P is then given by P = invmat * -C where C is cmat.matrix().col(2).head<2>()
Now we can reposition the camera anywhere along the absolute axes and keeping the rotation+scaling same: V = RS * (T - P) where RS is same as before, T is the new position vec and P is the decomposed position vec.
The cmat then simply translated by V to move the camera: cmat.pretranslate(V)

Projection matrix point to sphere surface

I need to project a 3D object onto a sphere's surface (uhm.. like casting a shadow).
AFAIR this should be possible with a projection matrix.
If the "shadow receiver" was a plane, then my projection matrix would be a 3D to 2D-plane projection, but my receiver in this case is a 3D spherical surface.
So given sphere1(centerpoint,radius),sphere2(othercenter,otherradius) and an eyepoint how can I compute a matrix that projects all points from sphere2 onto sphere1 (like casting a shadow).
Do you mean that given a vertex v you want the following projection:
v'= centerpoint + (v - centerpoint) * (radius / |v - centerpoint|)
This is not possible with a projection matrix. You could easily do it in a shader though.
Matrixes are commonly used to represent linear operations, like projection onto a plane.
In your case, the resulting vertices aren't deduced from input using a linear function, so this projection is not possible using a matrix.
If the sphere1 is sphere((0,0,0),1), that is, the sphere of radius 1 centered at the origin, then you're in effect asking for a way to convert any location (x,y,z) in 3D to a corresponding location (x', y', z') on the unit sphere. This is equivalent to vector renormalization: (x',y',z') = (x,y,z)/sqrt(x^2+y^2+z^2).
If sphere1 is not the unit sphere, but is say sphere((a,b,c),R) you can do mostly the same thing:
(x',y',z') = R*(x-a,y-b,z-c) / sqrt((x-a)^2+(y-b)^2+(z-c)^2) + (a,b,c). This is equivalent to changing coordinates so the first sphere is the unit sphere, solving the problem, then changing coordinates back.
As people have pointed out, these functions are nonlinear, so the projection cannot be called a "matrix." But if you prefer for some reason to start with a projection matrix, you could project first from 3D to a plane, then from a plane to the sphere. I'm not sure if that would be any better though.
Finally, let me point out that linear maps don't produce division-by-zero errors, but if you look closely at the formulas above, you'll see that this map can. Geometrically, that's because it's hard to project the center point of a sphere to its boundary.

mapping from normalized device coordinates to view space

I'd like to map from normalized device coordinates back to viewspace.
The other way arround works like this:
viewspace -> clip space : multiply the homogeneous coordinates by the projection matrix
clip space -> normalized device coordinates: divide the (x,y,z,w) by w
now in normalized device coordinates all coordinates which were within the view frustum fall into the cube x,y,z € [-1,1] and w=1
Now i'd like to transform some points on the boundary of that cube back into view coordinates. The projection matrix is nonsingular, so I can use the inverse to get from clipsace to viewspace. but i don't know how to get from normalized device space to clipspace, since i don't know how to calculate the 'w' i need to multiply the other coordinates with.
can someone help me with that? thanks!
Unless you actually want to recover your clip space values for some reason you don't need to calculate the W. Multiply your NDC point by the inverse of the projection matrix and then divide by W to get back to view space.
The flow graph on the top, and the formulas described on the following page, might help you : http://www.songho.ca/opengl/gl_transform.html

Show lat/lon points on screen, in 3d

It's been a while since my math in university, and now I've come to need it like I never thought i would.
So, this is what I want to achieve:
Having a set of 3D points (geographical points, latitude and longitude, altitude doesn't matter), I want to display them on a screen, considering the direction I want to take into account.
This is going to be used along with a camera and a compass , so when I point the camera to the North, I want to display on my computer the points that the camera should "see". It's a kind of Augmented Reality.
Basically what (i think) i need is a way of transforming the 3D points viewed from above (like viewing the points on google maps) into a set of 3d Points viewed from a side.
The conversion of Latitude and longitude to 3-D cartesian (x,y,z) coordinates can be accomplished with the following (Java) code snippet. Hopefully it's easily converted to your language of choice. lat and lng are initially the latitude and longitude in degrees:
lat*=Math.PI/180.0;
lng*=Math.PI/180.0;
z=Math.sin(-lat);
x=Math.cos(lat)*Math.sin(-lng);
y=Math.cos(lat)*Math.cos(-lng);
The vector (x,y,z) will always lie on a sphere of radius 1 (i.e. the Earth's radius has been scaled to 1).
From there, a 3D perspective projection is required to convert the (x,y,z) into (X,Y) screen coordinates, given a camera position and angle. See, for example, http://en.wikipedia.org/wiki/3D_projection
It really depends on the degree of precision you require. If you're working on a high-precision, close-in view of points anywhere on the globe you will need to take the ellipsoidal shape of the earth into account. This is usually done using an algorithm similar to the one descibed here, on page 38 under 'Conversion between Geographical and Cartesian Coordinates':
http://www.icsm.gov.au/gda/gdatm/gdav2.3.pdf
If you don't need high precision the techniques mentioned above work just fine.
could anyone explain me exactly what these params mean ?
I've tried and the results where very weird so i guess i am missunderstanding some of the params for the perspective projection
* {a}_{x,y,z} - the point in 3D space that is to be projected.
* {c}_{x,y,z} - the location of the camera.
* {\theta}_{x,y,z} - The rotation of the camera. When {c}_{x,y,z}=<0,0,0>, and {\theta}_{x,y,z}=<0,0,0>, the 3D vector <1,2,0> is projected to the 2D vector <1,2>.
* {e}_{x,y,z} - the viewer's position relative to the display surface. [1]
Well, you'll want some 3D vector arithmetic to move your origin, and probably some quaternion-based rotation functions to rotate the vectors to match your direction. There are any number of good tutorials on using quaternions to rotate 3D vectors (since they're used a lot for rendering and such), and the 3D vector stuff is pretty simple if you can remember how vectors are represented.
well, just a pice ov advice, you can plot this points into a 3d space (you can do easily this using openGL).
You have to transforrm the lat/long into another system for example polar or cartesian.
So starting from lat/longyou put the origin of your space into the center of the heart, than you have to transform your data in cartesian coord:
z= R * sin(long)
x= R * cos(long) * sin(lat)
y= R * cos(long) * cos(lat)
R is the radius of the world, you can put it at 1 if you need only to cath the direction between yoour point of view anthe points you need "to see"
than put the Virtual camera in a point of the space you've created, and link data from your real camera (simply a vector) to the data of the virtual one.
The next stemp to gain what you want to do is to try to plot timages for your camera overlapped with your "virtual space", definitevly you should have a real camera that is a control to move the virtual one in a virtual space.

Resources