SVG-VML-3D-MATH - EASY EXPLAINED
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
xpixel=xcenter+xs and ypixel=ycenter-ys.
The zs can be used to order the objects before drawing, so you would start the drawing with the
object which has the smallest zs.
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 (xs, 0, zs),
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 zs,
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