1. 程式人生 > >使用GDI+模擬directx 3D渲染中的座標變換

使用GDI+模擬directx 3D渲染中的座標變換

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