软件光栅器三之世界矩阵,相机变换矩阵,透视投影矩阵,透视除法,视口变换矩阵

来源:互联网 发布:手机淘宝联盟如何提现 编辑:程序博客网 时间:2024/05/12 02:58

其实各大矩阵具体的推导过程我就不给出了,我直接给出矩阵具体的形式和实现代码,以及那些大牛推导矩阵详细的文章:


一,世界矩阵(WorldMatrix)


我一般称世界矩阵为SRT矩阵,SRT分别是"Scale","rotate","translate"三个单词的缩写,也就是世界矩阵由缩放矩阵,旋转矩阵,平移矩阵构成的


(1)缩放矩阵(ScaleMatrix)

假设在X轴缩放Sx倍,在Y轴缩放Sy倍,在Z轴缩放Sz倍,缩放如下所示:




点乘以矩阵的公式如下:

\


实现代码:

//构造一个缩放矩阵Matrix BuildScaleMatrix(float x, float y, float z){Matrix ScaleMa;//清除为0ZeroMemory(&ScaleMa, sizeof(ScaleMa));ScaleMa.ma[0][0] = x;ScaleMa.ma[1][1] = y;ScaleMa.ma[2][2] = z;ScaleMa.ma[3][3] = 1.0f;return  ScaleMa;}



(2)旋转矩阵(RotateMatrix)

在说明旋转矩阵之前,我得说明一下,在左手坐标系如何判断哪个方向为顺时针方向旋转。

先来看下面的图(本人画工有点差,请见谅),我们先用左手的拇指朝向Y的正方向,则四指所绕的方向也就是饶Y轴渲染的顺时针方向了,其它轴的顺时针方向同理也就是这样判断。




一,绕X轴顺时针旋转Θ度数



实现代码:

//构造一个绕X轴的旋转矩阵,形参为角度,顺时针Matrix BuildRotateXMatrix(float x){Matrix RotateXMa;//清除为0ZeroMemory(&RotateXMa, sizeof(RotateXMa));//度数转化为弧度,math库的函数的参数都是弧度float angle = (x *MATH_PI) / (180.0f);RotateXMa.ma[0][0] = 1.0f;RotateXMa.ma[3][3] = 1.0f;float cs = (float)cos(angle);float ss = (float)sin(angle);RotateXMa.ma[1][1] = cs;RotateXMa.ma[2][2] = cs;RotateXMa.ma[1][2] = ss;RotateXMa.ma[2][1] = -ss;return RotateXMa;}


二,绕Y轴顺时针旋转Θ度数



实现代码:

//构造一个绕Y轴的旋转矩阵,形参为角度,顺时针Matrix BuildRotateYMatrix(float y){Matrix RotateYMa;//清除为0ZeroMemory(&RotateYMa, sizeof(RotateYMa));//度数转化为弧度,math库的函数的参数都是弧度float angle = (y *MATH_PI) / (180.0f);RotateYMa.ma[1][1] = 1.0f;RotateYMa.ma[3][3] = 1.0f;float cs = (float)cos(angle);float ss = (float)sin(angle);RotateYMa.ma[0][0] = cs;RotateYMa.ma[2][2] = cs;RotateYMa.ma[0][2] = -ss;RotateYMa.ma[2][0] = ss;return RotateYMa;}

三,绕Y轴顺时针旋转Θ度数





实现代码:

//构造一个绕Z轴的旋转矩阵,形参为角度,顺时针Matrix BuildRotateZMatrix(float z){Matrix RotateZMa;//清除为0ZeroMemory(&RotateZMa, sizeof(RotateZMa));RotateZMa.ma[2][2] = 1.0f;RotateZMa.ma[3][3] = 1.0f;//度数转化为弧度,math库的函数的参数都是弧度float angle = (z *MATH_PI) / (180.0f);float cs = (float)cos(angle);float ss = (float)sin(angle);RotateZMa.ma[0][0] = cs;RotateZMa.ma[1][1] = cs;RotateZMa.ma[0][1] = ss;RotateZMa.ma[1][0] = -ss;return RotateZMa;}


(3)移动矩阵(TranslateMatrix)


假设某个顶点在X轴方向移动dx个单位,Y轴移动dy个单位,在Z轴移动dz个单位



实现代码:

//构造一个移动矩阵Matrix BuildTranslateMatrix(float x,float y,float z){Matrix TranslateMa;//清除为0ZeroMemory(&TranslateMa, sizeof(TranslateMa));TranslateMa.ma[0][0] = 1.0f;TranslateMa.ma[1][1] = 1.0f;TranslateMa.ma[2][2] = 1.0f;TranslateMa.ma[3][3] = 1.0f;TranslateMa.ma[3][0] = x;TranslateMa.ma[3][1] = y;TranslateMa.ma[3][2] = z;return TranslateMa;}



关于缩放矩阵,旋转矩阵,移动矩阵的详细推导见《Introduction+to+3D+Game+Programming+with+DirectX+11》第三章

二,相机变换矩阵(ViewMatrix)



在此之前看看UVN相机模型,如图所示:



注视向量N的方向跟相机空间(ViewSpace)的Z轴方向是一致的,而竖直向量V的方向与相机空间(ViewSpace)的Y轴方向是一致的,右向量U的方向与相机空间(ViewSpace)的X轴方向是一致的。


计算UVN向量的公式如下:





相机矩阵如下所示:






实现代码:

//建立一个左手坐标系的相机变换矩阵Matrix MatrixLookAtLH(Point* Eye, Point* LookAt, Vector* Up){Vector N;Vector U;Vector V;//求出注视向量NN.x = LookAt->x - Eye->x;N.y = LookAt->y - Eye->y;N.z = LookAt->z - Eye->z;N.w = 0.0f;//用叉乘求出右向量U,U=UpxN;U = VectorCrossProduct(Up, &N);//用叉乘求出V,V=NxUV = VectorCrossProduct(&N, &U);//规格化UVN三个向量VectorNormalize(&U);VectorNormalize(&V);VectorNormalize(&N);//求出-EyeU -EyeV -EyeNfloat x1 = -VectorDotProduct(Eye, &U);float y1 = -VectorDotProduct(Eye, &V);float z1 = -VectorDotProduct(Eye, &N);//求出相机变换矩阵Matrix mViewMatrix;ZeroMemory(&mViewMatrix, sizeof(mViewMatrix));mViewMatrix.ma[0][0] = U.x;mViewMatrix.ma[1][0] = U.y;mViewMatrix.ma[2][0] = U.z;mViewMatrix.ma[3][0] = x1;mViewMatrix.ma[0][1] = V.x;mViewMatrix.ma[1][1] = V.y;mViewMatrix.ma[2][1] = V.z;mViewMatrix.ma[3][1] = y1;mViewMatrix.ma[0][2] = N.x;mViewMatrix.ma[1][2] = N.y;mViewMatrix.ma[2][2] = N.z;mViewMatrix.ma[3][2] = z1;mViewMatrix.ma[3][3] = 1.0f;return mViewMatrix;}


具体推导见文章:推导相机变换矩阵

三,透视投影矩阵(PerspectiveMatrix)


在推导透视投影矩阵前先看看视截体(Frustum)是怎么样的:




我的渲染器,近截面和投影平面是同一个平面,视截体在YZ平面的投影如下面图所示,




n为原点到近截面的距离,f为原点到远截面的距离,α为视截体在YZ平面投影的FOV视角,r为投影平面的宽高比,则透视投影矩阵为:





实现代码:

//建立一个左手坐标系的透视投影矩阵,注意为左手坐标系下的,视平面和近截面为同一个平面,FOV视角为YZ屏幕的Matrix MatrixPerspectiveFovLH(float FovYZ,float ScreenAspect,float ScreenNear,float ScreenFar){Matrix mProjMatrix;//清除为0ZeroMemory(&mProjMatrix, sizeof(mProjMatrix));//度数转化为弧度,math库的函数的参数都是弧度float angle = (FovYZ *MATH_PI)/ (180.0f);//半角angle /= 2.0f;//求出各类参数float s1 = 1 / (ScreenAspect*(float)tan(angle));float s2 = 1 / tan(angle);float a = ScreenFar / (ScreenFar - ScreenNear);float b = -(ScreenNear*ScreenFar) / (ScreenFar - ScreenNear);mProjMatrix.ma[0][0] = s1;mProjMatrix.ma[1][1] = s2;mProjMatrix.ma[2][2] = a;mProjMatrix.ma[3][2] = b;mProjMatrix.ma[2][3] = 1.0f;return mProjMatrix;}

具体推导参见文章:  

深入探索透视投影变换 和 深入探索透视投影变换(续)




四,透视除法:


透视除法的本质:
假设有一个向量为(x,y,z,w),进行透视除法也就是把向量的每个参数除以向量的第四个参数,得到(x/w,y/w,z/w,1)

参见 深入探索透视投影变换 和 深入探索透视投影变换(续)

实现代码:

//对点集进行透视除法,会后变到NDC空间void PerspectiveDivede(vector<Vertex>& pList){for (vector<Vertex>::iterator it = pList.begin(); it != pList.end(); ++it){(*it).x = (*it).x / (*it).w;(*it).y = (*it).y / (*it).w;(*it).z = (*it).z / (*it).w;(*it).w = (*it).w / (*it).w;}}



五,视口变换矩阵(ViewPortMatrix)

实际上在D3D11中,不需要我们手动进行视口变换的,D3D11给我们提供了一个接口,我们在这个接口填充参数,然后D3D11在内部自动帮我们进行视口变换了,那个接口如下图所示:



生成相应的视口矩阵:



解释一下上面的参数,TopLeftX和TopLeftY为视平面在左上角的坐标点,一般来说都为0。Width为视平面的宽度,Height为视平面的高度,MinDepth为顶点的最小深度(Z缓存),
MaxDepth为顶点的最大深度(Z缓存),一般来说MinDepth为0,MaxDepth为1.

所以一般视口变换矩阵为下面所示:







实现代码:

}//一般而言,视口变换矩阵的TopLeftX=0,TopLeftY=0,MaxDepth=1.0,MinDepth=0.0Matrix  MatrixViewPort(float ScreenWidth, float ScreenHeight, float MaxDepth, float MinDepth, float TopLeftX, float TopLeftY){Matrix MatrixViewPort;//清除为0ZeroMemory(&MatrixViewPort, sizeof(MatrixViewPort));MatrixViewPort.ma[0][0] = ScreenWidth / 2.0f;MatrixViewPort.ma[1][1] = -ScreenHeight/ 2.0f;MatrixViewPort.ma[2][2] = MaxDepth - MinDepth;MatrixViewPort.ma[3][0] = TopLeftX + ScreenWidth / 2.0f;MatrixViewPort.ma[3][1] = TopLeftY+ ScreenHeight / 2.0f;MatrixViewPort.ma[3][2] = MinDepth;MatrixViewPort.ma[3][3] = 1.0f;return MatrixViewPort;}

具体推导参见《Introduction+to+3D+Game+Programming+with+DirectX+11》的第十六章的16.1小节







实现代码:
0 0