I am trying to create a game using one-point perspective. Everything works fine for points within the view but goes wrong with the negative depth. I understand the perspective as shown on the following picture (source).
In general, I took a point at some distance from the left of the right vertical edge of the frame along the lower horizontal line (5 points in this case), join it with the O' point (line H'O') and where the line intersects the vertical line (at point H') is the depth line (of 5 in this case). This works well even for negative depth (as the line H'O' intersect the vertical line below the viewpoint). However, if the depth is more then is the distance of O' (that mean the point would be on the right from the O') the line flip and the H' end on top of the viewpoint (although it should end up below).
How should I correct it, so the point with negative depth is transformed correctly (mean from 3D space to 2D space)?
EDIT
This image is probably better.
My question is how to handle points with negative depth (should end up below the screen) higher then is a distance of transversal.
The points to the right of the point O', along the line determined by the lower edge of the frame, correspond to points that are behind the observer, so technically, the observer cannot see them. To see the points behind you, means that you have to turn around, so you need to change the position of the screen. Draw a copy of the black square frame to the right of the point O', so that the new square is the mirror symmetric image of the original frame square with respect to the line orthogonal to the horizon line and passing trough the point O'.
Edit: The points with negative depth to the right of point O' (i.e. a point behind the observer) is supposed to be mapped above the horizontal blue line. This is the right way to go.
I assume your coordinate system in three dimensions has its origin at the lower right corner of the square frame on your picture. The x axis (I think how you measure width) runs along the lower horizontl edge of the frame, while the y axis (what you call height) is along the right vertical edge of the frame. The depth axis is in three dimensions and it's perpendicular to the plane of the square frame (so it is parallel to the ground). It starts from the lower right corner of the frame square. Assume that the distance of point O' from the right vertical edge of the square is S and the coordinates of the point C are {C1, C2} (C1 is the distance of point C from the right vertical edge and C2 is the distance of C from the lower horizontal edge of the square).
Given the coordinates {w, h, d} (w - width, h - height, d - depth) of a point in three dimensions, its representation on the two dimesnional square screen is gievn by the formulas:
x = (S*w + C1*d)/(S+d)
y = (S*h + C2*d)/(S+d)
So the points you gave as an example in the comments are
P1 = {h = 5, w = 5, d = 5} and P2 = {h = 5, w = 5, d = -10}
Their representation on the screen is
P1_screen = {(S*5 + C1*5)/(S+5), (S*5 + C2*5)/(S+5)}
P2_screen = {(S*5 - C1*10)/(S-10), (S*5 - C2*10)/(S-10)}
whatever your parameters S, C1 and C2 are. The representation of the (infinte) line connecting points P1 and P2 is represented on the screen as the (infinite) line connecting the points P1_screen and P2_screen. However, if you want the 2D representation of the visible part of the segment that connects P1 and P2, then you have to draw the (infinite) line between P1_screen and P2_screen and exclude the following two segment: segment [P1_screen, P2_screen] and the segment from P2_screen along the line up towards the upper top edge. You have to draw on the screen only the segment from the infinite line connecting P1_screen and P2_screen which starts from P1_Screen and goes down towards the lower horizontal edge of the screen.
Related
I'm working on a project with two infrared positioning cameras which output the (X,Y) coordinate of any IR source. I'm placing them next to each other and my goal is to measure the 3D coordinate (X,Y,Z) of the IR source, using the same technique our eyes use to measure depth.
I have drawn a (lousy) sketch here
which illustrates what I'm trying to calculate. The red dot is my IR source, which can also be seen on the 'views' of the camera to the right. I am trying to measure the length of the blue line.
I have a few known variables:
The cameras have a resolution of 1024x768 (which also means that this is the maximum of the (X,Y) coordinate mentioned earlier)
Horizontally the field of view is 41deg, vertically 31deg.
I have yet to decide on the distance between cameras (AB), but this will be a known variable. Let's make it 30 cm for now.
Sadly I cannot seem to find the focal length of the camera.
Ultimately I'm hoping for an (X,Y,Z) coordinate relative to the middle point of AB. How would I go about measuring (Z)?
I am not sure how well aligned your cameras are, but from your pictures I am beginning to assume that the camera A and camera B are so well aligned that the rectangle representing the camera B's screen is simply horizontal translation of the rectangle representing the camera A's screen. What I mean by that is that the corresponding edges of the screens' rectangles are parallel to each other and the two rectangular screens lie in a common vertical plane perpendicular to the ground. Now, consider the plane parallel to the vertical plane that contains the two camera screens and passing through the focal points A and B of the two cameras. Call this latter plane the screen_plane. Also, the focal points A and B are at an equal height from the ground. If that is the case, and if I assume that c = |AB| is the distance between the focal points of the two cameras, and if I put a coordinate system at A, so that the x axis is horizontal to the ground, the y axis is perpendicular to the ground, and the z axis is parallel to the ground but perpendicular to the screen, then the focal point of camera B would have coordinates ( c, 0, 0 ). As an example, you have given c = 30 cm. Also the screen_plane is spanned by the x and y axes described above and the z axis is perpendicular to the screen_plane.
If that is the setting you want to work with, then the red point P will appear on both screens with the same coordinate Y_A = Y_B but different coordinates X_A and X_B.
Then let us denote by theta the horizontal field of view angle, which you have determined as theta = 41 deg. Just to be clear, I am assuming the angle between the leftmost side to the rightmost side of view is 2 * theta = 82 deg.
If I understand correctly, you are trying to calculate the distance Z between the vertical plane screen_plane that contains both camera focal points and the plane parallel to screen_plane and passing through the red point P, i.e. you are trying to calculate the distance from P to the vertical plane screen_plane.
Then, here is how you calculate Z:
Step 1: From the image of point P on screen A calculate the distances (e.g. the number of pixels) from P to the vertical edges of the screen. Say they are dist_P_to_left_edge and dist_P_to_right_edge. Set
a_A = dist_P_to_left_edge / (dist_P_to_left_edge + dist_P_to_right_edge) (this one is not really necessary)
b_A = dist_P_to_right_edge / (dist_P_to_left_edge + dist_P_to_right_edge)
Step 2: Do the same with the image of point P on screen B:
a_B = dist_P_to_left_edge / (dist_P_to_left_edge + dist_P_to_right_edge)
b_B = dist_P_to_right_edge / (dist_P_to_left_edge + dist_P_to_right_edge) (this one is not really necessary)
Step 3: Apply the formula:
Z = c * cot(theta) / (2 * (1 - b_A - a_B) )
So for example, from the pictures of the screens of camera A and B you have provided, I measured with a ruler, that
b_A = 4/38
a_B = 12.5/38
and from the data you have included
theta = 41 deg
c = 30 cm
so I have calculated that the length of the blue segment on your picture is
Z = 30 * cos(41*pi/180) / (sin(41*pi/180) * (1 - 4/38 - 12.5/38))
= 60.99628 cm
I have some triangles in 3D space, which originate from 0,0,0 and extend towards two points p1= -x0, 0, z0 and p2= +x0, 0, z0. This is in Unity, such that +z is the forward axis (i.e. they lie flat). Each triangle is its own mesh, pivot is at 0,0,0.
Now, I would like to rotate these (using Quaternion.LookRotation) such that their ends form a continuous polygon, in case of three triangles a triangle, in case of four triangles a square, etc.
My approach is to calculate the incircle radius of the resulting polygon based on the length of each triangle (which is 2*x0). If I now calculate n points on this circle (where n is the number of triangles I have), I get x/y coordinates which I can directly use to set the "up" axis of each triangle correctly, i.e. Quaternion.LookRotation(Vector3.forward, new Vector3(x,y,0)). This orients the triangle correctly around the z axis, i.e. the center is still on 0,0,1.
However, and this has me stumped, I still need to change the forward axis of the triangles such that they tilt to form the final polygon. I tried using new Vector3(x,y,z0) which gives an almost correct result, but leads to an overlap at the edges. I suspect this is somehow due to the fact that rotation of the triangles effectively changes z0, but I am not sure how to proceed.
My question is, how to calculate the new forward axis such that the triangles align properly?
The problem is setting the forward axis to (x,y,z0), which is wrong since the length of the vector (x,y,z0) does not equal the original length (which is just z0). The z value thus needs to be adjusted such that new Vector(x,y,z1).magnitude == z0. This can be done by calculating
Mathf.Sqrt(Mathf.Pow(z0, 2) - Mathf.Pow(x, 2) - Mathf.Pow(y, 2))
Problem solved.
I'm looking for a math formula that on a graph plotting Y as a function of X, before a specified starting point (a value of X, or even better, X and Y coordinates) will have a certain slope, then after that it will draw an arc of a specified radius that will end when it reaches a second specified slope, and from the point on will be another straight line of that second slope.
I'm am aware that because it's Y as a function of X, the slope parameters would need to be bigger than exactly -90 and smaller than exactly 90 degrees; i'm not worried about any misbehavior at (or beyond) those extremes.
Actually, i would be even happier with a formula that takes a starting and ending points (2d coordinates), and starting and ending slopes; and will have two arcs in between (with a straight line between them when needed), connecting the two straight lines seamlessly (obviously the X of the ending point needs to be bigger than the X for the starting point; i don't care what happens when that isn't the case). But i imagine such a formula might be much harder to come up with than what i asked first.
ps: by "arc" i mean segment of a circle; as in, if both axes of the graph have the same scale, the arcs will have the correct aspect ratio for a circle of the same radius.
Well I see it like this:
compute P0
as intersection of lines A + t*dA and B - t*dB
compute P1 (center of circle)
it is intersection of translated lines A->P0 and B->P0 perpendicular by radius r. There are 2 possibilities so choose the right one (which leads to less angle of circular part).
compute P2,P3
just an intersection between lines A-P0 and B-P0 and perpendicular line from P1 to it
the curve
// some constants first
da=P2-A;
db=B-P3;
a2=atan2(P2.x-P1.x,P2.y-P1.y);
a3=atan2(P3.x-P1.x,P3.y-P1.y);
if (a2>a3) a3-=M_PI*2.0;
dang=a3-a2;
// now (x,y)=curve(t) ... where t = <0,3>
if (t<=1.0)
{
x=A.x+t*da.x;
y=A.y+t*da.y;
}
else if (t<=2.0)
{
t=a2+((t-1.0)*dang);
x=P1.x+r*cos(t);
y=P1.y+r*sin(t);
}
else
{
t=t-2.0;
x=P3.x+t*db.x;
y=P3.y+t*db.y;
}
I have 2 circles that collide in a certain collision point and under a certain collision angle which I calculate using this formula :
C1(x1,y1) C2(x2,y2)
and the angle between the line uniting their centre and the x axis is
X = arctg (|y2 - y1| / |x2 - x1|)
and what I want is to translate the circle on top under the same angle that collided with the other circle. I mean with the angle X and I don't know what translation coordinates should I give for a proper and a straight translation!
For what I think you mean, here's how to do it cleanly.
Think in vectors.
Suppose the centre of the bottom circle has coordinates (x1,y1), and the centre of the top circle has coordinates (x2,y2). Then define two vectors
support = (x1,y1)
direction = (x2,y2) - (x1,y1)
now, the line between the two centres is fully described by the parametric representation
line = support + k*direction
with k any value in (-inf,+inf). At the initial time, substituting k=1 in the equation above indeed give the coordinates of the top circle. On some later time t, the value of k will have increased, and substituting that new value of k in the equation will give the new coordinates of the centre of the top circle.
How much k increases at value t is equal to the speed of the circle, and I leave that entirely up to you :)
Doing it this way, you never need to mess around with any angles and/or coordinate transformations etc. It even works in 3D (provided you add in z-coordinates everywhere).
I'm using CML to manage the 3D math in an OpenGL-based interface project I'm making for work. I need to know the width of the viewing frustum at a given distance from the eye point, which is kept as a part of a 4x4 matrix that represents the camera. My goal is to position gui objects along the apparent edge of the viewport, but at some distance into the screen from the near clipping plane.
CML has a function to extract the planes of the frustum, giving them back in Ax + By + Cz + D = 0 form. This frustum is perpendicular to the camera, which isn't necessarily aligned with the z axis of the perspective projection.
I'd like to extract x and z coordinates so as to pin graphical elements to the sides of the screen at different distances from the camera. What is the best way to go about doing it?
Thanks!
This seems to be a duplicate of Finding side length of a cross-section of a pyramid frustum/truncated pyramid, if you already have a cross-section of known width a known distance from the apex. If you don't have that and you want to derive the answer yourself you can follow these steps.
Take two adjacent planes and find
their line of intersection L1. You
can use the steps here. Really
what you need is the direction
vector of the line.
Take two more planes, one the same
as in the previous step, and find
their line of intersection L2.
Note that all planes of the form Ax + By + Cz + D = 0 go through the origin, so you know that L1 and L2
intersect.
Draw yourself a picture of the
direction vectors for L1 and L2,
tails at the origin. These form an
angle; call it theta. Find theta
using the formula for the angle
between two vectors, e.g. here.
Draw a bisector of that angle. Draw
a perpendicular to the bisector at
the distance d you want from the
origin (this creates an isosceles
triangle, bisected into two
congruent right triangles). The
length of the perpendicular is your
desired frustum width w. Note that w is
twice the length of one of the bases
of the right triangles.
Let r be the length of the
hypotenuses of the right triangles.
Then rcos(theta/2)=d and
rsin(theta/2)=w/2, so
tan(theta/2)=(w/2)/d which implies
w=2d*tan(theta/2). Since you know d
and theta, you are done.
Note that we have found the length of one side of a cross-section of a frustrum. This will work with any perpendicular cross-section of any frustum. This can be extended to adapt it to a non-perpendicular cross-section.