- 注意!本文是在下几年前入门期间所写(young and naive),其中许多表述可能不正确,为防止误导,请各位读者仔细鉴别。
渲染管线
因为很多英文强行翻译过来有点怪怪的(比如Normalized Device Coordinates翻译成归一化的设备坐标)或者翻译过来可能会有点不规范,所以以后有些部分直接英文写了。
渲染管线的示意图如下
Input Assembler Stage
顶点存在绑定到渲染管线的vertex buffer里,顶点的拓扑关系有很多种,最常用的是D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,即每3个顶点构成一个三角形。但是这样重复存储了很多顶点,n个三角形要存3n个顶点,所以用indice可以减少存储的顶点数量和运算量,即存每一个顶点的时候还带一个index,然后单独用一个列表存三角形,每三个index构成一个三角形。
Vertex Shader Stage
顶点着色中有一步是变换坐标,即首先是把局部空间变换到其次裁剪空间。变换过程首先是从局部空间到世界空间,然后世界空间到摄像机的空间,然后摄像机再变换到齐次裁剪空间。
第一步,要从模型坐标空间到世界坐标,要知道模型坐标在世界坐标下的三个轴和原点的位置,就可以拼出变换矩阵,不过这样有点麻烦,更简单的办法是知道模型坐标系在世界坐标系下的缩放、旋转、位移,变换矩阵就是W=SRT,称W为world matrix。
第二步是到摄像机空间,也就是摄像机变换到世界坐标的逆变换,知道摄像机的S、R、T,变换矩阵就是V=(SRT)-1=T-1R-1(因为这里S肯定是1),称V为view matrix。在dx中获取view matrix如下:
XMMATRIX XM_CALLCONV XMMatrixLookAtLH( // Outputs view matrix V
FXMVECTOR EyePosition, // Input camera position Q
FXMVECTOR FocusPosition, // Input target point T
FXMVECTOR UpDirection); //Input world up direction j
下一步就是投影,输入是FOV角 α \alpha α和宽高比r(FOV是纵向的角,这也就是为什么平常游戏里设置90度就觉得已经比较宽了,因为按16比9的话横着有160度了),然后还有输入是摄像机坐标下的xyz,z是摄像机朝向的方向,x是朝右,y是朝上(摄像机一般不能转roll,只能转pitch和yaw,所以只有朝上),然后投影的原理很简单,三角形相似就能得到屏幕上的坐标。
最后化NDC,注意现在x是在-r到r之间,我们想变成x/r在-1到1之间,所以现在只要x除以个r就行了。
然而这只是数学原理,现在我们要求变换矩阵,但是这个变换不是线性的,我们拆成两部分,除以z的这一步是非线性,其他都是线性,所以我们把除z放到最后一步,(这一步叫做齐次除法),然后我们还想把z的范围从n到f归一化到从0到1,顶点的第四个维度是1,所以变换的时候我们用个小trick,用这个第四维来存z,变换矩阵如下
现在我们先计算 [ x , y , z , 1 ] P = [ x r t a n ( α / 2 ) , y r t a n (