作業目標:
get_model_matrix(float rotation_angle)
:
逐個元素地構建模型變換矩陣並返回該矩陣。在此函數中,你只需要實現三維中繞 z 軸旋轉的變換矩陣,而不用處理平移與縮放
get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
:
使用給定的參數逐個元素地構建透視投影矩陣並返回該矩陣。
實現:
get_model_matrix(float rotation_angle)
三維空間,繞Z軸旋轉,由一個角度,得到一個旋轉矩陣
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
float theta = (rotation_angle / 180.0) * MY_PI;//角度轉弧度
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();//新建一個4x4的矩陣
model << std::cos(theta), -std::sin(theta), 0, 0,
std::sin(theta), std::cos(theta), 0, 0,
0, 0, 1.0, 0,
0, 0, 0, 1.0;
//PS:繞X、Z軸旋轉的旋轉矩陣同理,繞Y軸的旋轉矩陣略有不同,sin和-sin的位置會互換
return model;
}
get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
攝像機上方是y正半軸,右邊是x正半軸、看向z負半軸
現在假設我們站在x正半軸 看向x負半軸
eye_fov:視角,攝像機最上方能看到的角度到最下方能看到的角度
aspect_ratio:矩形寬高比
zNear、zFar:近處面的z值,遠處面的z值,注意,朝向z負半軸,即兩者均為負值
目標得到投影矩陣,先將透視圖轉化為正交圖,然后再進行縮放,使得x、y、z三維方向的值的范圍均在[-1,1],最后將其平移到視口中心
視錐->立方體
透視圖->正交圖
根據zFar、zNear、eye_fov、aspect_ratio可以算出寬、高。
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
//Eigen::Matrix4f::Identity() 初始化為單位矩陣
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
//透視圖,近大遠小,是個視錐 此矩陣是一個公式
Eigen::Matrix4f P2O = Eigen::Matrix4f::Identity();//將透視矩陣擠壓成正交矩陣
P2O << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, - zFar* zNear,
0, 0, 1.0, 0;
float halfEyeAngelRadian = (eye_fov / 2.0 / 180.0) * MY_PI; //視角的一半
float y_top = -zNear * std::tan(halfEyeAngelRadian);//y軸正方向值 = 顯示視口的一半高度 zNear是負值!
float x_left = -y_top * aspect_ratio;//x軸負方向值 = 顯示視口的一半寬度
float y_down = -y_top;
float x_right = -x_left;
//構造縮放矩陣,使視口大小等同窗口大小
Eigen::Matrix4f scaleMat = Eigen::Matrix4f::Identity();
scaleMat << 2 / (x_right - x_left), 0, 0, 0, //將中心視為原點,則窗口的三維方向值域均為[-1,1]
0, 2 / (y_top - y_down), 0, 0, //縮放的倍數為 期望值/當前值
0, 0, 2 / (zNear - zFar), 0, //所以縮放的倍數為 (1+1)/某一維度的當前值
0, 0, 0, 1;
//構造平移矩陣,將視口左下角移動到原點
Eigen::Matrix4f translateMat = Eigen::Matrix4f::Identity();
//左下角的點原本為 (x_left,y_down,zNear)
//注意!此時已經經過了縮放,所以左下角的點的位置已經變化
//左下角的點現在為 (-1,-1,zNear)
//即其實可以不用管x和y軸,比較尺寸已經和窗口匹配了
//但網上其他人卻還是右下方注釋的那樣寫的,左側+右側或者上側+下側,結果不都是0么?
translateMat << 1, 0, 0, 0, //-(x_left+x_right)/2
0, 1, 0, 0, //-(y_top+y_down)/2
0, 0, 1, -(zNear+zFar)/2,
0, 0, 0, 1;
//注意!此處矩陣相乘,右結合率,必須先壓縮矩陣,再縮放,最后平移,順序一定不可以改!
projection = translateMat * scaleMat * P2O;
return projection;
}