Directx 渲染管线一个重要功能就是将3d空间里 建立好的虚拟系统投影映射到屏幕的2D空间里显示,其实主要有4个步骤,也就是坐标系的变换,如下:
物体坐标系 -> 世界坐标系 –> 摄像机坐标系 -> 屏幕坐标系统
每个变换都有相对应的矩阵:
1、 物体坐标系 -> 世界坐标系 (主要用到旋转、平移、缩放矩阵)
2、 世界坐标系 –> 摄像机坐标系(视口矩阵)
3、 摄像机坐标系->屏幕坐标系(投影矩阵)
首先我们的关键任务就是定义一个矩阵类和向量类以及矩阵向量之间的运算,然后写出获得这些矩阵的函数来:
向量类
class Vector4
{
public:
Vector4();
Vector4(float x,float y,float z,float w);
Vector4(const Vector4& v);
// 取负,+=,-=,*=,+,-,*,=操作
Vector4 operator-() const;
void operator+=(const Vector4& rhs);
void operator-=(const Vector4& rhs);
void operator*=(float s);
Vector4 operator+(const Vector4& rhs) const;
Vector4 operator-(const Vector4& rhs) const;
Vector4 operator*(float s) const;
void operator=(const Vector4& rhs);
// 单位化、叉积、点积
float Length();
static Vector4 Normalize(Vector4& v);
static Vector4 Cross3(const Vector4& v0, const Vector4& v1);
static float Dot3(const Vector4& v0, const Vector4& v1);
static float Dot4(const Vector4& v0, const Vector4& v1);
// 坐标变换
static Vector4 Transform(const Vector4&v,const Matrix44&mat);
float x;
float y;
float z;
float w;
};
矩阵类
class Matrix44
{
public:
Matrix44();
Matrix44(const Vector4& row0, const Vector4& row1, const Vector4& row2, const Vector4& row3);
Matrix44(const Matrix44& rhs);
static Matrix44 Identity();
// 旋转、缩放、平移矩阵、乘
static Matrix44 RotationX(float angle);
static Matrix44 RotationY(float angle);
static Matrix44 RotationZ(float angle);
static Matrix44 Scaling(float sx,float sy,float sz);
static Matrix44 Translation(float x,float y,float z);
static Matrix44 Multiply(const Matrix44& m0, const Matrix44& m1);
// 视口、投影矩阵
static Matrix44 ViewMatrix(Vector4& eye,Vector4& at,Vector4& up);
static Matrix44 ProjMatrix(float fovy,float aspect,float zn,float zf);
Vector4 r0;
Vector4 r1;
Vector4 r2;
Vector4 r3;
};
向量、矩阵的运算以及视口投影矩阵的获得我都以类静态函数给出,直接调用就用 ‘类名:’来引用.例如:Matrix44::ViewMatrix(…);就是获得视口矩阵的调用,参数自己加
最后投影过后还需要将坐标映射到屏幕上还需要做以下工作:
math::Matrix44 mat;
mat.r0.x = g_clientWndW;
mat.r2.x = g_clientWndW/2;
mat.r2.y = g_clientWndH/2;
mat.r1.y = -g_clientWndW;
Vector4 vTemp[8];
for(int i = 0;i < 8;i++)
{
vTemp[i] = math::Vector4::Transform(g_cubeVertex[i] , worldMatrix);
vTemp[i] = math::Vector4::Transform(vTemp[i] , g_viewMatrix);
vTemp[i] = math::Vector4::Transform(vTemp[i] , g_projMatrix);
vTemp[i].x /= vTemp[i].w;
vTemp[i].y /= vTemp[i].w;
vTemp[i].z /= vTemp[i].w;
vTemp[i].w = 1;
vTemp[i] = math::Vector4::Transform(vTemp[i] , mat);
}
//
vTemp[i].x /= vTemp[i].w;
vTemp[i].y /= vTemp[i].w;
vTemp[i].z /= vTemp[i].w;
vTemp[i].w = 1;
除以w是为了让坐标锁定在-1到1之间,然后再乘以个mat矩阵就是具体屏幕坐标,mat矩阵其实就是将坐标x放大屏幕宽度大小,y放大屏幕高度大小,然后将y轴取反,x,y分别平移到屏幕左上角。
其实就是下面坐标系变换,如图:
最终效果图(我简单绘制了一个立方体,并且把不可见变用虚线绘制,就用变换后的坐标点连接的线用GDI+的DrawLine实现):
代码下载地址:
http://download.csdn.net/source/2962028
http://bbs.csdn.net/topics/350262983