转换 (Direct3D 9)

将几何图形推过固定函数几何图形管道的 Direct3D 部分是转换引擎。 它会找到世界中的模型和查看器、投影显示在屏幕上的顶点,并将顶点剪切到视区上。 转换引擎还会执行照明计算来确定每个顶点的漫射和反射组件。

几何图形管道会将顶点用作输入。 转换引擎将对顶点应用世界转换、视图转换和投影转换,剪裁结果并将所有内容传递到光栅器。

在管道的前端,相对于本地坐标系声明模型的顶点。 这是本地源和方向。 这种坐标方向通常称为模型空间,各个坐标称为模型坐标。

几何图形管道的第一个阶段会将模型的顶点从其本地坐标系转换到由场景中的所有对象使用的坐标系。 重新定位顶点的过程称为世界转换。 这种新方向通常称为世界空间,世界空间中的每个顶点都是使用世界坐标声明的。

在下一个阶段,相对于相机确定描述你的 3D 世界的顶点的方向。 也就是说,应用程序为场景选择一个视点,世界空间坐标将围绕相机的视图重新定位和旋转,将世界空间转换为相机空间。 这是视图转换。

下一阶段是投影转换。 在管道的这一部分,通常根据对象与查看器的距离来缩放对象,以便为场景提供深度的错觉:关闭对象看起来比远方对象大,依此类说。 为简单起见,本文档将投影转换后顶点所在的空间称为投影空间。 在一些图形书籍中,可能会将投影空间称为后透视齐性空间。 并非所有投影转换都会按比例缩放场景中对象的大小。 此类投影有时称为仿射或正交投影。

在管道的最后一个部分中,将删除屏幕上所有不可见的顶点,这样一来,光栅器便无需花时间计算不可见项的颜色和着色。 此过程称为剪裁。 在剪裁后,剩余顶点将根据视口参数进行缩放并转换为屏幕坐标。 生成的顶点(光栅化场景时可在屏幕上看到)存在于屏幕空间中。

转换用于将对象几何图形从一个坐标空间转换到另一个坐标空间。 Direct3D 使用矩阵执行 3D 转换。 本部分介绍矩阵如何创建 3D 转换,介绍转换的一些常见用途,并详细说明如何组合矩阵以生成包含多个转换的单个矩阵。

矩阵转换

在处理 3D 图形的应用程序中,你可使用几何转换来执行以下操作:

  • 表示一个对象相对于另一个对象的位置。
  • 旋转对象并设置对象的大小。
  • 更改查看位置、方向和角度。

你可使用 4x4 矩阵将任意点 (x,y,z) 转换为另一个点 (x', y', z'),如以下等式所示。

将任意点转换为另一个点的等式

执行 (x, y, z) 与矩阵的等式以生成点 (x', y', z')。

新点的等式

最常见的转换是转换、旋转和缩放。 你可将产生这些效果的矩阵组合到一个矩阵中来一次性计算多个转换。 例如,你可生成一个矩阵来转换和旋转一系列点。

矩阵是按行-列顺序编写的。 均匀缩放每个轴上的顶点(称为“统一缩放”)的矩阵由使用数学符号的以下矩阵表示。

用于统一缩放的矩阵的等式

在 C++ 中,Direct3D 使用 D3DMATRIX 结构将矩阵声明为二维数组。 以下示例演示如何初始化 D3DMATRIX 结构以充当统一缩放矩阵。

// In this example, s is a variable of type float.

D3DMATRIX scale = {
    s,               0.0f,            0.0f,            0.0f,
    0.0f,            s,               0.0f,            0.0f,
    0.0f,            0.0f,            s,               0.0f,
    0.0f,            0.0f,            0.0f,            1.0f
};

Translate

以下等式将点 (x, y, z) 转换为新点 (x', y', z')。

新点的转换矩阵的等式

你可在 C++ 中手动创建转换矩阵。 以下示例显示了创建用于转换顶点的矩阵的函数的源代码。

D3DXMATRIX Translate(const float dx, const float dy, const float dz) {
    D3DXMATRIX ret;

    D3DXMatrixIdentity(&ret);
    ret(3, 0) = dx;
    ret(3, 1) = dy;
    ret(3, 2) = dz;
    return ret;
}    // End of Translate

为方便起见,D3DX 实用工具库提供 D3DXMatrixTranslation 函数。

缩放

以下等式按 x 轴、y 轴和 z 轴方向的任意值将点 (x, y, z) 缩放到新点 (x', y', z')。

新点的缩放矩阵的等式

旋转

此处描述的转换是针对左手坐标系的,因此可能不同于你在其他地方看到的转换矩阵。

以下等式围绕 x 轴旋转点 (x, y, z),从而产生新点 (x', y', z')。

新点的 x 旋转矩阵的等式

以下等式围绕 y 轴旋转此点。

新点的 y 旋转矩阵的等式

以下等式围绕 z 轴旋转点。

新点的 z 旋转矩阵的等式

在这些示例矩阵中,希腊字母 θ 标识旋转角度(以弧度为单位)。 在顺着面向原点的旋转轴观察时,角度是按顺时针方向测量的。

在 C++ 应用程序中,使用 D3DX 实用工具库提供的 D3DXMatrixRotationXD3DXMatrixRotationY 和 D3DXMatrixRotationZ 函数创建旋转矩阵。 下面是 D3DXMatrixRotationX 函数的代码。

D3DXMATRIX* WINAPI D3DXMatrixRotationX
    ( D3DXMATRIX *pOut, float angle )
{
#if DBG
    if(!pOut)
        return NULL;
#endif

    float sin, cos;
    sincosf(angle, &sin, &cos);  // Determine sin and cos of angle

    pOut->_11 = 1.0f; pOut->_12 =  0.0f;   pOut->_13 = 0.0f; pOut->_14 = 0.0f;
    pOut->_21 = 0.0f; pOut->_22 =  cos;    pOut->_23 = sin;  pOut->_24 = 0.0f;
    pOut->_31 = 0.0f; pOut->_32 = -sin;    pOut->_33 = cos;  pOut->_34 = 0.0f;
    pOut->_41 = 0.0f; pOut->_42 =  0.0f;   pOut->_43 = 0.0f; pOut->_44 = 1.0f;

    return pOut;
}

连接矩阵

使用矩阵的一个优势在于,你可通过将两个或两个以上的矩阵相乘来组合其效果。 这意味着,要旋转模型并将其转换到某个位置,你无需应用两个矩阵。 相反,你将旋转矩阵和转换矩阵相乘以产生包含其所有效果的复合矩阵。 此过程称为矩阵连接,可使用以下等式进行编写。

矩阵连接的等式

在此等式中,C 是要创建的复合矩阵,而 M₁ 到 Mₙ 是独立的矩阵。 在大多数情况下,仅连接两个或三个矩阵,但没有限制。

使用 D3DXMatrixMultiply 函数执行矩阵乘法。

进行矩阵乘法运算的顺序很关键。 上述等式反映矩阵连接的从左到右规则。 也就是会说,你用于创建复合矩阵的矩阵的可见效果是按从左到右的顺序出现的。 以下示例中显示了典型的世界矩阵。 假设你要为常规飞碟创建世界矩阵。 你可能需要围绕飞碟中心(模型空间的 y 轴)旋转飞碟,并将它转换到场景中的某个其他位置。 要完成此效果,您先创建一个旋转矩阵,然后将它与转换矩阵相乘,如以下等式所示。

基于旋转矩阵和转换矩阵旋转的等式

在此公式中,Ry 是围绕 Y 轴的旋转的矩阵,Tw 是到世界坐标中某个位置的转换。

矩阵相乘的顺序很重要,因为与两个标量值相乘不同,矩阵相乘是不可交换的。 按相反顺序将矩阵相乘会产生以下视觉效果:将飞碟转换到其世界空间位置,然后围绕世界原点旋转飞碟。

无论你创建哪种类型的矩阵,请记住从左到右规则以确保实现预期效果。

入门