世界坐标系到摄影坐标系的变换
摄影坐标系的原点为世界坐标系中的观察位置eye点,z轴与观察方向(从eye点出发到at点的向量)一致。
由于仅由一个坐标原点的位置和一个z坐标轴的方向不足以完全确定一个坐标系,因此,还需要指定一个up向量来定位摄影坐标系的y轴方向。这个在世界坐标系中给出的up向量是不必与观察方向垂直的,只是指明摄影坐标系应该以向上的方向还是以向下的方向作为y轴。
Builds a left-handed, look-at matrix.
D3DXMATRIX * D3DXMatrixLookAtLH(
D3DXMATRIX * pOut,
CONST D3DXVECTOR3 * pEye,
CONST D3DXVECTOR3 * pAt,
CONST D3DXVECTOR3 * pUp
);
Parameters
- pOut
- [in, out] Pointer to the D3DXMATRIX structure that is the result of the operation.
- pEye
- [in] Pointer to the D3DXVECTOR3 structure that defines the eye point. This value is used in translation.
- pAt
- [in] Pointer to the D3DXVECTOR3 structure that defines the camera look-at target.
- pUp
- [in] Pointer to the D3DXVECTOR3 structure that defines the current world's up, usually [0, 1, 0].
Return Values
Pointer to a D3DXMATRIX structure that is a left-handed, look-at matrix.
Remarks
The return value for this function is the same value returned in the pOut parameter. In this way, the D3DXMatrixLookAtLH function can be used as a parameter for another function.
This function uses the following formula to compute the returned matrix.
zaxis = normal(At - Eye)
xaxis = normal(cross(Up, zaxis))
yaxis = cross(zaxis, xaxis)
xaxis.x yaxis.x zaxis.x 0
xaxis.y yaxis.y zaxis.y 0
xaxis.z yaxis.z zaxis.z 0
-dot(xaxis, eye) -dot(yaxis, eye) -dot(zaxis, eye) 1
要正确运行以下的示例程序,需要在工程中包含d3dx9.lib,或者在main函数前加入
#pragma comment(lib, "d3dx9.lib")
以指示编译器链接d3dx9.lib。
代码示例:
#include <d3dx9math.h>
#include <stdio.h>
#pragma warning(disable : 4305)
int main()
{
D3DXVECTOR3 p(3.0, 3.0, 3.0); // point with world coordinate
D3DXVECTOR3 eye(1.0, 0.0, 0.0); // camera position with world coordinate
D3DXVECTOR3 at(0.0, 0.0, 0.0); // look at target with world coordinate
D3DXVECTOR3 up(0.0, 1.0, 0.0); // define the current world's up
D3DXMATRIX m; // transform matrix from world coordinate to camera coordinate
// Builds a left-handed, look-at matrix.
D3DXMatrixLookAtLH(&m, &eye, &at, &up);
// print out matrix
printf(" |%f %f %f %f|\n", m._11, m._12, m._13, m._14);
printf(" |%f %f %f %f|\n", m._21, m._22, m._23, m._24);
printf("m =|%f %f %f %f|\n", m._31, m._32, m._33, m._34);
printf(" |%f %f %f %f|\n", m._41, m._42, m._43, m._44);
printf("\n\n\n");
D3DXVec3TransformCoord(&p, &p, &m);
printf("p(%f, %f, %f)", p.x, p.y, p.z);
printf("\n\n\n");
return 0;
}
输出:
|0.000000 0.000000 -1.000000 0.000000|
|-0.000000 1.000000 0.000000 0.000000|
m = |1.000000 0.000000 0.000000 0.000000|
|-0.000000 -0.000000 1.000000 1.000000|
p(3.000000, 3.000000, -2.000000)
透视投影变换
为了显示三维物体,必须将三维物体透视投影到平面上,以获得一个二维的平面图象,同时还要保存物体的深度信息(z轴上的位置信息)。
Builds a left-handed perspective projection matrix based on a field of view.
D3DXMATRIX * D3DXMatrixPerspectiveFovLH(
D3DXMATRIX * pOut,
FLOAT fovy,
FLOAT Aspect,
FLOAT zn,
FLOAT zf
);
Parameters
- pOut
- [in, out] Pointer to the D3DXMATRIX structure that is the result of the operation.
- fovy
- [in] Field of view in the y direction, in radians.
- Aspect
- [in] Aspect ratio, defined as view space width divided by height.
- zn
- [in] Z-value of the near view-plane.
- zf
- [in] Z-value of the far view-plane.
Return Values
Pointer to a D3DXMATRIX structure that is a left-handed perspective projection matrix.
Remarks
The return value for this function is the same value returned in the pOut parameter. In this way, the D3DXMatrixPerspectiveFovLH function can be used as a parameter for another function.
This function computes the returned matrix as shown:
xScale 0 0 0
0 yScale 0 0
0 0 zf/(zf-zn) 1
0 0 -zn*zf/(zf-zn) 0
where:
yScale = cot(fovY/2)
xScale = yScale / aspect ratio
视截体的平面计算
在世界坐标系中选择观察者位置,并确定视角的方向和视线的远近距离,视截体的6个侧面也就决定下来,由于三维物体一开始是在世界坐标系中给出的,因此视截体在世界坐标系中的6个平面的计算就显得相当重要,因为可由这6个视截体的平面法向量计算出世界空间中的哪些三维物体应该显示出来。
完全可以根据观察者的坐标位置、观察视角的大小和远近平面的方位,直接计算出6个视截体平面在世界坐标系中的方程。不过,DirectX并没有直接提供一个函数来计算这些平面的方程,但可以根据摄影变换和透视投影变换矩阵,反向计算出视截体的各个平面在世界坐标系中的方程。
世界空间中的视截体在摄影变换和透视投影变换后,变成摄影空间中的立方体[-1, 1] x [-1, 1] x [0, 1],即6个视截体平面的方程分别为x=-1,x=1,y=-1,y=1,z=0和z=1。
代码示例:
#include <d3dx9math.h>
#include <stdio.h>
#pragma warning(disable : 4305)
int main()
{
// build camera reference frame
D3DXVECTOR3 eye(0.0, 0.0, 3.0); // camera position with world coordinate
D3DXVECTOR3 at(0.0, 0.0, 30.0); // look at target with world coordinate
D3DXVECTOR3 up(0.0, 1.0, 3.0); // define the current world's up
D3DXMATRIX view_matrix; // transform matrix from world coordinate to camera coordinate
// Builds a left-handed, look-at matrix.
D3DXMatrixLookAtLH(&view_matrix, &eye, &at, &up);
D3DXMATRIX proj_matrix;
// Builds a left-handed perspective projection matrix based on a field of view.
D3DXMatrixPerspectiveFovLH(&proj_matrix, D3DX_PI/3, 2.0, 2.0, 8.0);
printf("calculate frustum plane equation in world reference frame:\n\n");
D3DXMATRIX M = view_matrix * proj_matrix;
D3DXPLANE plane[6];
plane[0].a = M._11 + M._14;
plane[0].b = M._21 + M._24;
plane[0].c = M._31 + M._34;
plane[0].d = M._41 + M._44;
D3DXPlaneNormalize(&plane[0], &plane[0]);
printf("left plane equation: a=%f, b=%f, c=%f, d=%f\n\n", plane[0].a, plane[0].b, plane[0].c, plane[0].d);
plane[1].a = M._14 - M._11;
plane[1].b = M._24 - M._21;
plane[1].c = M._34 - M._31;
plane[1].d = M._44 - M._41;
D3DXPlaneNormalize(&plane[1], &plane[1]);
printf("right plane equation: a=%f, b=%f, c=%f, d=%f\n\n", plane[1].a, plane[1].b, plane[1].c, plane[1].d);
plane[2].a = M._14 - M._12;
plane[2].b = M._24 - M._22;
plane[2].c = M._34 - M._32;
plane[2].d = M._44 - M._42;
D3DXPlaneNormalize(&plane[2], &plane[2]);
printf("top plane equation: a=%f, b=%f, c=%f, d=%f\n\n", plane[2].a, plane[2].b, plane[2].c, plane[2].d);
plane[3].a = M._12 + M._14;
plane[3].b = M._22 + M._24;
plane[3].c = M._32 + M._34;
plane[3].d = M._42 + M._44;
D3DXPlaneNormalize(&plane[3], &plane[3]);
printf("bottom plane equation: a=%f, b=%f, c=%f, d=%f\n\n", plane[3].a, plane[3].b, plane[3].c, plane[3].d);
plane[4].a = M._13;
plane[4].b = M._23;
plane[4].c = M._33;
plane[4].d = M._43;
D3DXPlaneNormalize(&plane[4], &plane[4]);
printf("near plane equation: a=%f, b=%f, c=%f, d=%f\n\n", plane[4].a, plane[4].b, plane[4].c, plane[4].d);
plane[5].a = M._14 - M._13;
plane[5].b = M._24 - M._23;
plane[5].c = M._34 - M._33;
plane[5].d = M._44 - M._43;
D3DXPlaneNormalize(&plane[5], &plane[5]);
printf("far plane equation: a=%f, b=%f, c=%f, d=%f\n\n\n\n", plane[5].a, plane[5].b, plane[5].c, plane[5].d);
return 0;
}
输出:
calculate frustum plane equation in world reference frame:
left plane equation: a=0.654654, b=0.000000, c=0.755929, d=-2.267787
right plane equation: a=-0.654654, b=0.000000, c=0.755929, d=-2.267787
top plane equation: a=0.000000, b=-0.866025, c=0.500000, d=-1.500000
bottom plane equation: a=0.000000, b=0.866025, c=0.500000, d=-1.500000
near plane equation: a=0.000000, b=0.000000, c=1.000000, d=-5.000000
far plane equation: a=0.000000, b=0.000000, c=-1.000000, d=11.000000
透视投影空间到屏幕视口的变换
视截体透视投影到[-1, -1] x [-1, 1] x [0, 1]立方体后,就需要将xy平面中的二维图像变换到计算机的屏幕视口中进行显示,同时还将z轴[0, 1]变换为[min_z, max_z],这就是透视投影空间到视口的坐标变换。对于DirectX来说,通常取min_z = 0和max_z = 1,即维持z轴的坐标值不变。
在三维游戏开发中,一般使用D3DVIEWPORT9结构体和 IDirect3DDevice9::SetViewport Method设置视口的区域。