16 August 2014

Calculating perspective projection

All those motivational talks and sayings about one being able to achieve what they really want, is actually true. You just have to know how. While programming my games, I had for a very long time wanted to create a game in 3D. Whenever a vehicle would move through a tunnel or narrow area, I'd observe carefully, how every point moved with respect to each other. The 3D feeling!

As I learnt trigonometry and geometry, my ability to understand dimensions got better, and I tried plotting points on paper, imagining 3D situations and using the laws of geometry to try and derive an equation which would be able to plot and erase (creating the effect of an animation) on a 2D screen, the points I observed in the tunnel.

All this was long before I had internet access or access to a good library.

The first attempt ended up in a long equation which didn't work when I wrote a program to plot points in 3D:




I guess I was just about crazy enough to derive these when in twelfth standard :-)
And then I tried again:


and again and again until I finally, I decided that it was far simpler if I didn't consider all four quadrants, and instead considered the computer screen to be a single quadrant where x and y were positive. That simplified the calculation and I also got it right!


And when used in a C program like Unipar, would look like this:
(note how the function is declared first and the datatypes are declared in the next two lines. This is legal C)

void perspective(x,y,z,x2,y2,z2,tx,ty,tz)
int x,y,z,x2,y2,z2;
float tx,ty,tz;
{
int Z=500,XC=240,YC=250;
float i,j,k;
float r,c,X,Y;
if (z>=Z) {printf("move the observation point farther");getch();return(0);}
i=x;j=-y;k=z;
rot3d(&i,&j,&k,tx,1);
rot3d(&i,&j,&k,ty,2);
rot3d(&i,&j,&k,tz,3);
if (beam.perspective==1)
{
r=(Z*j)/(Z-k)+YC;
c=(Z*i)/(Z-k)+XC;
}
else {r=j+YC;c=i+XC;}
i=x2;j=-y2;k=z2;
rot3d(&i,&j,&k,tx,1);
rot3d(&i,&j,&k,ty,2);
rot3d(&i,&j,&k,tz,3);
if (beam.perspective==1)
{
Y=(Z*j)/(Z-k)+YC;
X=(Z*i)/(Z-k)+XC;
}
else {X=i+XC;Y=j+YC;}
line(c,r,X,Y);
}/*perspective*/

/*to rotate in 3D*/
void rot3d(a,b,c,T,axis)
float *a,*b,*c,T;
int axis;
{
float x,y,z;
x=*a;y=*b;z=*c;
T=T*RADIAN;
switch(axis)
{
case 1: /*-- x axis rotn --*/
*a=x;
*b=y*cos(T)-z*sin(T);
*c=y*sin(T)+z*cos(T);
break;
case 2: /*-- y axis rotn --*/
*a=z*sin(T)+x*cos(T);
*b=y;
*c=z*cos(T)-x*sin(T);
break;
case 3: /*-- z axis rotn --*/
default:
*a=x*cos(T)-y*sin(T);
*b=x*sin(T)+y*cos(T);
*c=z;
break;
}
}/* end of 3d rotn */


Had found these derivations in one of my old notebooks, and I thought I'd put it up here for my own reference and perhaps even in case it inspires any bright kid to try some brain-work magic of their own :)

No comments: