Perspective projection view plane distance calculation confusion? - math

I'm studying from a 3D computer graphics book trying to implement my own software renderer for educational purposes. Everything was going great until the book started talking about perspective projection, it got a bit confusing.
The book (Tricks of the 3D Game Programming Gurus) was talking about computing d: the distance from the view point to the view plane. If we look at this scanned image from the book (I hope Andre doesn't mind because I tried contacting him asking but no reply):
Well, here it's clear that the author meant 0.5 * Width besides the figure and not Width.
Then he continues to say:
tan(θ) = opposite/adjacent = y/x, and in this case, y is the distance and x is related to the width.
Looking at the figure, I can't see how this is correct because the opposite of the angle θ/2 is W/2 and the adjacent is d. So:
tan(θ/2) = (W/2) / d =>
d * tan(θ/2) = (W/2) =>
d = (W/2) / tan(θ/2)
Am I wrong? am I missing anything? did the author mean something else?
Any ideas/thoughts/explanations are appreciated.
EDIT: Although in the book's code it does 'work' when multiplication is used, but it's not correct. Because objects should get smaller the higher FOV value is used, that's why division is correct. But if multiplication is used the higher the FOV the bigger objects get. I think the author didn't notice this because he was using 90 degrees FOV angle most the times and tan(90/2)=1 so it doesn't matter if we multiplied or divided.

Related

Project 3D velocity values from vector field around a sphere to create flow lines

I just cannot figure out how to make an a point with a given velocity move around in cartesian space in my visualization while staying around a sphere (planet).
The input:
Many points with:
A Vector3 position in XYZ (lat/lon coordinates transformed with spherical function below).
A Vector3 velocity (eg. 1.0 m/s eastward, 0.0 m/s elevation change, 2.0 m/s northward).
Note these are not degrees, just meters/second which are similar to my world space units.
Just adding the velocities to the point location will make the points fly of the sphere, which makes sense. Therefore the velocities need to be transformed so stay around the sphere.
Goal: The goal is to create some flowlines around a sphere, for example like this:
Example image of vectors around a globe
So, I have been trying variations on the basic idea of: Taking the normal to center of my sphere, get a perpendicular vector and multiply that again to get a tangent:
// Sphere is always at (0,0,0); but just to indicate for completeness:
float3 normal = objectposition - float3(0,0,0);
// Get perpendicular vector of our velocity.
float3 tangent = cross(normal,velocity);
// Calculate final vector by multiplying this with our original normal
float3 newVector = cross(normal, tangent);
// And then multiplying this with length (magnitude) of the velocity such that the speed is part of the velocity again.
float final_velocity = normalize(newVector) * length(velocity);
However, this only works for an area of the data, it looks like it only works on the half of the western hemisphere (say, USA). To get it (partially) working at the east-southern area (say, South-Africa) I had to switch U and V components.
The XYZ coordinates of the sphere are created using spherical coordinates:
x = radius * Math.Cos(lat) * Math.Cos(lon);
y = radius * Math.Sin(lat);
z = radius * Math.Cos(lat) * Math.Sin(lon);
Of course I have also tried all kinds of variations with multiplying different "Up/Right" vectors like float3(0,1,0) or float3(0,0,1), switching around U/V/W components, etc. to transform the velocity in something that works well. But after about 30 hours of making no progress, I hope that someone can help me with this and point me in the right direction. The problem is basically that only a part of the sphere is correct.
Considering that a part of the data visualizes just fine, I think it should be possible by cross and dot products. As performance is really important here I am trying to stay away from 'expensive' trigonometry operations - if possible.
I have tried switching the velocity components, and in all cases one area/hemisphere works fine, and others don't. For example, switching U and V around (ignoring W for a while) makes both Africa and the US work well. But starting halfway the US, things go wrong again.
To illustrate the issue a bit better, a couple of images. The large purple image has been generated using QGIS3, and shows how it should be:
Unfortunately I have a new SO account and cannot post images yet. Therefore a link, sorry.
Correct: Good result
Incorrect: Bad result
Really hope that someone can shed some light on this issue. Do I need a rotation matrix to rotate the velocity vector? Or multiplying with (a correct) normal/tangent is enough? It looks like that to me, except for these strange oddities and somehow I have the feeling I am overlooking something trivial.
However, math is really not my thing and deciphering formula's are quite a challenge for me. So please bear with me and try to keep the language relative simple (Vector function names are much easier for me than scientific math notation). That I got this far is already quite an achievement for myself.
I tried to be as clear as possible, but if things are unclear, I am happy to elaborate them more.
After quite some frustration I managed to get it done, and just posting the key information that was needed to solve this, after weeks of reading and trying things.
The most important thing is to convert the velocity using rotation matrix no. 6 from ECEF to ENU coordinates. I tried to copy the matrix from the site; but it does not really paste well. So, some code instead:
Matrix3x3:
-sinLon, cosLon, 0,
-cosLon * sinLat, -sinLon * sinLat, cosLat,
cosLon * cosLat, sinLon * cosLat, sinLat
Lon/Lat has to be acquired through a Cartesian to polar coordinate conversion function for the given location where your velocity applies.
Would have preferred a method which required no sin/cos functions but I am not sure if that is possible after all.

Trigonometry: 3D rotation around center point

Yeah, yeah, I checked out the suggested questions/answers that were given to me but most involved quaternions, or had symbols in them that I don't even HAVE on my keyboard.
I failed at high school trig, and while I understand the basic concepts of sin and cos in 2D space, I'm at a loss when throwing in a third plane to deal with.
Basically, I have these things: centerpoint, distance, and angles for each of the three axes. Given that information, I want to calculate the point that is -distance- away from the center point, at the specified angles.
I'm not sure I'm explaining this correctly. My intent is to get what amounts to electrons orbiting around a nucleus, if anyone happens to know how to do that. I am working with Java, JRE 6, if there are any utility classes in there that can help.
I don't want just an answer, but also the how and why of the answer. If I'm going to learn something, i want to learn ABOUT it as well. I am not afraid to take a lesson in trigonometry, or how quaternions work, etc. I'm not looking for an entire course on the answer, but at least some basic understanding would be cool.
If you did this in 2D, you would have a point on a plane with certain x and y coordinates. The distance from the origin would be sqrt(x^2+y^2), and the angle atan(y/2).
If you were given angle phi and distance r you would compute x= r*cos(phi); y=r*sin(phi);
To do this in three dimensions you need two angles - angle in XY plane and angle relative to Z axis. Calling these phi and theta, you compute coordinates as
X = r*cos(phi)*sin(theta);
Y = r*sin(phi)*sin(theta);
Z = r*cos(theta);
When I have a chance I will make a sketch to show how that works.

In OpenGL, How can I determine the bounds of the view at a given depth

I'm playing around with OpenGL and I've got a question that I haven't been able to find an answer to or at least haven't found the right way to ask search engines. I have a a pretty simple setup. An 800x600 viewport and a projection matrix with a 45 degree field of view and near and far planes of 1.0 and 200.0. For the sake of discussion, the modelview matrix is the identity matrix.
What I'm trying to determine is the bounds of the view at a given depth. For example, (0,0,0) is the center of the screen. And I'm looking in the -Z direction.
I want to know, if I draw geometry on a plane 100 units into the screen (0,0,-100), what are the bounds of the view? How far in the x and y direction can I draw in this plane and the geometry still be visible.
More generically, Given a plane parallel to the near and far plane (and between them), what are the visible bounds of that plane?
Also, if what I'm trying to determine has a common name or is a common operation, what's it called? That way I can track down more reading material
Your view angle is 45 degrees, you have a plane at a distance of a away from the camera, with an unkown height h. The whole thing looks like this:
Note that the angle here is half of your field of view.
Dusting off the highschool maths books, we get:
tan(angle) = h/a
Rearrange for h and subsitute the half field of view:
h = tan(FieldOfView / 2) * a;
This is how much your plane extends upwards along the Y axis.
Since screens aren't square, the width of your plane is different to the height. More exactly, the width is the aspect ratio times the height. I.e. w = h * aspectRatio
I hope this answers your question.

Normal Vector of Three Points

Hey math geeks, I've got a problem that's been stumping me for a while now. It's for a personal project.
I've got three dots: red, green, and blue. They're positioned on a cardboard slip such that the red dot is in the lower left (0,0), the blue dot is in the lower right (1,0), and the green dot is in the upper left. Imagine stepping back and taking a picture of the card from an angle. If you were to find the center of each dot in the picture (let's say the units are pixels), how would you find the normal vector of the card's face in the picture (relative to the camera)?
Now a few things I've picked up about this problem:
The dots (in "real life") are always at a right angle. In the picture, they're only at a right angle if the camera has been rotated around the red dot along an "axis" (axis being the line created by the red and blue or red and green dots).
There are dots on only one side of the card. Thus, you know you'll never be looking at the back of it.
The distance of the card to the camera is irrelevant. If I knew the depth of each point, this would be a whole lot easier (just a simple cross product, no?).
The rotation of the card is irrelevant to what I'm looking for. In the tinkering that I've been doing to try to figure this one out, the rotation can be found with the help of the normal vector in the end. Whether or not the rotation is a part of (or product of) finding the normal vector is unknown to me.
Hope there's someone out there that's either done this or is a math genius. I've got two of my friends here helping me on it and we've--so far--been unsuccessful.
i worked it out in my old version of MathCAD:
Edit: Wording wrong in screenshot of MathCAD: "Known: g and b are perpendicular to each other"
In MathCAD i forgot the final step of doing the cross-product, which i'll copy-paste here from my earlier answer:
Now we've solved for the X-Y-Z of the
translated g and b points, your
original question wanted the normal of
the plane.
If cross g x b, we'll get the
vector normal to both:
| u1 u2 u3 |
g x b = | g1 g2 g3 |
| b1 b2 b3 |
= (g2b3 - b2g3)u1 + (b1g3 - b3g1)u2 + (g1b2 - b1g2)u3
All the values are known, plug them in
(i won't write out the version with g3
and b3 substituted in, since it's just
too long and ugly to be helpful.
But in practical terms, i think you'll have to solve it numerically, adjusting gz and bz so as to best fit the conditions:
g · b = 0
and
|g| = |b|
Since the pixels are not algebraically perfect.
Example
Using a picture of the Apollo 13 astronauts rigging one of the command module's square Lithium Hydroxide cannister to work in the LEM, i located the corners:
Using them as my basis for an X-Y plane:
i recorded the pixel locations using Photoshop, with positive X to the right, and positive Y down (to keep the right-hand rule of Z going "into" the picture):
g = (79.5, -48.5, gz)
b = (-110.8, -62.8, bz)
Punching the two starting formulas into Excel, and using the analysis toolpack to "minimize" the error by adjusting gz and bz, it came up with two Z values:
g = (79.5, -48.5, 102.5)
b = (-110.8, -62.8, 56.2)
Which then lets me calcuate other interesting values.
The length of g and b in pixels:
|g| = 138.5
|b| = 139.2
The normal vector:
g x b = (3710, -15827, -10366)
The unit normal (length 1):
uN = (0.1925, -0.8209, -0.5377)
Scaling normal to same length (in pixels) as g and b (138.9):
Normal = (26.7, -114.0, -74.7)
Now that i have the normal that is the same length as g and b, i plotted them on the same picture:
i think you're going to have a new problem: distortion introduced by the camera lens. The three dots are not perfectly projected onto the 2-dimensional photographic plane. There's a spherical distortion that makes straight lines no longer straight, makes equal lengths no longer equal, and makes the normals slightly off of normal.
Microsoft research has an algorithm to figure out how to correct for the camera's distortion:
A Flexible New Technique for Camera Calibration
But it's beyond me:
We propose a flexible new technique to
easily calibrate a camera. It is well
suited for use without specialized
knowledge of 3D geometry or computer
vision. The technique only requires
the camera to observe a planar pattern
shown at a few (at least two)
different orientations. Either the
camera or the planar pattern can be
freely moved. The motion need not be
known. Radial lens distortion is
modeled. The proposed procedure
consists of a closed-form solution,
followed by a nonlinear refinement
based on the maximum likelihood
criterion. Both computer simulation
and real data have been used to test
the proposed technique, and very good
results have been obtained. Compared
with classical techniques which use
expensive equipments such as two or
three orthogonal planes, the proposed
technique is easy to use and flexible.
It advances 3D computer vision one
step from laboratory environments to
real world use.
They have a sample image, where you can see the distortion:
(source: microsoft.com)
Note
you don't know if you're seeing the "top" of the cardboard, or the "bottom", so the normal could be mirrored vertically (i.e. z = -z)
Update
Guy found an error in the derived algebraic formulas. Fixing it leads to formulas that i, don't think, have a simple closed form. This isn't too bad, since it can't be solved exactly anyway; but numerically.
Here's a screenshot from Excel where i start with the two knowns rules:
g · b = 0
and
|g| = |b|
Writing the 2nd one as a difference (an "error" amount), you can then add both up and use that value as a number to have excel's solver minimize:
This means you'll have to write your own numeric iterative solver. i'm staring over at my Numerical Methods for Engineers textbook from university; i know it contains algorithms to solve recursive equations with no simple closed form.
From the sounds of it, you have three points p1, p2, and p3 defining a plane, and you want to find the normal vector to the plane.
Representing the points as vectors from the origin, an equation for a normal vector would be
n = (p2 - p1)x(p3 - p1)
(where x is the cross-product of the two vectors)
If you want the vector to point outwards from the front of the card, then ala the right-hand rule, set
p1 = red (lower-left) dot
p2 = blue (lower-right) dot
p3 = green (upper-left) dot
# Ian Boyd...I liked your explanation, only I got stuck on step 2, when you said to solve for bz. You still had bz in your answer, and I don't think you should have bz in your answer...
bz should be +/- square root of gx2 + gy2 + gz2 - bx2 - by2
After I did this myself, I found it very difficult to substitute bz into the first equation when you solved for gz, because when substituting bz, you would now get:
gz = -(gxbx + gyby) / sqrt( gx2 + gy2 + gz2 - bx2 - by2 )
The part that makes this difficult is that there is gz in the square root, so you have to separate it and combine the gz together, and solve for gz Which I did, only I don't think the way I solved it was correct, because when I wrote my program to calculate gz for me, I used your gx, and gy values to see if my answer matched up with yours, and it did not.
So I was wondering if you could help me out, because I really need to get this to work for one of my projects. Thanks!
Just thinking on my feet here.
Your effective inputs are the apparent ratio RB/RG [+], the apparent angle BRG, and the angle that (say) RB makes with your screen coordinate y-axis (did I miss anything). You need out the components of the normalized normal (heh!) vector, which I believe is only two independent values (though you are left with a front-back ambiguity if the card is see through).[++]
So I'm guessing that this is possible...
From here on I work on the assumption that the apparent angle of RB is always 0, and we can rotate the final solution around the z-axis later.
Start with the card positioned parallel to the viewing plane and oriented in the "natural" way (i.e. you upper vs. lower and left vs. right assignments are respected). We can reach all the interesting positions of the card by rotating by \theta around the initial x-axis (for -\pi/2 < \theta < \pi/2), then rotating by \phi around initial y-axis (for -\pi/2 < \phi < \pi/2). Note that we have preserved the apparent direction of the RB vector.
Next step compute the apparent ratio and apparent angle after in terms of \theta and \phi and invert the result.[+++]
The normal will be R_y(\phi)R_x(\theta)(0, 0, 1) for R_i the primitive rotation matrix around axis i.
[+] The absolute lengths don't count, because that just tells you the distance to card.
[++] One more assumption: that the distance from the card to view plane is much large than the size of the card.
[+++] Here the projection you use from three-d space to the viewing plane matters. This is the hard part, but not something we can do for you unless you say what projection you are using. If you are using a real camera, then this is a perspective projection and is covered in essentially any book on 3D graphics.
right, the normal vector does not change by distance, but the projection of the cardboard on a picture does change by distance (Simple: If you have a small cardboard, nothing changes.
If you have a cardboard 1 mile wide and 1 mile high and you rotate it so that one side is nearer and the other side more far away, the near side is magnified and the far side shortened on the picture. You can see that immediately that an rectangle does not remain a rectangle, but a trapeze)
The mostly accurate way for small angles and the camera centered on the middle is to measure the ratio of the width/height between "normal" image and angle image on the middle lines (because they are not warped).
We define x as left to right, y as down to up, z as from far to near.
Then
x = arcsin(measuredWidth/normWidth) red-blue
y = arcsin(measuredHeight/normHeight) red-green
z = sqrt(1.0-x^2-y^2)
I will calculate tomorrow a more exact solution, but I'm too tired now...
You could use u,v,n co-oridnates. Set your viewpoint to the position of the "eye" or "camera", then translate your x,y,z co-ordinates to u,v,n. From there you can determine the normals, as well as perspective and visible surfaces if you want (u',v',n'). Also, bear in mind that 2D = 3D with z=0. Finally, make sure you use homogenious co-ordinates.

Math programming help for a gimble-based painting machine

I'm an artist involved with building various sorts of computer controlled machines. I've started prototyping a gimble-based XY painting machine and have realized that the maths needed are out of my reach. I'm a decent enough programmer but not strong in math- esp. 3D math.
To get a sense of what I'm needing to do, it might be helpful to look at the rig:
Early prototype:
http://roypardi.com/gimble/gimbleSmall.MOV (small video)
http://roypardi.com/gimble/gimbleLarge.mov (larger video)
The two inner rings represent the X/Y axes and are controlled by stepper motors. I want to be able to use both raster images and vector data (gcode). So I need to be able to address a point in 2D space on the paper/from my data and have the gimble figure out what orientation it needs to be at in order to get there (i.e. how much to step each motor).
I've been searching out 2D > 3D projection, Euler angles, etc. but I'm out of my depth. Any pointers, pushes in the right direction, or code snippets would be most welcome. I can make sense of most programming languages.
Very nice machine you have made, I hope this works for you I believe it is correct.
The way I see it, is to get one angle is simple, but the other is slightly harder to visualise as we have tilted the axis which it turns upon.
I'm going to avoid using tan, as when programming this could result in a division by 0, which could be frustrating. Also Z is going to be the height of the origin above the paper.
YAxis = arcsin( X / sqrt(X² + Z²))
XAxis = arcsin( Y / sqrt(Y² + X² + Z²))
or we could use
XAxis = arcsin(Y / sqrt(Y² + Z²))
YAxis = arcsin( X / sqrt(X² + Y² + Z²))
Also, I'd very much like to see a video of this plotting, if it works.
Edit:
After thinking about it i believe only one solution will work it depends on which axis is affected by the other. Is the YAxis in the Middle or the Xaxis?
I think it's a problem of simple http://en.wikipedia.org/wiki/Trigonometry
Let's say that the distance from the centre of your rings to the nearest point on the paper (which I'll call point 'O' for 'Origin') is distance X.
Take another point P directly north of O, whose distance from O is Y.
To paint this point, you need the angle alpha such that tan(alpha)=Y/X, i.e. you can calculate alpha using the formula "arctan(Y/X)" [arctan is sometimes also known as atan]. Arctan is a trignometric function, which I think you'll probably find defined in the API of a general purpose math library.
The above is the simplest case.
The only other case that I can think of is when the point P isn't due north. Instead of being due north, let's say that its distance is Y1 to the north, and Y2 to the east. The solution is two angles (one angle for each of two rings), one of which is "arctan(Y1/X)" and the other of which is "arctan(Y2/X)".
Perhaps I misunderstand, but I don't believe a gimbal will do what you want. A gimbal can point in any 3D direction, but it cannot move to arbitrary points in 3D space. If the plane of the paper intersects the volume swept by the pen held in the gimbal, the pen might be able to draw a circle, but nothing more. Even drawing a circle is not a sure thing, since in this case the paper would also intersect the volume swept by the gimbal rings; trying to orient the pen would make a ring hit the paper.
I think what you want is a plotter, not a gimbal.

Resources