Relationship between distance in 3D space and its z depth - math

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.

Related

Formula for calculating camera x,y,z position to force 3D point to appear at left side of the screen and rightmost position on the globe

I'd need a formula to calculate 3D position and direction or orientation of a camera in a following situation:
Camera starting position is looking directly into center of the Earth. Green line goes straight up to the sky
Position that camera needs to move to is looking like this
Starting position probably shouldn't matter, but the question is:
How to calculate camera position and direction given 3D coordinates of any point on the globe. In the camera final position, the distance from Earth is always fixed. From desired camera point of view, the chosen point should appear at the rightmost point of a globe.
I think what you want for camera position is a point on the intersection of a plane parallel to the tangent plane at the location, but somewhat further from the Center, and a sphere representing the fixed distance the camera should be from the center. The intersection will be a circle, so there are infinitely many camera positions that work.
Camera direction will be 1/2 determined by the location and 1/2 determined by how much earth you want in the picture.
Suppose (0,0,0) is the center of the earth, Re is the radius of the earth, and (a,b,c) is the location on the earth you want to look at. If it's in terms of latitude and longitude you should convert to Cartesian coordinates which is straightforward. Your camera should be on a plane perpendicular to the vector (a,b,c) and at a height kRe above the earth where k>1 is some number you can adjust. The equation for the plane is then ax+by+cz=d where d = kRe^2. Note that the plane passes through the point (ka,kb,kc) in space, which is what we wanted.
Since you want the camera to be at a certain height above the earth, say h*Re where 1 < k < h, you need to find points on ax+by+cz=d for which x^2+y^2+z^2 = h^2*Re^2. So we need the intersection of the plane and a sphere. It will be easier to manage if we have a coordinate system on the plane, which we get from an orthonormal system which includes (a,b,c). A good candidate for the second vector in the orthonormal system is the projection of the z-axis (polar axis, I assume). Projecting (0,0,1) onto (a,b,c),
proj_(a,b,c)(0,0,1) = (a,b,c).(0,0,1)/|(a,b,c)|^2 (a,b,c)
= c/Re^2 (a,b,c)
Then the "horizontal component" of (0,0,1) is
u = proj_Plane(0,0,1) = (0,0,1) - c/Re^2 (a,b,c)
= (-ac/Re^2,-bc/Re^2,1-c^2/Re^2)
You can normalize the vector to length 1 if you wish but there's no need. However, you do need to calculate and store the square of the length of the vector, namely
|u|^2 = ((ac)^2 + (bc)^2 + (Re^2-c^2))/Re^4
We could complicate this further by taking the cross product of (0,0,1) and the previous vector to get the third vector in the orthonormal system, then obtain a parametric equation for the intersection of the plane and sphere on which the camera lies, but if you just want the simplest answer we won't do that.
Now we need to solve for t such that
|(ka,kb,kc)+t(-ac/Re^2,-bc/Re^2,1-c^2/Re^2)|^2 = h^2 Re^2
|(ka,kb,kc)|^2 + 2t (a,b,c).u + t^2 |u|^2 = h^2 Re^2
Since (a,b,c) and u are perpendicular, the middle term drops out, and you have
t^2 = (h^2 Re^2 - k^2 Re^2)/|u|^2.
Substituting that value of t into
(ka,kb,kc)+t(-ac/Re^2,-bc/Re^2,1-c^2/Re^2)
gives the position of the camera in space.
As for direction, you'll have to experiment with that a bit. Some vector that looks like
(a,b,c) + s(-ac/Re^2,-bc/Re^2,1-c^2/Re^2)
should work. It's hard to say a priori because it depends on the camera magnification, width of the view screen, etc. I'm not sure offhand whether you'll need positive or negative values for s. You may also need to rotate the camera viewport, possibly by 90 degrees, I'm not sure.
If this doesn't work out, it's possible I made an error. Let me know how it works out and I'll check.

Maximum Projection FIeld of View

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.

Math/Calculations for infinite/repeating world with rotation

How do I make a infinite/repeating world that handles rotation, just like in this game:
http://bloodfromastone.co.uk/retaliation.html
I have coded my rotating moving world by having a hierarchy like this:
Scene
- mainLayer (CCLayer)
- rotationLayer(CCNode)
- positionLayer(CCNode)
The rotationLayer and positionLayer have the same size (4000x4000 px right now).
I rotate the whole world by rotating the rotationLayer, and I move the whole world by moving the positionLayer, so that the player always stays centered on the device screen and it is the world that moves and rotates.
Now I would like to make it so that if the player reaches the bounds of the world (the world is moved so that the worlds bounds gets in to contact with the device screen bounds), then the world is "wrapped" to the opposite bounds so that the world is infinite. If the world did not rotate that would be easy, but now that it does I have no idea how to do this. I am a fool at math and in thinking mathematically, so I need some help here.
Now I do not think I need any cocos2d-iphone related help here. What I need is some way to calculate if my player is outside the bounds of the world, and then some way to calculate what new position I must give the world to wrap the world.
I think I have to calculate a radius for a circle that will be my foundry inside the square world, that no matter what angle the square world is in, will ensure that the visible rectangle (the screen) will always be inside the bounds of the world square. And then I need a way to calculate if the visible rectangle bounds are outside the bounds circle, and if so I need a way to calculate the new opposite position in the bounds circle to move the world to. So to illustrate I have added 5 images.
Visible rectangle well inside bounds circle inside a rotated square world:
Top of visible rectangle hitting bounds circle inside a rotated square world:
Rotated square world moved to opposite vertical position so that bottom of visible rectangle now hitting bounds circle inside rotated world:
Another example of top of visible rectangle hitting bounds circle inside a rotated square world to illustrate a different scenario:
And again rotated square world moved to opposite vertical position so that bottom of visible rectangle now hitting bounds circle inside rotated world:
Moving the positionLayer in a non-rotated situation is the math that I did figure out, as I said I can figure this one out as long as the world does not get rotate, but it does. The world/CCNode (positionLayer) that gets moved/positioned is inside a world/CCNode (rotationLayer) that gets rotated. The anchor point for the rotationLayer that rotates is on the center of screen always, but as the positionLayer that gets moved is inside the rotating rotationLayer it gets rotated around the rotationLayer's anchor point. And then I am lost... When I e.g. move the positionLayer down enough so that its top border hits the top of the screen I need to wrap that positionLayer as JohnPS describes but not so simple, I need it to wrap in a vector based on the rotation of the rotationLayer CCNode. This I do not know how to do.
Thank you
Søren
Like John said, the easiest thing to do is to build a torus world. Imagine that your ship is a point on the surface of the donut and it can only move on the surface. Say you are located at the point where the two circles (red and purple in the picture) intersect:
.
If you follow those circles you'll end up where you started. Also, notice that, no matter how you move on the surface, there is no way you're going to reach an "edge". The surface of the torus has no such thing, which is why it's useful to use as an infinite 2D world. The other reason it's useful is because the equations are quite simple. You specify where on the torus you are by two angles: the angle you travel from the "origin" on the purple circle to find the red circle and the angle you travel on the red circle to find the point you are interested in. Both those angles wrap at 360 degrees. Let's call the two angles theta and phi. They are your ship's coordinates in the world, and what you change when you change velocities, etc. You basically use them as your x and y, except you have to make sure to always use the modulus when you change them (your world will only be 360 degrees in each direction, it will then wrap around).
Suppose now that your ship is at coordinates (theta_ship,phi_ship) and has orientation gamma_ship. You want to draw a square window with the ship at its center and length/width equal to some percentage n of the whole world (say you only want to see a quarter of the world at a time, then you'd set n = sqrt(1/4) = 1/2 and have the length and width of the window set to n*2*pi = pi). To do this you need a function that takes a point represented in the screen coordinates (x and y) and spits out a point in the world coordinates (theta and phi). For example, if you asked it what part of the world corresponds to (0,0) it should return back the coordinates of the ship (theta_ship,phi_ship). If the orientation of the ship is zero (x and y will be aligned with theta and phi) then some coordinate (x_0,y_0) will correspond to (theta_ship+k*x_0, phi_ship+k*y_0), where k is some scaling factor related to how much of the world one can see in a screen and the boundaries on x and y. The rotation by gamma_ship introduces a little bit of trig, detailed in the function below. See the picture for exact definitions of the quantities.
!Blue is the screen coordinate system, red is the world coordinate system and the configuration variables (the things that describe where in the world the ship is). The object
represented in world coordinates is green.
The coordinate transformation function might look something like this:
# takes a screen coordinate and returns a world coordinate
function screen2world(x,y)
# this is the angle between the (x,y) vector and the center of the screen
alpha = atan2(x,y);
radius = sqrt(x^2 + y^2); # and the distance to the center of the screen
# this takes into account the rotation of the ship with respect to the torus coords
beta = alpha - pi/2 + gamma_ship;
# find the coordinates
theta = theta_ship + n*radius*cos(beta)/(2*pi);
phi = phi_ship + n*radius*sin(beta)/(2*pi));
# return the answer, making sure it is between 0 and 2pi
return (theta%(2*pi),phi%(2*pi))
and that's pretty much it, I think. The math is just some relatively easy trig, you should make a little drawing to convince yourself that it's right. Alternatively you can get the same answer in a somewhat more automated fashion by using rotations matrices and their bigger brother, rigid body transformations (the special Euclidian group SE(2)). For the latter, I suggest reading the first few chapters of Murray, Li, Sastry, which is free online.
If you want to do the opposite (go from world coordinates to screen coordinates) you'd have to do more or less the same thing, but in reverse:
beta = atan2(phi-phi_ship, theta-theta_ship);
radius = 2*pi*(theta-theta_ship)/(n*cos(beta));
alpha = beta + pi/2 - gamma_ship;
x = radius*cos(alpha);
y = radius*sin(alpha);
You need to define what you want "opposite bounds" to mean. For 2-dimensional examples see Fundamental polygon. There are 4 ways that you can map the sides of a square to the other sides, and you get a sphere, real projective plane, Klein bottle, or torus. The classic arcade game Asteroids actually has a torus playing surface.
The idea is you need glue each of your boundary points to some other boundary point that will make sense and be consistent.
If your world is truly 3-dimensional (not just 3-D on a 2-D surface map), then I think your task becomes considerably more difficult to determine how you want to glue your edges together--your edges are now surfaces embedded in the 3-D world.
Edit:
Say you have a 2-D map and want to wrap around like in Asteroids.
If the map is 1000x1000 units, x=0 is the left border of the map, x=999 the right border, and you are looking to the right and see 20 units ahead. Then at x=995 you want to see up to 1015, but this is off the right side of the map, so 1015 should become 15.
If you are at x=5 and look to the left 20 units, then you see x=-15 which you really want to be 985.
To get these numbers (always between 0 and 999) when you are looking past the border of your map you need to use the modulo operator.
new_x = x % 1000; // in many programming languages
When x is negative each programming language handles the result of x % 1000 differently. It can even be implementation defined. i.e. it will not always be positive (between 0 and 999), so using this would be safer:
new_x = (x + 1000) % 1000; // result 0 to 999, when x >= -1000
So every time you move or change view you need to recompute the coordinates of your position and coordinates of anything in your view. You apply this operation to get back a coordinate on the map for both x and y coordinates.
I'm new to Cocos2d, but I think I can give it a try on helping you with the geometry calculation issue, since, as you said, it's not a framework question.
I'd start off by setting the anchor point of every layer you're using in the visual center of them all.
Then let's agree on the assumption that the first part to touch the edge will always be a corner.
In case you just want to check IF it's inside the circle, just check if all the four edges are inside the circle.
In case you want to know which edge is touching the circumference of the circle, just check for the one that is the furthest from point x=0 y=0, since the anchor will be at the center.
If you have a reason for not putting the anchor in the middle, you can use the same logic, just as long as you include half of the width of each object on everything.

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