1. Introduction 介绍
矩阵转换通常就是三种东西-旋转,缩放,平移。在3d graphics中帮你完成 99%的工作,我未见到比他更快的方法。矩阵使程序加速,也许每秒多渲染500个对象。
Transformation consists of 3 things - rotating (spinning things around), scaling (making things bigger/smaller) and translation (moving things around). With 3D graphics Direct3D will do 99% of the work for you, however, along my travels I've found a way of going faster - if you do the matrix maths calculations yourself you can go just that little bit faster (2.5x faster), we're talking the difference between 0.0001ms and 0.00005ms here, but if you're rendering 500+ objects a frame (several 1000 a second), it could be worth the extra effort. It also looks much cleverer! (if you open-source your code).
Onwards and upwards...
--------------------------------------------------------------------------------
2. 2D Transformations 两维转换
它在2D,3D游戏中都很有用,数学方法这里不多解释。This is the most useful part, using either DirectDraw (fully 2D) or Direct3D (quasi 2D) no transformations are done for you - you want to rotate, translate or scale something you're gonna have to do it yourself. The actual mathematical proof behind all of this is a little complicated, and to be honest - it really makes much difference why it works, all we want to know is how to make it work for us. Much of the pure maths is glossed over here, if you're interested in proofs or further explanations dig out your old maths books or go searching the web - much of the pure maths is too lengthy to explain here.
我们不用知道它是任何推导的,仅使用它。We're going to be using a mathematical technique known as matrix maths - the ideas behind matrices (plural of matrix) isn't too important, heck! I dont even know exactly how to use them - I just learnt what I need to know about them. Matrix maths allows us to do the transformations relatively quickly, and relatively simply as well - just what I like.
基本的结构向这样[X,Y] --> [M] --> [X',Y']一个x,y坐标进去经过变化出来一个新坐标,可能是缩放、旋转也可能是平移。The basic structure is like this [X,Y] ---} [M] ---} [X',Y'] (one set of coordinates goes in, something happens, and a new set of coordinates come out). [M] is the transformation matrix, it may scale the coordinates, rotate them, translate them or some combination of the 3. The best part is that you can combine all 3 transformations into one matrix (more on that later).
Translation 平移
2D平移需要3x3的矩阵。This is probably the easiest of the 3, and thus we'll start here. A 2D translation matrix for what we want is a 3x3 grid of numbers (known as a 3x3 matrix). for a translation it looks like this:
平移一个点要将这个点与上面矩阵相乘,下图为计算过程和结果。a bit weird really, isn't it. in order to translate a point by this matrix we must multiply the point by the matrix. Now this isn't as simple as it sounds, it's not quite like normal multiplication, and it's an awful lot more complicated. if we refer to all of the elements in row-column notation (ie, yx) and the rows are i and columns j, to get the FINAL value for element we multiply each element in the row i (of the source matrix) with each element in the column j (of the destination matrix), we then add all of these values together and that is our final value. scared? look at this following example and see if you can understand what happened:
it aint too scary really, is it? the final results aren't particularly amazing - I'm pretty sure that without all this extra work you could of told me how to translate the original coordinates (the x' = x + dx part)... It'll come into it's own a little later on when we're combining rotation, scaling and translation into one big equation.
Rotation 旋转
旋转在游戏中使用非常频繁下图是旋转使用的矩阵,X代表角度。Rotation is the big one - this is what everyone likes emailing me about. Rotating your sprite around is a very useful trick in games - and is often quite heavily used; therefore it obviously helps being able to do it! The following diagram shows you what the 2D rotation matrix should look like:
我们已经推导了平移矩阵,借鉴推导出旋转矩阵,下面是将点(x,y)旋转置(x',y')A little more scary this time - trig functions. The presence of these trig functions will bring the processing time for a rotation transformation up considerably - trig functions are slow. If you can optimise away any of the trig functions then do so - the only realistic optimisation to be done here is to pre-process CosX and SinX, as that will 1/2 the number of calls to Cos( ) or Sin( ). More on this later (when we do a bit of code).
Again, not too simple if you can see your way through the matrix multiplication algorithm. Be careful to differentiate between the X and the x, the X is the angle (usually denoted as theta, q), and x is the coordinate.
Scaling 缩放
This isn't used as often, but it's pretty simple - so I may as well explain it, and we can incorporate it into the big matrix later on. You may have noticed that all of the matrices so far have had the r=c values equal to 1 (unless replaced by another value), this is because the r=c values are the scaling values - in previous matrices they are set to 1 so that they dont intefere with the resultant values. a matrix where all the values are 0 except the r=c values which are 1 is called the "identity matrix", which is a useful type of matrix, but not really relevant here.
Not at all complicated, and neither is the resulting equations - you can probably guess them now!
Tell me you could see that coming? it's pretty obvious...
Combining Transformations
Up till this point, the use of matrices has been a little pointless - the derived equations are enough to rotate, translate and scale. you could quite easily apply each transformation individually like this:
x' = x + dx
y' = y + dy
x'' = x'Cosq - y'Sinq
y'' = x'Sinq + y'Cosq
x''' = x'' * sx
y''' = y'' * sy
the above code would translate the point, rotate it around the origin and then scale it by a given factor - not necessarily what you want (rotation before translation is more common), but you can juggle the lines around. BUT, using matrices we can combine all 3 transformations together, then split them out into two lines - generating x',y' from x,y instead of going all the way to x''',y''' - is that not better?
我们现在要制造一个平移-旋转-缩放一起的复合矩阵。注意先旋转再平移与先平移再旋转是不同的。她能更适合你的程序。The way we do this is by creating a master matrix - we multiply (using matrix multiplication) the translation, rotation and scaling matrices together into one matrix, then multiply the point x,y by this master matrix. A prior note on multiplication order - as with the 6 equations just listed it matter what order they go in, rotation-translation is very different from translation-rotation (one will create an orbiting body, the other will create a spinning object). Normally you would scale the points, rotate them and then translate them - but you'll need to decide which is best for your application. here is the complete proof for the "master matrix":
[M] is the Master Matrix '三种复合的矩阵
[S] is the scaling Matrix '缩放矩阵
[R] is the rotation Matrix '旋转矩阵
[T] is the translation Matrix '平移矩阵
[M] = [S][R][T]
= ( [S]*[R] ) * [T]
[M] = [SR][T]
there we have the final "Master Matrix". How amazing, if we now multiply a point [x,y,1] by this matrix we should be left with an equation to transform x and an equation to transform y - which will result in a rotation, translation and scaling. Arguably, through substitution, you could of combined the original 3 equations, but this way is open to much more powerful calculations - there are quite a few other types of transformation that can be done using matrices, and a few shortcuts can be found along the way as well. The following illustration indicates how the final all-in-one formula works:
方程最后转换了一个点——旋转q角度—— 缩放(sx,sy)—— 平移(dx,dy)。验证方程的正确性如下。So the final equation to transform the point (x,y) - rotating through q radians, scaling by (sx,sy) and translating by (dx,dy) units - is shown above. we can prove that this combined equation is actually correct by substituting in the previous set of equations - shown below.
Scale:
x' = x * sx
y' = y * sy
Rotate:
x'' = x'Cosq - y'Sinq
y'' = x'Sinq + y'Cosq
Translate:
x''' = x'' + dx
y''' = y'' + dy
Combined:
x' = ((x * sx)Cosq - (y * sy)Sinq) + dx
y' = ((x * sx)Sinq + (y * sy)Cosq) + dy
Simplified:
x' = xSxCosq - ySySinq + dx
y' = xSxSinq + ySyCosq + dy
通过这种方法直接得到了答案,我写了一个与方程等价的简单程序。and there, as if by magic - we've gotten the same formula back. The method you choose - by straight algebra or by matrix algebra - is your choice entirely; I personally prefer the matrix method as it allows for many 1000's of combinations; for example - scale, rotate, translate, rotate - will (given a flying saucer object) spin it around it's center and then spin it around the origin (like orbiting a planet) - you try doing that with those plain equations, it's still possible - but a little more complicated methinks. To finish things off, I've written a simple transformation function incorporating the overall equation.
Private Function Transform2DPoint(tX As Single, tY As Single, sX As Single, sY As Single, Theta As Single, SrcPt As Pt) As Pt
'(tX,tY) describes the translation
'(sX,sY) describes the scale
'Theta describes the rotation
'SrcPt is the point to be transformed
'[RETURN] is the transformed point
' The general formulas:
' X' = xSxCosq - ySySinq + dx
' Y' = xSxSinq + ySyCosq + dy
Dim Cosq As Single
Dim Sinq As Single
Cosq = Cos(Theta)
Sinq = Sin(Theta)
Transform2DPoint.X = (SrcPt.X * sX * Cosq) - (SrcPt.Y * sY * Sinq) + tX
Transform2DPoint.Y = (SrcPt.X * sX * Sinq) + (SrcPt.Y * sY * Cosq) + tY
End Function
--------------------------------------------------------------------------------
3. 3D Transformations 三维转换
我已经说了本章90%的内容了。I'm not going to say much on this topic - 90% of what was in the previous section is still relevant in this section. More importantly, however, is that Direct3D (or any other 3D API) will do matrix transformations for you - only the specialist/elite will need to play around with the transformation matrices manually.
根据你变换的需要推导出3D矩阵是非常重要的。硬件对矩阵会加速运算(1000倍于一些软件运算)。The advantage of having D3D do the actual transformation is that all you need to do is present the overall matrix and it'll work out what needs to happen - and in some cases the hardware will actually do the mathematics on the geometry (which is going to be 10000x faster than any software implementation). As visitors to the VoodooVB message board will be aware, I actually worked this out a while back and posted a generalised matrix formula for a 3D transformation. During my tests on this it gave exactly the same results as using the built in D3DX functions, yet was 1.6-2.6x faster than them. The only trade off is actually working out the generalised matrix in the first place - this usually only ever has to be done once.
2D矩阵与3D矩阵不同仅在于大小(4X4取代3X3)及2D一个旋转轴,3D三个旋转轴。The only two differences between the 2D matrices and the 3D matrices is their size (now 4x4 instead of 3x3) and there are 3 rotation matrices (x,y,z) - 2D rotation (on a plane) only requires you to have 1 rotation axis, 3D has 3 main rotation axis's...
5个组合矩阵如下The 5 matrices are shown below - this is for reference, given the information about 2D transformations you should quite easily be able to do some clever things with them...
--
-
这是通用的变换矩阵Finally, As I already mentioned, I calculated the generalised matrix for this a while back - and it's shown below.
这是与变换矩阵相同的代码Pretty isn't it! Here's the same transformation matrix but in code:
Private Function CreateMatrix(Rx As Single, Ry As Single, Rz As Single, Sx As Single, _
Sy As Single, Sz As Single, Tx As Single, Ty As Single, Tz As Single) As D3DMATRIX
Dim CosRx As Single, CosRy As Single, CosRz As Single
Dim SinRx As Single, SinRy As Single, SinRz As Single
CosRx = Cos(Rx) 'Used 6x
CosRy = Cos(Ry) 'Used 4x
CosRz = Cos(Rz) 'Used 4x
SinRx = Sin(Rx) 'Used 5x
SinRy = Sin(Ry) 'Used 5x
SinRz = Sin(Rz) 'Used 5x
'total of 29 trig functions
'23 trig functions cancelled out by
'this optimisation; hence the 2.6x speed increase.
With CreateMatrix
.m11 = (Sx * CosRy * CosRz)
.m12 = (Sx * CosRy * SinRz)
.m13 = -(Sx * SinRy)
.m21 = -(Sy * CosRx * SinRz) + (Sy * SinRx * SinRy * CosRz)
.m22 = (Sy * CosRx * CosRz) + (Sy * SinRx * SinRy * SinRz)
.m23 = (Sy * SinRx * CosRy)
.m31 = (Sz * SinRx * SinRz) + (Sz * CosRx * SinRy * CosRz)
.m32 = -(Sz * SinRx * CosRx) + (Sz * CosRx * SinRy * SinRz)
.m33 = (Sz * CosRx * CosRy)
.m41 = Tx
.m42 = Ty
.m43 = Tz
.m44 = 1#
End With
End Function
关于逆矩阵的定义:
V*M=V'
V=V'*M’
如果满足上面两个方程那么M'就是M的逆矩阵,从它的定义可看到逆矩阵可以使坐标向相反的方向变换,比如顶点由一矩阵旋转,再经其逆矩阵就合产生反转,函数原型:
Function D3DXMatrixInverse(MOut As D3DMATRIX, Determinant As Single, M As D3DMATRIX) As Long
'如何求一个矩阵(3*3)的逆阵 --作者kaiaili
'这是一个求矩阵的逆阵的简例
' 但由于吾业不精,不知道如何利用二维数组(矩阵)作为函数的参数,并利用函数返回一个二维数组(逆阵)
'无法将它写成函数,只好写成一个过程来完成.
' 同时吾对递归也无研究,无法使它支持矩阵4*4,5*5...的逆阵转换.
Sub main()
Dim ci As Byte, cj As Byte, i As Byte, j As Byte, n As Byte '循环,统计变量
Dim a(2, 2) As Single '矩阵数组
Dim b(2, 2) As Single '逆阵数组
Dim t(3) As Single '余子式数组
Dim rValue As Single '矩阵值变量
'初始矩阵
a(0, 0) = 1: a(0, 1) = -1: a(0, 2) = 3
a(1, 0) = 2: a(1, 1) = -1: a(1, 2) = 4
a(2, 0) = -1: a(2, 1) = 2: a(2, 2) = -4
Debug.Print "原始矩阵为: "
For i = 0 To 2
For j = 0 To 2
Debug.Print a(i, j),
Next j
Debug.Print
Next i
'求伴随阵
For ci = 0 To 2
For cj = 0 To 2
'求余子式
n = 0
For i = 0 To 2
For j = 0 To 2
If i <> ci And j <> cj Then t(n) = a(i, j): n = n + 1
Next j
Next i
b(cj, ci) = ((-1) ^ (ci + cj)) * (t(0) * t(3) - t(1) * t(2))
Next cj
Next ci
'求矩阵的值
rValue = 0
For i = 0 To 2
rValue = rValue + a(i, 0) * b(0, i)
Next i
If rValue Then '逆阵存在
'求矩阵的逆阵
Debug.Print "逆阵为: "
rValue = 1 / rValue
For i = 0 To 2
For j = 0 To 2
b(i, j) = b(i, j) * rValue
Debug.Print b(i, j),
Next j
Debug.Print
Next i
Else '逆阵不存在
Debug.Print "逆阵不存在!"
End If
End Sub