Direct3D 转换管道

本文为 Direct3D 应用程序开发人员提供了有关如何通过直接操作 Direct3D 矩阵设置 Direct3D 转换管道参数的技术说明。

概述

Direct3D 使用三个转换来将 3D 模型坐标更改为像素坐标(屏幕空间)。 这些转换是世界转换、视图转换和投影转换。

世界转换控制如何将模型坐标转换为世界坐标。 世界转换可以包括平移、旋转和缩放,但它不适用于灯光。 有关使用世界转换的详细信息,请参阅 世界转换

视图转换控制从世界坐标到“相机空间”的转换,确定相机在世界中的位置。 有关使用视图转换的示例,请参阅 视图转换

投影转换将几何图形从相机空间更改为“剪辑空间”,并应用透视失真。 术语“剪辑空间”是指在此转换期间如何将几何图形剪裁到视图体积。 有关使用投影转换的示例,请参阅 投影转换

最后,剪辑空间中的几何图形将转换为像素坐标, (屏幕空间) 。 此转换由视区设置控制。

简单地说,剪裁和转换顶点必须在同源空间中发生 (坐标系统包含第四个元素) 的空间,但大多数应用程序的最终结果必须是“屏幕空间”中定义的非同质三维 (三维) 坐标。这意味着输入顶点和剪裁量都必须转换为同质空间才能执行剪裁,然后再转换回要显示的非同源空间。

三个 Direct3D 转换(世界转换、视图转换和投影转换)由 Direct3D 矩阵定义。 Direct3D 矩阵是由 D3DMATRIX 结构定义的 4x4 同构矩阵。 尽管 Direct3D 矩阵不是标准对象(它们不由 COM 接口表示),但可以像创建和设置任何其他 Direct3D 对象一样创建和设置它们。 有关 Direct3D 矩阵的详细信息,请参阅 转换

转换管道

如果模型坐标中的顶点由 Pm = (Xm、Ym、Zm、1) 给出,则下图中显示的转换将应用于计算屏幕坐标 Ps = (Xs、Ys、Zs、Ws) 。

模型空间到屏幕空间转换

下面是上图中所示阶段的说明:

  1. 世界矩阵 Mworld 将顶点从模型空间转换为世界空间。 此矩阵按以下方式设置:

        d3dDevice->SetTransform (D3DTRANSFORMSTATE_WORLD, matrix address) 
    

    Direct3D 实现假定此矩阵的最后一列 (0、0、0、1) 。 如果用户指定具有不同最后一列的矩阵,但照明和雾将不正确,则不会返回错误。

  2. 视图矩阵 Mview 将顶点从世界空间转换为相机空间。 此矩阵按以下方式设置:

        d3dDevice->SetTransform (D3DTRANSFORMSTATE_VIEW, matrix address) 
    

    Direct3D 实现假定此矩阵的最后一列 (0、0、0、1) 。 如果用户指定具有不同最后一列的矩阵,但照明和雾将不正确,则不会返回错误。

  3. 投影矩阵 Mproj 将顶点从相机空间转换为投影空间。 此矩阵按以下方式设置:

        d3dDevice->SetTransform (D3DTRANSFORMSTATE_PROJECTION, matrix address) 
    

    投影矩阵的最后一列应 (0、0、1、0) 或 (0、0、a、0) ,以便获得正确的雾和照明效果;首选 (0、0、1、0) 形式。

    投影空间中所有点 Xp = (Xp、Yp、Zp、Wp) 的剪辑音量定义为:

        -Wp < Xp <= Wp 
        -Wp < Yp <= Wp 
        0 < Zp <= Wp 
    

    不满足这些公式的所有点将被剪裁。

    如果视图卷定义为:

    • 近剪裁平面中相机空间中的屏幕窗口宽度
    • 近剪裁平面中相机空间中的 Sh 屏幕窗口高度
    • 相机空间中沿 Z 轴到近剪裁平面的 Zn 距离
    • 相机空间中沿 Z 轴的远距剪裁平面的 Zf 距离

    然后,可以编写透视投影矩阵,如下所示:

    显示透视投影矩阵。

    其中 Mij 是 Mproj 的成员。

    对于正交投影,我们有:

    正交投影

    Direct3D 假定透视投影矩阵采用以下形式:

    透视投影矩阵

    如果透视投影矩阵不具有此形式,则会出现一些项目。 例如,表雾将不起作用。

  4. Direct3D 允许用户更改剪辑音量,如下所示:

    更改剪辑音量

    这可以重写为:

    更改重写的剪辑卷

    其中:

        Cx, Cy - dvClipX, dvClipY from D3DVIEWPORT9  
        Cw, Ch - dvClipWidth, dvClipHeight from D3DVIEWPORT9  
        Zmin, Zmax - dvMinZ, dvMaxZ from D3DVIEWPORT9  
    

    此转换可以提供更高的精度,相当于缩放和移动剪裁量。

    相应的 Mclip 矩阵为:

    mclip 矩阵

    顶点的转换方式为:

        dvClipWidth = 2   
        dvClipHeight = 2   
        dvClipX = -1   
        dvClipY = 1   
        dvMinZ = 0   
        dvMaxZ = 1   
    

    如果不想缩放剪辑音量,可以将视区参数设置为默认值:

        (Xc, Yc, Zc, Wc) = (Xp, Yp, Zp, Wp) * Mclip   
    
  5. 如果用户在 DrawPrimitive 调用中指定D3DDP_DONOTCLIP标志,则剪辑阶段是可选的。 在这种情况下,可以组合所有矩阵 (包括 Mvs) 。

  6. 视区比例矩阵 Mvs 将坐标缩放为在视区窗口中,并将 Y 轴从上翻转为向下翻转:

    视区比例矩阵 mvs

    其中:

        dwX, dwY - viewport offsets in pixels from D3DVIEWPORT9 
        dwWidth, dwHeight - viewport width and height in pixels from D3DVIEWPORT9    
    
  7. 最后,计算屏幕坐标并将其传递给光栅器:

    计算并传递给光栅器的屏幕坐标

使用技巧

下面是有关如何使用 Direct3D 转换管道的一些提示:

  • 世界和视图矩阵的最后一列应 (0、0、0、1) ,否则照明不正确。

  • 设置视区参数以生成标识 Mclip 矩阵,除非你确切地了解它的用途。

        dvClipWidth = 2 
        dvClipHeight = 2
        dvClipX = -1
        dvClipY = 1
        dvMinZ = 0 
        dvMaxZ = 1