Maximum Projection FIeld of View - math

What is the maximum field of view that can be accomplished via a projection matrix with no distortion? There is a hard limit of < 180 degrees before the math completely breaks down, but experimenting with 170-180 degrees leads me to believe that distortion and deviation from reality begins prior to the hard limit. Where does the point at which the projection matrix begins to distort the view lie?
EDIT: Maybe some clarification is in order. As I increased the FOV angle toward 180 with a fixed render size, I observed objects getting smaller much faster than they should in reality. With a fixed render size and the scene/camera being identical, the diameter of objects should be inversely proportionate to the field of view size, if I'm not mistaken. Yet I observed them shrinking exponentially, down to 0 size at 180 degrees. This is undoubtedly due to the fact that X and Y scaling in a projection matrix are proportionate to cot(FOV / 2). What I'm wondering is when exactly this distortion effect begins.

Short answer: There is no deviation from reality and there is always distortion.
Long answer: Common perspective projection matrices project a 3D scene onto a 2D plane with respect to a camera position. If you consider a fixed distance of the plane from the camera, then the field of view defines the plane's size. Larger angles define larger planes. If you fix the size, then the field of view defines the distance. Larger angles define a smaller distance.
Viewed from the camera, the image does not change whether it sees the original scene or the plane with the projected scene (i.e. there is no deviation from reality).
Problems occur when you look at the plane from a different view point. E.g. when the projected plane is displayed on the screen (fixed size), there is only one position of the camera (your eye) from which the image is realistic. For very large field of view angles, you'll need to be very close to the screen to find that position. All other positions will not result in the correct image. For small field of view angles, the resulting distortion is very small and users will mostly consider it a realistic projection. That's because for small angles, the projected image does not change significantly if you change the distance slightly (changing the distance from 1 meter to 1.1 meters (10%) with a small fov is less problematic than changing the distance from 0.1 meters to 0.2 meters (100%) with a large fov). The most extreme case is an orthographic projection with virtually zero fov. Then, the projection does not depend on the distance at all.
And there is always distortion if objects are not at the projection axis (i.e. for any fov greater than zero). This results in spheres not projecting to perfect circles. This effect also happens with small fovs but there it is less obvious.

Related

Resize image using real world measurements

I'm working on a floor design app where the user can import a floor texture and the app will place the texture on to a room image.
I've managed to transform the perspective of the floor image so that it matches the room image - thanks to this answer, but I'm now stuck on scaling the floor image to match the room image dimensions.
I know the real dimensions of the wooden floor (177mm x 1220mm per plank), I know the height of an object in the room image (height of white tile near sink is 240mm) and I know the distance between the camera and the white tile (roughly 2500mm). The room image size is 2592x1936, the floor image size is 1430x1220.
The room image was taken with from an iPad air camera to which I can't seem to find any info regarding the focal length and sensor size, the nearest I could find was a 3.3 focal length with 3.6mm sensor height (this may be where I'm going wrong).
I tried using this equation
The numbers I plugged in to the equation,
2662 = (3.3 240 x 1936) / (160 x 3.6)
I then tried to work out the object height for a wooden plank in the floor image,
(3.3 x 1220 x 1936) / (2662 x 3.6) = 813 px
I then divided the image height by the object height to get a ratio = 2.38.
This image is with a 2.38 ratio applied to the floor image which isn't quite right.
I know I'm going wrong somewhere or going the complete wrong way about it, hope somebody can point me in the right direction.
Thanks
I'd extend the lines of the tile till they touch the edge where the back wall meets the floor. Using this technique you can transfer a length from the wall plane to an equal length in the floor plane. So at that point, all you have to do is match lengths along a single line, namely the lengths between planks and the lengths between your transferred points. But you have to do this in a projectively consistent fashion. The most versatile tool for projective measurements is the cross ratio. An application very similar to what you have here is described in How to calculate true lengths from perspective projection on Math SE. If your vanishing point on that line where the walls meet is indeed at infinity (which appears to be approximately the case in your setup), you can get by with some simpler computations, but unless you can guarantee that this will always be the case, I'd not rely on that.
The above will help you adjust the scale in one direction only. The direction perpendicular to that is still open, though. In your exaple that would be the depth direction, the direction away from the camera. Do you have any reference points for that direction? It looks to me as though you might be able to use one complete tile on the left wall, before the window starts. But depending on how the corner between the two walls is tiled, that might be slightly off.
To illustrate these ideas, look at the picture above. Since the red lines appear almost horizontal, seeing the effects of perspective there is pretty hard. Therefore I'll do the other direction. Suppose the tile in the corner is indeed the same visible size as all the other tiles on the wall. So you know the real world distance between A1 and B1. You project along the blue vertical lines (vertical in the real world, not neccessarily the image) down to A2 and B2 which is where the left wall plane meets the floor plane.
Why do they meet there? Well, the lines A1,A2 is where the left all meets the back wall. The line A2,A3 is where the back wall meets the floor. Both of these plane intersections are actually visible at least in part, which made drawing the lines possible. So at A2 all three planes meet, and connecting that to the far point F gives the third edge, where the left wall meets the floor.
Since the segments A1,B1 and A2,B2 are just vertically transported versions of one another in the real world, they have equals length. That transportation was along the left wall in the vertical direction. Now transport them again, this time in the floor plane and in the left-right direction. You do so using the red lines, which are either parallel or meet at a point (which is pretty far away in this example). These red lines A2,A3 and B2,B3 are parallel in the real world, and their distance is still the edge length of that tile.
Now start measuring something, e.g. distance between C and D. To do that, compute the cross ratio (F,A3;B3,C) which expresses the distance from A3 to C, expressed in multiples of the distance from A3 to B3, and using F as the point at infinity. Do the same for D, and then the difference will be the length from C to D, expressed in multiples of the distance from A3 to B3. So the distance between C and D is 4.42 tile edge lengths in this example. Scale your image to fit this figure.

Weird phenomenon with three.js plane

This is the first question I've ever asked on here! Apologies in advance if I've done it wrong somehow.
I have written a program which stacks up spheres in three.js.
Each sphere starts with randomly generated (within certain bounds) x and z co-ordinates, and a y co-ordinate high above the ground plane. I casts rays from each of the sphere's vertices to see how far down it can fall before it intersects with an existing mesh.
For each sphere, I test it in 80 different random xz positions, see where it can fall the furthest, and then 'drop' it into that position.
This is intended to create bubble towers like this one:
However, I have noticed that when I make the bubble radius very small and the base dimensions of the tower large, this happens:
If I turn the recursions down from 80, this effect is less apparent. For some reason, three.js seems to think that the spheres can fall further at the corners of the base square. The origin is exactly at the center of the base square - perhaps this is relevant.
When I console log all the fall-distances I'm receiving from the raycaster, they are indeed larger the further away you get from the center of the square... but only at the 11th or 12th decimal place.
This is not so much a problem I am trying to solve (I could just round fall distances to the nearest 10th decimal place before I pick the largest one), but something I am very curious about. Does anyone know why this is happening? Has anybody come across something similar to this before?
EDIT:
I edited my code to shift everything so that the origin is no longer at the center of the base square:
So am I correct in thinking... this phenomenon is something to do with distance from the origin, rather than anything relating to the surface onto which the balls are falling?
Indeed, the pattern you are seeing is exactly because the corners and edges of the bottom of your tower are furthest from the origin where you are dropping the balls. You are creating a right triangle (see image below) in which the vertical "leg" is the line from the origin from which you are dropping the balls down to the point directly below on mesh floor (at a right angle to the floor - thus the name, right triangle). The hypotenuse is always the longest leg of a right triangle, and the futher out your rays cast from the point just below the origin, the longer the hypotenuse will be, and the more your algorithm will favor that longer distance (no matter how fractional).
Increasing the size of the tower base would exaggerate this effect as the hypotenuse measurements can now grow even larger. Reducing the size of the balls would also favor the pattern you are seeing, as now each ball is not taking up as much space, and so the distant measurments to the corners won't fill in as quickly as they would with larger balls so that now more balls will congregate at the edges before filling in the rest of the space.
Moving your dropping origin to one side or another creates longer distances (hypotenuses) to the opposites sides and corners, so that the balls will fill in those distant locations first.
The reason you see less of an effect when you reduce the sample size from 80 to say, 20, is that there are simply fewer chances to detect these more distant locations to which the balls could fall (an odds game).
A right triangle:
A back-of-the-napkin sketch:

Relationship between distance in 3D space and its z depth

I have a flat plane of 2D graphics with a camera pointing at them. I want to get the effect so when a user pinches and zooms, it looks like they anchored their fingers on the plane and can pinch zoom realistically. To do this, I need to calculate the the distance between their fingers into distance in 3D space (which I already can do), but then I need to map that 3D distance to a z value.
For example, if a 100 units wide square and shrunk to 50 units (50%), how much further back would the camera need to move to make that 100 unit square shrink by half?
So to put it simply, If I have the distance in 3D space, how do I calculate the distance of the camera needed to shrink that 3D space by a certain amount?
EDIT:
So, I tried it myself and came up with this formula:
So let's say you are 1 unit away from the object.
When you want to shrink it to 50% (zoomfactor) the new distance equals 2 units => 1 / 0.5 = 2. The camera must be twice as far away.
Moving the camera closer to the plane for zooming only works with a perspective projection. The absolute distance depends on the angle of view. Usually you zoom by reducing the angle of view and not moving the camera at all.
If you are using an orthographic projection you can simply adjust the field of view / scale the projection matrix.

How to fake a 2D sprite moving on the Z axis?

I will precise my question, I have a 2D object moving in a 2D world (X,Y), and I want to fake a movement on the Z axis. So I believe the best is to play with its extent (width,height) and position a bit.
But what would be the equation to determine the new extent for an object of size(w,h) and moving from 1 meter forward the camera (Z axis)? What would be the parameters of such a function?
Thanks in advance for your help.
Use a projection by storing the object's true (X,Y,Z) coordinates and display from a camera K units above the plane by a 2D projection (K*X/(Z+K),K*Y/(Z+K)) where +Z moves away from the camera.
To change the height, width follow a similar pattern with (DX,DY) the true size of the sprite and (K*DX/(Z+K),K*DY/(Z+K)) the apparent (drawn) size.
To do this right you can follow the advice from this FlipCode article.
The main parameters would be the distance to the camera and the aperture angle.
It is simple to determine the new size by new_size = size / distance.
Notice that objects that have no distance would be of infinite size.
To get the effect of the aperture angle you would want to include another factor f:
new_size = f * size / distance
Where f is the distance of unit size.
Distance of unit size is the distance where the image would be drawn at its original size.
Of course, this must not be zero too. By this distance you define impicitly the aperture angle.
When I talk about size I mean width and height so the formula applies on both.
I hope you can follow my explanations.
The width and height will be inversely proportional to the distance from the viewer. If they are twice as far away, the size will be half. So If your "natural" distance from the viewer is A, and the new position is at A+Z, you will want to multiply the original width and height by A/(A+Z). This also works for small negative Z values (object is closer to viewer and will appear larger).

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.

Resources