要把游戏模型显示到屏幕需要经历五个空间。

指模型对象所在的坐标空间,即建模软件的空间。
一般在导出创建的模型时,将模型的的坐标设为(0,0,0),目的是为了保持和世界坐标重合,方便调整其在世界坐标中的位置。
游戏所在的场景就是世界空间。
将模型放到世界空间之后,可以对模型对象进行平移,缩放,旋转等操作,本质上就是通过矩阵的转换算法实现的。(模型举证)
通过虚拟摄像机观察世界空间。
在游戏中,可以操作视角看到周围的物体。本质就是操作虚拟摄像机,将视线内的物体转换到摄像机的坐标系中。
观察空间也就是摄像机所观察的空间,是将物体对象的世界空间坐标转换为观察视觉内的坐标。
观察空间的计算需要使用观察矩阵
不能将所有的物体都移动到观察空间中,没有在视线范围内的物体需要被裁剪掉。
平截头体:由投影矩阵创建的观察区域。出现在其区域内的物体都会出现在屏幕中。
将场景中的物体从观察空间转换到裁剪空间,需要定义投影矩阵。
将一定范围内的坐标转化到标准设备坐标系的过程叫投影。
两种投影矩阵:
正交投影矩阵定义了一个类似立方体的平截头体。在立方体外的物体都会被裁剪掉。
在创建一个正交投影矩阵的时候,需要指定平截头体的宽度、高度和长度的远近。

摄像机的可视坐标系由这个平截头体的宽、高、深来决定。
由此可以看到,正交投影的矩阵由宽和高来决定,和深度无关。即不管远近,所显示的部分和近裁剪面是一致的。
作用:由于正交投影没有远近之分,一般作用于UI的摄像机上。
bool Camera::initOrthographic(float zoomX, float zoomY, float nearPlane, float farPlane)
{
_zoom[0] = zoomX;
_zoom[1] = zoomY;
_nearPlane = nearPlane;
_farPlane = farPlane;
Mat4::createOrthographicOffCenter(0, _zoom[0], 0, _zoom[1], _nearPlane, _farPlane, &_projection);
_viewProjectionDirty = true;
_frustumDirty = true;
_type = Type::ORTHOGRAPHIC;
return true;
}
zoomX : 平截头体的宽
zoomY : 平截头体的高
nearPlane :近裁剪面
farPlane : 远裁剪面
然后调用函数createOrthographicOffCenter
void Mat4::createOrthographicOffCenter(float left, float right, float bottom, float top,
float zNearPlane, float zFarPlane, Mat4* dst)
{
GP_ASSERT(dst);
GP_ASSERT(right != left);
GP_ASSERT(top != bottom);
GP_ASSERT(zFarPlane != zNearPlane);
memset(dst, 0, MATRIX_SIZE);
dst->m[0] = 2 / (right - left);
dst->m[5] = 2 / (top - bottom);
dst->m[10] = 2 / (zNearPlane - zFarPlane);
dst->m[12] = (left + right) / (left - right);
dst->m[13] = (top + bottom) / (bottom - top);
dst->m[14] = (zNearPlane + zFarPlane) / (zNearPlane - zFarPlane);
dst->m[15] = 1;
}
正交投影的平截头体为一个长方体[left,right][bottom,top][far,near]

那么需要把这个长方体转换到[-1,1][-1,1][-1,1]中
那么就需要两步。
算出上面的长方体的中心点。
``[(left+right) / 2,(bottom+top) / 2,(far+near) / 2] ``
那么将这个点移动到原点。
平移公式为:
∣
1
0
0
T
x
0
1
0
T
y
0
0
1
T
z
0
0
0
1
∣
⋅
{
x
y
z
1
}
=
{
x
1
y
1
z
1
1
}
\left| 100Tx010Ty001Tz0001
其中 x1,y1,z1为新移动的点。
那么就可以得到,平移矩阵为
T
−
1
(
t
)
=
T
(
−
t
)
=
∣
1
0
0
−
T
x
0
1
0
−
T
y
0
0
1
−
T
z
0
0
0
1
∣
T^{-1}(t) = T(-t)= \left| 100−Tx010−Ty001−Tz0001
所以,该长方体的平移矩阵为:
M
t
r
a
n
s
l
a
t
e
=
∣
1
0
0
−
l
e
f
t
+
r
i
g
h
t
2
0
1
0
−
b
o
t
t
o
m
+
t
o
p
2
0
0
1
−
f
a
r
+
n
e
a
r
2
0
0
0
1
∣
M_{translate} = \left| 100−left+right2010−bottom+top2001−far+near20001
设缩放因子为s,则沿着坐标轴的缩放变换矩阵为:
S
(
s
)
=
S
(
s
x
,
s
y
,
s
z
)
=
∣
s
x
0
0
0
0
s
y
0
0
0
0
s
z
0
0
0
0
1
∣
S(s) = S(s_x,s_y,s_z)= \left| sx0000sy0000sz00001
缩放矩阵的逆为:
S
−
1
(
s
)
=
S
(
1
s
x
,
1
s
y
,
1
s
z
)
=
∣
1
s
x
0
0
0
0
1
s
y
0
0
0
0
1
s
z
0
0
0
0
1
∣
S^{-1}(s) = S(\frac{1}{s_x},\frac{1}{s_y},\frac{1}{s_z})= \left| 1sx00001sy00001sz00001
要将 [ l e f t , r i g h t ] [left,right] [left,right]缩放到 [ − 1 , 1 ] [-1,1] [−1,1],那么它的缩放因子就为 2 r i g h t − l e f t \frac{2}{right-left} right−left2。
所以,该长方体的缩放变换矩阵为:
M
s
c
a
l
e
=
∣
2
r
i
g
h
t
−
l
e
f
t
0
0
0
0
2
t
o
p
−
b
o
t
t
o
m
0
0
0
0
2
n
e
a
r
−
f
a
r
0
0
0
0
1
∣
M_{scale} = \left| 2right−left00002top−bottom00002near−far00001
最后的正交投影矩阵就为:
M o r t h o = M s c a l e ∗ M t r a n s l a t e Mortho = M_{scale} * M_{translate} Mortho=Mscale∗Mtranslate
即:
M
O
r
t
h
o
g
r
a
p
h
i
c
=
∣
2
r
i
g
h
t
−
l
e
f
t
0
0
0
0
2
t
o
p
−
b
o
t
t
o
m
0
0
0
0
2
n
e
a
r
−
f
a
r
0
0
0
0
1
∣
∗
∣
1
0
0
−
l
e
f
t
+
r
i
g
h
t
2
0
1
0
−
b
o
t
t
o
m
+
t
o
p
2
0
0
1
−
f
a
r
+
n
e
a
r
2
0
0
0
1
∣
M_{Orthographic} = \left| 2right−left00002top−bottom00002near−far00001
起作用就是将正交投影的物体变换成标准视体。
这里保持一个疑惑 :cocos2dx的代码中,矩阵最后的数不是算出来的。由于知识有限,不明白为啥那么写。即:
dst->m[12] = (left + right) / (left - right);
dst->m[13] = (top + bottom) / (bottom - top);
dst->m[14] = (zNearPlane + zFarPlane) / (zNearPlane - zFarPlane);
所以正交投影一般用在UI上。
它的特点是近大远小。这跟我们的眼睛是一样的。

Near Plane : 近裁剪面
Far Plane :远裁剪面
FOV :视口的夹角
因为有远近之分,所以一般运用于3D场景中。
bool Camera::initPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
{
_fieldOfView = fieldOfView;
_aspectRatio = aspectRatio;
_nearPlane = nearPlane;
_farPlane = farPlane;
Mat4::createPerspective(_fieldOfView, _aspectRatio, _nearPlane, _farPlane, &_projection);
_viewProjectionDirty = true;
_frustumDirty = true;
_type = Type::PERSPECTIVE;
return true;
}
fieldOfView : 即FOV,视野的夹角
aspectRatio :屏幕视口的宽高比
nearPlane : 近裁剪面
farPlane :远裁剪面
创建透视投影矩阵的函数:
void Mat4::createPerspective(float fieldOfView, float aspectRatio,
float zNearPlane, float zFarPlane, Mat4* dst)
{
GP_ASSERT(dst);
GP_ASSERT(zFarPlane != zNearPlane);
float f_n = 1.0f / (zFarPlane - zNearPlane);
float theta = MATH_DEG_TO_RAD(fieldOfView) * 0.5f;
if (std::abs(std::fmod(theta, MATH_PIOVER2)) < MATH_EPSILON)
{
CCLOGERROR("Invalid field of view value (%f) causes attempted calculation tan(%f), which is undefined.", fieldOfView, theta);
return;
}
float divisor = std::tan(theta);
GP_ASSERT(divisor);
float factor = 1.0f / divisor;
memset(dst, 0, MATRIX_SIZE);
GP_ASSERT(aspectRatio);
dst->m[0] = (1.0f / aspectRatio) * factor;
dst->m[5] = factor;
dst->m[10] = (-(zFarPlane + zNearPlane)) * f_n;
dst->m[11] = -1.0f;
dst->m[14] = -2.0f * zFarPlane * zNearPlane * f_n;
}
需要把视锥体压缩成长方体,有三个原则:

M p r e s s M_{press} Mpress的推算方法,就是要找到一个使得下面的成立。
∣
x
y
z
1
∣
=
>
∣
n
x
/
z
n
y
/
z
u
n
k
n
o
w
n
1
∣
\left| xyz1