图元装配完之后,vertex们就到达了vertex shader阶段。可以将vertex shader阶段视为一个函数,输入的是vertex,输出的是vertex。在硬件的实际计算过程中可以简单的认为是硬件做了如下过程:

for(UINT i = 0; i < numVertices; ++i)

outputVertex[i] = VertexShader( inputVertex[i] );

这个阶段可以实现的效果有:

  • 变换transformation
  • 光照lighting
  • 置换贴图displacement mapping

    and so on

     
     

物体空间和世界空间

Local Space and World Space

观察空间

View Space

投影与齐次裁剪空间

Projection and Homogeneous Clip Space

定义一个视锥体(平截头体)

Defining a Frustum

投影矩阵

 
 

这个部分可以参考《3d math primer for graphics and game development》,我就不废话了

 
 

标准化设备坐标系

Normalized Device Coordinates (NDC)

前面部分中投影点的坐标是在视图空间中计算的。在视图空间中,投影窗口的高度为2,宽度为2r,其中r是纵横比。问题是尺寸取决于纵横比。这意味着需要告诉硬件纵横比,因为硬件稍后需要执行一些涉及投影窗口尺寸的操作(例如将其映射到后缓冲区)。如果我们能去掉对长宽比的依赖,那就更方便了。解决方案是将投影的x坐标从间隔[-r,r]缩放到[-1,1],如下所示:


映射之后坐标就被描述在了NDC中。这里只有x和y轴坐标被标准化,z轴坐标并没有。

当且仅当满足以下条件时一个点才会在视锥体中:


用矩阵写出投影方程

Writing the Projection Equations with a Matrix

目的是保持一致性,因为别的变换都用矩阵表示了。

但是Equation是非线性的,需要拆成两部分,非线性的部分除以Z,但是z坐标之后还需要用,先保存在w分量。

 
 

标准化深度值

Normalized Depth Value

因为需要进行深度测试,DirectX中希望深度值在[0,1],这样好处理,所以要把深度值进行标准化。将原区间映射到0~1可以通过缩放和唯一来实现,如下:


如果近平面映射到0则有:


如果远平面映射到0则有


由1式可以得B = −An,代入2式


因此:


函数图像如下:


急剧增加且非线性的,和近平面比较近的部分几乎占用了所有的范围,大部分的深度值其实被映射到了一个小的子集里面,会导致深度缓冲的精度问题,毕竟硬件的精度数值式有限的。在实际使用过程中尽量使近平面和远平面接近一点。

 
 

XMMatrixPerspectiveFovLH

透视投影矩阵的创建可以使用如下的D3D数学函数:

XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovLH

(

float FovAngleY, //从上往下看视锥体的角度

float AspectRatio, //X:Y 宽高比

float NearZ, //近平面到裁剪平面的距离 必须大于零

float FarZ //远平面到裁剪平面的距离 必须大于零

);

 
 

来自 <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-xmmatrixperspectivefovlh>

 
 

返回透视投影矩阵。

宽高比与窗口宽高比匹配:

float D3DApp::AspectRatio()const {

return static_cast<float>(mClientWidth) / mClientHeight;

}