在數學中,矩陣(Matrix)是一個按照長方陣列排列的復數或實數集合,最早來自于方程組的系數及常數所構成的方陣。這一概念由19世紀英國數學家凱利首先提出。
基本運算
矩陣相乘
- 只有當左側矩陣的列數與右側矩陣的行數相等,兩個矩陣才能相乘。
-
矩陣相乘不遵守交換律(Commutative),也就是說A ? B ≠ B ? A
縮放位移矩陣
glm::mat4 trans;
trans = glm::translate(trans, glm::vec3(Sx, Sy, Sz));
trans = glm::scale(trans, glm::vec3(Tx, Ty, Tz));
旋轉矩陣
- 歐拉角
-
Y 偏航角(yaw)
繞y軸旋轉,類似我們的搖頭動作
-
X 俯仰角(pitch)
繞x軸旋轉,類似我們的點頭動作
-
Z 滾轉角(roll)
繞z軸旋轉,你可以想象飛船在做360°翻轉動作
-
- 示例
矩陣乘法是不遵守交換律的,所以它們的順序很重要。
當矩陣相乘時,在最右邊的矩陣是第一個與向量相乘的,所以你應該從右向左閱讀。
// 在代碼中我們先位移再旋轉,實際的變換卻是先應用旋轉再是位移
glm::mat4 trans;
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
正射投影矩陣
將OpenGL坐標系映射到指定寬高的屏幕坐標
圖片轉自https://learnopengl.com
// 參數:左邊界,右邊界,下邊界,上邊界,近平面,遠平面
glm::ortho(float L, float R, float B, float T, float N, float F);
// 示例
glm::ortho(0.0f, width, 0.0f, height, 0.0f, 100.0f)
- 近平面和遠平面構成的長方體箱子(平截頭體)之外的內容都會被裁剪
- 屏幕上的點會被縮小寬高比例,并移動(以x軸為例)
- (R + L) / (R - L)
,即將非平截頭體內的內容移出 - 因為OpenGL是右手坐標系,近平面
1.0f
,遠平面-1.0f
,為了轉化為我們常見遠近認知(左手坐標系),z軸進行了負縮放
透視投影矩陣
3D游戲中的透視效果,離的越遠的東西看起來越小
圖片轉自https://learnopengl.com
// 參數:視野弧度,寬高比,近平面,遠平面
glm::perspective(float F, float A, float N, float F)
// 示例
glm::perspective(glm::radius(45.0f), width / height, 0.1f, 100.0f);
- 近平面和遠平面構成的不規則箱子(平截頭體)之外的內容都會被裁剪
- 視野越大,看到的東西越多,通常為
glm::radius(45.0f)
- 近平面不能為
0
,這樣整個屏幕都會繪制到一個點上,通常為0.1f
- 近平面設置太大,表現為物體太靠近時會穿過去(消失)
觀察矩陣
顧名思義,是一個看著目標物體的矩陣,模擬出一個攝像機
// 上圖中的f,s,u分別對應以下變量
glm::vec3 f = glm::normalize(center - eye)
glm::vec3 s = glm::normalize(glm::cross(f, up))
glm::vec3 u = glm::cross(s, f)
// 參數:攝像機位置,目標點,上向量
glm::lookAt(glm::vec3 &eye, glm::vec3 ¢er, glm::vec3 &up)
// 示例
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f); // 攝像機位置向量
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f); // 攝像機方向向量
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); // 上向量
// 攝像機位置向量 + 攝像機方向向量 = 目標點向量
glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
- 設置攝像機位置時,注意是右手坐標系
- 通過攝像機位置向量 + 攝像機方向向量,可以得到攝像機方向
- 通過上向量叉乘攝像機方向可以得到右向量(兩個向量叉乘的結果會同時垂直于兩向量)
在一個3D游戲,頂點著色器最終都會乘上三個矩陣
// 透視矩陣 * 觀察矩陣 * 模型變幻矩陣 * 頂點信息
gl_Position = projection * view * model * vec4(a_position, 1);
攝像機變換
依托觀察矩陣,我們可以很方便的模擬出一個攝像機,并實現相關的變換。
攝像機移動
操作觀察矩陣中的cameraPos變量
- 前進(+) / 后退(-):攝像機方向 * 移動距離
cameraPos += cameraFront * distance;
- 右移(+) / 左移(-):攝像機方向叉乘上向量 * 移動距離
cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * distance;
- 上移(+) / 下移(-): 兩次叉乘得到上向量,我們稱為格拉姆—施密特正交化(Gram-Schmidt Process)
cameraPos += glm::normalize(glm::cross(glm::cross(cameraFront, cameraUp), cameraFront)) * distance;
攝像機旋轉
操作觀察矩陣中的cameraFront變量
float pitch = glm::radians(45.0f); // 俯仰角
float yaw = glm::radians(30.0f); // 偏航角
cameraFront.y = sinf(pitch); // 攝像機方向y
float newRadius = cosf(pitch); // 俯仰后偏航半徑變化
cameraFront.x = newRadius * sinf(yaw); // 攝像機方向x
cameraFront.z = -newRadius * cosf(yaw); // 攝像機方向z
cameraFront = glm::normalize(cameraFront); // 單位化
后記:
依賴游戲引擎,你可能并不需要實現這些,但是這些原理是你有必要掌握的。
3D的世界很精彩,我們要學習的也很多,希望能在這條路上越走越遠。
與君共勉!