The 3D perspective field - 3D pong
by ericlin


This article is intended to solve the thread in http://www.flashkit.com/board/showthread.php?threadid=590887

Before reading this article, you should have some experience in Flash 3d scripting. This article is to discuss how to find out those constants for a specific 3d perspective field. This article is about Math. If you get headache about Math, drop this article.

What is the story ? A guy used camera to take a picture of pong board. The bitmap contains a 3d perspective board. Now he wants to put a ball bouncing onTop of this pong board - over the bitmap.

To make a 3d pong is not so difficult. The present problem is : how to "fit" our perspective field to this readonly bitmap. In other words, how do we set those constant parameters in 3d display equation to fit this bitmap.

Below is the movie. There are only several lines of script. But the underlying Math is complicated. I would explain it steps by steps about how I did it.

[movie here]

download fla


Step 1.:Create a 2D pong. with a board of 100x50;

Well, in this simple example, it is not really a pong game. There are no paddles, holes or gate. It is just a travelling ball with simple bounce off the wall. No such thing as friction, acceleration or deceleration. The movie is simple.

The size of 2d board is not limited to 100x50. We can create our game with a board of 400x200. We still can calculate out the proper constants we need by the same method. Of-course, the constants will be different.

The bounce is checked by (_x<0||_x>100), and (_y<0||_y>50); The script is very simple. I wont explain this in details. You may see the script in the fla.


Step 2: The 3d display equation:

plotX=((x+shiftX)*(zoom*focus)/(z+focus))+Xinfinity;
plotY=((y+shiftY)*(zoom*focus)/(z+focus))+Yinfinity;

plotX and  plotY are the corresponding (x,y) on the screen;

x,y,z is the 3d data.

shiftX,  shiftY are the shift caused by view angles.

(this is important but difficult to understand)

zoom is like camera zoom, the spatial relationship is preserved but Zoomed.

Xinfinity and Yinfinity are the (x,y) of infinity point, the farest point of our view. It is similar to the center of our camera.

Beside this, our project needs to calculate out the rotation of original 2d field.

The process to display plot a point from 2d model to the 3d model is:

1. pick (x,y) in our 2d model;

2. transform the 2d point (x,y) into a 3d data (x,y,z);

In this example, the (x,y) is transformed to new (x2,y2) due to rotation of the board. Then it is converted to 3d data (x2, 0 . -y2); The hieght is 0, the distance is the negative y; Please note that, positive side of z is away from us and usually is upper side. While positive _y in 2d is down side;

3. Through 3d display equation, we get (plotX, plotY) from (x,y,z). Then we plot the point onto the screen.


Step 3: Analyse and correct the bitmap.

Put the bitmap onto the fla. Lock that layer. Do not move the bitmap once you feel the position is satisfied. Otherwise, all these procedures need to be repeated to get correct constant parameters.

Now draw lines like the picture here. Mark the coordinate of those important points that we need to analyse.

If you are a drawer with experience of 3d, I guess you already know what those lines are. Two parallel lines always convert to a horizon line. 

The two "parallel"  lines BA and DC convert to the point XL , and the two "parallele" lines DA and CB convert to the point XR. These two infinity points should lie on a horizontal line - that is the horoizon - the sea surface in the far .

If the horizontal line is not "horizontal", then the camera must have been rotated. If we get a bitmap with incorrect horizon, we need to rotate the bitmap so that the horizon is horizontal.

The horozon is the Yinfinity.

We will try to calculate Xinfinity later. At present, we have two Xinfinity, left XL and right XR, each is determined by two parallel side lines. The coordinates of these two points can be calculated by coordinates of A, B, C, D by Math. A function to get the intersect point of line A->B and line C->D is available. Instead, I do it manually because this is easier and simpler for demonstration..

Here we get Yinfinity:

Yinfinity=XL._y;


Step 4: Solve the Y spatial relation

plotX is a function of (x,z), y is not related. We can solve that equation by various pair of (x,z); The plotY is a function of (y,z) and x is not related. Since y is always 0, the plotY is a function of z ; It is easier to do calculation.  Lets solve the plotY first.

plotY=((y+shiftY)*(zoom*focus)/(z+focus))+Yinfinity;

since y=0, the equation is:

plotY=(shiftY*zoom*focus)/(z+focus)+Yinfinity;
z+focus=(shiftY*zoom*focus)/(plotY-Yinfinity);

Assume M=shiftY*zoom*focus then the equation is:

z+focus=M/(plotY-Yinfinity);

To define a plane, we need 3 points. Lets take a,b,d for reference. I drew 4 vertical bar lines extending from point A, B, C, D to horizontal line. The length of those bar lines are (plotY-Yinfinity);

We can measure the length of bars. Lets do calculation.

var barA=A._y-Yinfinity;
var barB=B._y-Yinfinity;
var barD=D._y-Yinfinity;

Take the 3 3d data of a,b,d we get 3 eqation:

focus=M/barA;  // because az=0;
b.z+focus=M/barB;
d.z+focus=M/barD;

So, remove the focus, we get

bz=M*(1/barA-1/barB);
dz=M*(1/barA-1/barD); 

From what we know the position of b and d, we know the rotation angle of 2d pong is:

angle = Math.atan2(bz,2*dz); 
rotation = angle*(180/Math.PI);

bz = 50*2*Math.sin(angle);
dz = 50*Math.cos(angle);

Bring the bz back, we get 

M = bz/(1/barA-1/barB);

Bring back the M to the point A, we get 

focus = M/(barA);

From the plotY equation and length of bars, we have solved these data:

focus
rotation angle of 2d pong
y coordinate of b, d
the M which is (shiftY*zoom*focus); 

We will break the M part further. With Y relationship only, the shiftY is inversely related with zoom. It needs further data to break these two constants apart.


Step 5; The Xinfinity:and plotX equation

Now lets take the plotX function:

plotX=((x+shiftX)*(zoom*focus)/(z+focus))+Xinfinity;

We have two X infinity points , the left XL and right XR. We are going to take advantage of them.

Lets focus on the rotated 2d model of pong board. If we walk along the b->a line passing through the (0,0) point a, each point (x,y) along this line is conform to the relationship of x*tan=y; At the left extreme of infinity, the point the y is - infinity, then the x coordinate is -infinity*tan; The 3d data would be (-infinity*tan, 0, infinity). If we plot it on the 3d screen, the point should falls accurately on XL.That is what X infinity means on 3d screen.

plotX=((-infinity+shiftX)*(zoom*focus)/(infinity*tan+focus))+Xinfinity;

Because of infinity, the adding and subtraction result in infinity, and the equation is simplified into:

plotX=(-infinity)*(zoom*focus)/(infinity*tan)+Xinfinity;
plotX=(-(zoom*focus)/tan)+Xinfinity;

Lets assume zoom*focus=W, then the equatioin is

plotX= -W/tan+Xinfinity;
plotX-Xinfinity= -W/tan;

Similarly, we walk along the d->a line passing through the (0,0) a point to the far far right infinity, we will get a similar result by another tan2 and convert to XR on 3d screen; Please note that, the line of d->a is vertical to b->a, so tan2=-1/tan1; 

Now we get to solve these two equations:

tan2=-1/tan1;
XL._x -Xinfinity= -W/tan1;
XR._x-Xinfinity= -W/tan2;

After cancelling out W, we get Xinfinity:

Xinfinity=((XL._x*tan1)-(XR._x*tan2))/(tan1-tan2);

Put the Xinfinity and XL._x back to the equation, we get

W= -(XL._x-Xinfinity)*tan1;

Because W is zoom*focus and we already know focus, so here we get:

zoom=W/focus;


Step 6: The shiftY and shiftX

OK, lets check what data we have solved.

Xinfinity;
Yinfinity;
focus;
zoom;
M;

rotation of 2d pong, and the bz, dz;

Only shiftY and shiftX are pending.

We have been assume M=focus*zoom*shiftY; So,

shiftY=M/(focus*zoom);

Now the shiftX;

We know the equation: plotX=((x+shiftX)*(zoom*focus)/(z+focus))+Xinfinity; Take the plotX of (0,0), we get:

 A._x=shifX*zoom*focus/focus+Xinfinity;

That means:

shiftX = (A._x-Xinfinity)/Zoom;

Here is the problem. XL, A and B points are along a line.We extraplot the XL-B point by reference of XL-A segment, This is an extraplot of long segment by short segment. Slight inaccuracy of A point will be exagerated in extraploting B point. 

It would be better to take the farest X point to calculate the shiftX. So, we extraplot XL-A point by reference of XL-B segment. Slight inaccuracy of B point will tolerated by A point.

This step is very very important to create an accurate fit.

The pitfall is that, the A point is derived from a very simple 3d data (0,0,0). Claculation is easier. The B point is derived from b in rotated 2d pong. The 3d data is more complex.

The x of b is dz*2, and y is bz; The 3d (x,y,z) data is (dz*2, 0, -bz);

shiftX=(B._x-Xinfinity)*(focus-bz)/(Zoom*focus)-dz*2;


Step 7: Summary of the codes:

//plotX=((x+shiftX)*(zoom*focus)/(z+focus))+Xinfinity;
//plotY=((y+shiftY)*(zoom*focus)/(z+focus))+Yinfinity;

Yinfinity = XL._y;
var barA = A._y-Yinfinity;
var barB = B._y-Yinfinity;
var barD = D._y-Yinfinity;
var M = 100;
//presumed;
bz = M*(1/barA-1/barB);
dz = M*(1/barA-1/barD);
angle = Math.atan2(bz, 2*dz);
rotation = angle*(180/Math.PI);
bz = 50*2*Math.sin(angle);
dz = 50*Math.cos(angle);
M = bz/(1/barA-1/barB);
focus = M/barA;
//----------------------------
var tan1 = Math.tan(angle);
var tan2 = -1/tan1;
Xinfinity = ((XL._x*tan1)-(XR._x*tan2))/(tan1-tan2);
var W = -(XL._x-Xinfinity)*tan1;
zoom = W/focus;
shiftY = M/(focus*zoom);
shiftX = (B._x-Xinfinity)*(focus-bz)/(Zoom*focus)-dz*2;
//--------------------------------


Step 8: Implementation:

To plot a ball on 3d perspective model, besides the plotX and plotY, we need to scale the ball so that it appears larger when it is near and appears smaller when it is far.

So, the 3d eqation can be break into more practical parts.

scale=(zoom*focus)/(z+focus);
plotX=((x+shiftX)*scale)+Xinfinity;
plotY=((y+shiftY)*scale)+Yinfinity;

Finally, after we finished calculating those constants, we may just use those constants. Those scripts are not really necessary in the movie. The script in the 2d pong can be simplified by removing the _x, _y to spare the CPU out of updating the painting of the ball. . Only the x, y is enough; Anyway,  in the movie, I leave all the script in the fla for demonstration.


After note:

After finish this math article, I decide to add some explanation.

I retrieve those constants I get and plot here and add axis line.

Please note the Y axis and X axis in the 3d perspective field. That is how these two axis look like.  The Y axis is oblique, because of the shiftX. The value shiftX deviate the Y axis.

Please note how the "rotation" is shown in 3D perspective field. 

Please note the Xinfinity and Y infinity point. Some may call it the "center" point.


ericlin@ms1.hinet.net