原文地址:http://www.xuanyusong.com/archives/1353
最近工作比较忙,好长时间没更新了,今天得闲,再来一篇。
上一章讲了坐标变换的相关知识,包括图形的平移、旋转与缩放,这一章,我将结合具体项目来讲解一下,坐标变换在实际开发中的应用。
我们拿太阳系为模型,主要实现太阳自转、地球自转、地球公转、月球自转、月球公转效果。由于现在还没有说到模型的绘制,我们现在暂时用正方体来代表三个星球。
先来看一下,在Direct3D中是如何生成平移、旋转、缩放矩阵的。
1、生成平移矩阵:
1
2
|
D3DXMATRIX
*
WINAPI
D3DXMatrixTranslation
(
D3DXMATRIX
*
pOut
,
FLOAT
x
,
FLOAT
y
,
FLOAT
z
)
;
|
pOUt是最终生成的平移矩阵指针,x、y、z分别表示各方向上的移动量。
2、生成旋转矩阵:
1
2
3
4
5
6
|
D3DXMATRIX
*
WINAPI
D3DXMatrixRotationX
(
D3DXMATRIX
*
pOut
,
FLOAT
Angle
)
;
D3DXMATRIX
*
WINAPI
D3DXMatrixRotationY
(
D3DXMATRIX
*
pOut
,
FLOAT
Angle
)
;
D3DXMATRIX
*
WINAPI
D3DXMatrixRotationZ
(
D3DXMATRIX
*
pOut
,
FLOAT
Angle
)
;
|
这三个函数分别生成绕x、y、z轴旋转的旋转矩阵。其中pOut是生成的旋转矩阵指针,Angle为旋转的角度。
3、生成缩放矩阵:
1
2
|
D3DXMATRIX
*
WINAPI
D3DXMatrixScaling
(
D3DXMATRIX
*
pOut
,
FLOAT
sx
,
FLOAT
sy
,
FLOAT
sz
)
;
|
pOut为生成的缩放矩阵指针,sx、sy、sz分别为在三个坐标轴上的缩放系数。同设置不同的缩放系数可以实现一些特殊效果。
将要实现的简单太阳系就是通过这一系列的有序组合实现的。我们分别为太阳、地球、月球进行设置。
设置太阳:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
void
SetSunMatrix
(
)
{
//世界变换
D3DXMATRIX
matWorld
;
D3DXMatrixIdentity
(
&
matWorld
)
;
//自转
D3DXMatrixRotationY
(
&
matWorld
,
(
timeGetTime
(
)
%
50000
)
*
(
2.0f
*
D3DX_PI
)
/
50000.0f
)
;
g_pDevice
->
SetTransform
(
D3DTS_WORLD
,
&
matWorld
)
;
//观察变换
D3DXVECTOR3
eyePos
(
0.0f
,
10.0f
,
-
20.0f
)
;
D3DXVECTOR3
lookatPos
(
0.0f
,
0.0f
,
1.0f
)
;
D3DXVECTOR3
updir
(
0.0f
,
1.0f
,
0.0f
)
;
D3DXMATRIX
matView
;
D3DXMatrixLookAtLH
(
&
matView
,
&
eyePos
,
&
lookatPos
,
&
updir
)
;
g_pDevice
->
SetTransform
(
D3DTS_VIEW
,
&
matView
)
;
//投影变换
D3DXMATRIX
matProj
;
D3DXMatrixPerspectiveFovLH
(
&
matProj
,
D3DX_PI
/
4
,
1.0f
,
1.0f
,
100.0f
)
;
g_pDevice
->
SetTransform
(
D3DTS_PROJECTION
,
&
matProj
)
;
}
|
在此方法中首先进行了世界变换,也就是我们的太阳自转操作,然后是观察、投影的变换。在声明一个矩阵后,调用函数D3DXMatrixIdentity在将矩阵转换为单位矩阵(矩阵左上角到右下角这条对角线上的值为1,其他值为0的矩阵)以防止意外操作产生的不利影响。在世界变换中实现太阳的自转此处设置y轴为太阳中心轴,角速度由系统时间得出。设置观察变换,主要需要三个向量:眼睛的位置、所观察的位置、眼睛摆放向上方向。
1
2
3
|
D3DXMATRIX
*
WINAPI
D3DXMatrixLookAtLH
(
D3DXMATRIX
*
pOut
,
CONST
D3DXVECTOR3
*
pEye
,
CONST
D3DXVECTOR3
*
pAt
,
CONST
D3DXVECTOR3
*
pUp
)
;
|
此方法生成观察变换矩阵(此处为左手坐标系),pOut为生成的观察矩阵指针,pEye为眼睛的摆放位置指针,pAt为观察的点的指针,pUp为眼睛摆放的向上方向指针。
1
2
|
D3DXMATRIX
*
WINAPI
D3DXMatrixPerspectiveFovLH
(
D3DXMATRIX
*
pOut
,
FLOAT
fovy
,
FLOAT
Aspect
,
FLOAT
zn
,
FLOAT
zf
)
;
|
此方法生成投影变换矩阵(此处为左手坐标系),pOut为生成的投影变换矩阵指针,fovy为在y轴方向看到的最大范围(弧度),Aspect为视区宽度与高度的比例,zn为近裁剪面的z值,zf为远裁剪面的z值,这样就形成一个近小远大的台体,我们所看到的一切就都在这个台体中。
设置地球:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
void
SetEarthMatrix
(
)
{
D3DXMATRIXA16
matWorld
;
D3DXMATRIX
matTran
;
D3DXMATRIX
matRot0
;
D3DXMATRIX
matRot1
;
D3DXMATRIX
matScal
;
D3DXMatrixIdentity
(
&
matWorld
)
;
D3DXMatrixIdentity
(
&
matTran
)
;
D3DXMatrixIdentity
(
&
matRot0
)
;
D3DXMatrixIdentity
(
&
matRot1
)
;
D3DXMatrixIdentity
(
&
matScal
)
;
//缩放
D3DXMatrixScaling
(
&
matScal
,
0.5f
,
0.5f
,
0.5f
)
;
//自转
D3DXMatrixRotationY
(
&
matRot0
,
2
*
D3DX_PI
*
(
timeGetTime
(
)
%
2000
)
/
2000
)
;
//平移到轨道位置
D3DXMatrixTranslation
(
&
matTran
,
5
,
0
,
0
)
;
//在轨道上绕太阳公转
D3DXMatrixRotationY
(
&
matRot1
,
2
*
D3DX_PI
*
(
timeGetTime
(
)
%
10000
)
/
10000
)
;
matWorld
=
matScal
*
matRot0
*
matTran
*
matRot1
;
g_pDevice
->
SetTransform
(
D3DTS_WORLD
,
&
matWorld
)
;
}
|
设置月球:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
void
SetMoonMatrix
(
)
{
D3DXMATRIXA16
matWorld
;
D3DXMATRIX
matTran0
;
D3DXMATRIX
matTran1
;
D3DXMATRIX
matRot0
;
D3DXMATRIX
matRot1
;
D3DXMATRIX
matRot2
;
D3DXMATRIX
matScal
;
D3DXMatrixIdentity
(
&
matWorld
)
;
D3DXMatrixIdentity
(
&
matTran0
)
;
D3DXMatrixIdentity
(
&
matTran1
)
;
D3DXMatrixIdentity
(
&
matScal
)
;
D3DXMatrixIdentity
(
&
matRot0
)
;
D3DXMatrixIdentity
(
&
matRot1
)
;
D3DXMatrixIdentity
(
&
matRot2
)
;
//缩放
D3DXMatrixScaling
(
&
matScal
,
0.125f
,
0.125f
,
0.125f
)
;
//自转
D3DXMatrixRotationY
(
&
matRot0
,
2
*
D3DX_PI
*
(
timeGetTime
(
)
%
1000
)
/
1000
)
;
//相对地球,平移到绕地球轨道
D3DXMatrixTranslation
(
&
matTran0
,
2
,
0
,
0
)
;
//绕地球公转
D3DXMatrixRotationY
(
&
matRot1
,
2
*
D3DX_PI
*
(
timeGetTime
(
)
%
1000
)
/
1000
)
;
//相对太阳平移
D3DXMatrixTranslation
(
&
matTran1
,
5
,
0
,
0
)
;
//绕太阳公转
D3DXMatrixRotationY
(
&
matRot2
,
2
*
D3DX_PI
*
(
timeGetTime
(
)
%
10000
)
/
10000
)
;
matWorld
=
matScal
*
matRot0
*
matTran0
*
matRot1
*
matTran1
*
matRot2
;
g_pDevice
->
SetTransform
(
D3DTS_WORLD
,
&
matWorld
)
;
}
|
对地球和月球的设置,主要注意各种矩阵变换的顺序,在这里,矩阵变换的组合操作由矩阵相乘得到,操作的顺序由左向右,需要清楚的一点事,矩阵相乘不支持交换律。当然也可以使用函数D3DXMatrixMultiply数做乘法运算,原型如下:
1
2
|
D3DXMATRIX
*
WINAPI
D3DXMatrixMultiply
(
D3DXMATRIX
*
pOut
,
CONST
D3DXMATRIX
*
pM1
,
CONST
D3DXMATRIX
*
pM2
)
;
|
pOut为得到的矩阵指针,pM1、pM2为待处理矩阵,两者按顺序相乘(本人比较喜欢使用a*b的形式,书写起来更方便一些)。
然后说一下视区变换,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
void
SetViewPort
(
)
{
RECT
rect
;
GetClientRect
(
g_hwnd
,
&
rect
)
;
D3DVIEWPORT9
vp
;
ZeroMemory
(
&
vp
,
sizeof
(
vp
)
)
;
vp
.
X
=
0
;
vp
.
Y
=
0
;
vp
.
Width
=
rect
.
right
;
vp
.
Height
=
rect
.
bottom
;
vp
.
MinZ
=
0.0f
;
vp
.
MaxZ
=
1.0f
;
g_pDevice
->
SetViewport
(
&
vp
)
;
}
|
视区变换通过函数SetViewport实现,它只有一个参数,就是一个D3DVIEWPORT9结构体的指针,D3DVIEWPORT9中的属性含义:X为视区左上角x坐标,Y为视区左上角y坐标,Width为视区的宽度,Height为视区的高度,MinZ为视区内物体的最小深度值,MaxZ为视区内物体的最大深度值。
在绘制图形的时候,要先执行变换操作,再进行绘制。
运行程序,我们将看到如图效果:
好,到这里,简单的太阳系就做好了。
本文仅供参考,如有不足,还望赐教,大家共同学习进步。
ZXGoto祝大家编程愉快