Once upon a time, a friend and dedicated hobby-programmer asked me, how do I all this 3 dimensional drawing stuff,
and as I found out he was not the only one, who had problems getting the math in the right way. So here's a little
explanation of the underlying math of my SVG-VML-3D pages for all those, who are also fiddling around whith this topic.
Let's say you have your 3D objects in the range of the box
{ xmin, xmax, ymin, ymax, zmin, zmax }
By a first coordinate transformation shift the box, so that its center is in the origin:
xd=(xmin+xmax)/2,
yd=(ymin+ymax)/2,
zd=(zmin+zmax)/2
x'=x-xd, y'=y-yd, z'=z-zd
So you get your objects now in the range of the box
{ -x'm, x'm, -y'm, y'm, -z'm, z'm }
Now its time to introduce our 3D coordinate system:
z
|
|
|
|_______ y
/
/
/
x
Let's say you stand at (1000,0,0) and look at the origin (0,0,0) where you see all your 3D objects which are near to (0,0,0).
Now let's turn the objects around the z-axis by an angle of Φ. What will be the new position of a point (x', y' z')
after that? As it is easy to see, the z-component doesn't change, so we get z''=z'. But what about x' and y'?
When we look from the top of the coordinate system, we see this:
_______________ y
|. .
| . .
| . .
| .___/ .
| . Φ * (x'', y'')
| .
| .
| * (x', y')
|
x
It seems to be difficult to find a formula which calculates x'' and y'' from x', y' and Φ, but when we do a separate
calculation for x' and y', it get's easier, look at this:
_______________ y * (x'', y'')
|. . x''= - y' sin(Φ)
| . . y''= y' cos(Φ)
| . x'' = x' cos(Φ) |----------*---- y
| . y'' = x' sin(Φ) | (0, y')
| . |
| * (x'', y'') |
* (x', 0) |
| |
x x
Now we can add the components for x, y and z and write it down properly:
x'' = x'*cos(Φ) - y'*sin(Φ) + z'*0
y'' = x'*sin(Φ) + y'*cos(Φ) + z'*0
z'' = x'*0 + y'*0 + z'*1
It's a fine thing to do this with matrix and vector, so it will look like this:
( |
x'' |
) |
|
( |
cosΦ | -sinΦ | 0 |
) |
|
( |
x' |
) |
y'' |
= |
sinΦ | cosΦ | 0 |
* |
y' |
z'' |
|
0 | 0 | 1 |
|
z' |
OK, but how about turning all the stuff additionally around the y-axis by an angle of Θ, so it looks as if you view
from a higher position down to the 3D objects (view from a higher position down to the objects - doesn't it sound
great?). Also in this way the z-axis will still remain vertically, so this is what
you often will need for 3D-representations when the z-axis should always point to the top of the screen. So lets try to
apply the same math that we used for the rotation around the z-axis. When we look from the right side, the coordinate system
looks like this:
z z
| |
(x''', z''') | (0, z'') * (x''', z''')
* | x'''=x'' cos(Θ) | *
. | z'''=x'' sin(Θ) | . x'''= - z'' sin(Θ)
. | |. z'''= z'' cos(Θ)
x ---*-------| x ------------
(x'', 0)
Writing the stuff fine together, we get
x''' = x''*cos(Θ) - y''*0 - z''*sin(Θ)
y''' = x''*0 + y''*1 + z''*0
z''' = x''*sin(Θ) + y''*0 + z''*cos(Θ)
And in matrix notation:
( |
x''' |
) |
|
( |
cosΘ | 0 | -sinΘ |
) |
|
( |
x'' |
) |
y''' |
= |
0 | 1 | 0 |
* |
y'' |
z''' |
|
sinΘ | 0 | cosΘ |
|
z'' |
Now let's do the two rotations in one step whithout calculating the (x'', y'', z'') vector:
( |
x''' |
) |
|
( |
cosΘ | 0 | -sinΘ |
) |
|
[ |
( |
cosΦ | -sinΦ | 0 |
) |
|
( |
x' |
) |
] |
y''' |
= |
0 | 1 | 0 |
* |
sinΦ | cosΦ | 0 |
* |
y' |
z''' |
|
sinΘ | 0 | cosΘ |
|
0 | 0 | 1 |
|
z' |
You probably know from your Algebra lessons, that we can use A*(B*C)=(A*B)*C to simplify the matrix multiplication,
so we get:
( |
x''' |
) |
|
( |
cosΦcosΘ | -sinΦcosΘ | -sinΘ |
) |
|
( |
x' |
) |
y''' |
= |
sinΦ | cosΦ | 0 |
* |
y' |
z''' |
|
cosΦsinΘ | -sinΦsinΘ | cosΘ |
|
z' |
Finally, we get the position of a point in the screen coordinate system by
xs = y''' = sinΦ x' + cosΦ y'
ys = z''' = cosΦsinΘ x' - sinΦsinΘ y' + cosΘ z'
zs = x''' = cosΦcosΘ x' - sinΦcosΘ y' + sinΘ z'
The pixel count in y is from top to down, so you will get the appropriate pixel numbers by
x
pixel=x
center+x
s and y
pixel=y
center-y
s.
The z
s can be used to order the objects before drawing, so you would start the drawing with the
object which has the smallest z
s.
The current picture will not show any perspective, it looks as if you were standing very far away.
If you want to get a more realistic impression, you must change the formula just a little bit.
Assume your position is (0,0,d), so d is your distance from the origin. When you look at a point (x
s, 0, z
s),
then its picture will be (x'
s, 0, 0), as all picture points are located in the same plane, where z=0:
| (x's,0,0)
---|--------*---------- x
| .
| .
| * (xs,0,zs)
| .
| .
| .
| .
|.
o (0,0,d)
|
z
Then you get by similar triangles
(x's-xs)/zs = xs/(d-zs)
so the new screen coordinate with perspective is
x's = xs * (d /(d-zs))
y's = ys * (d /(d-zs))
Note, that now also the drawing order must be changed: Instead of sorting the objects according to their z
s,
the objects should now be sorted according to their distance to (0,0,d).
O.k. folks, that's enough for the beginning, if you want to see how I implemented this, then have a look at my
SVG-VML-3D examples.
© Lutz Tautenhahn, 2002