「學」的部分
通過上一節的內容,我們已經知道關於矩陣的兩個重要知識點:矩陣的由來和矩陣的乘法。
這個時候,大部分人腦子裡估計都會有個疑問:矩陣只不過是數學家換一種方式的「戲法」罷了,具體有什麼作用?
如果這個世界上沒有電腦,我們還停留在用紙和筆計算的時代,那矩陣真的沒什麼用(其實,當時數學家發明矩陣就處於這樣的時代背景下,所以,數學家的思維一直都是超前的)。在大多數人的眼中,矩陣除了可以快速解多元一次方程外,沒有其他任何用處。說真的,在現實生活中,我們用到矩陣的地方也非常非常少。
我一直把某一個道理信奉為真理:如果你覺得某個問題難以解決,不妨好好去學一學數學,沒準兒,數學家在好幾百年前就已經有了解決方法了。
矩陣就是這樣,當時被發明出來的時候很多人並不理解,一個快速計算方法有什麼用?直到有了計算機,直到程式設計師在處理計算機3D問題時遇到了非常大困難的時候,大家才發現矩陣的真正用處——它能大大提高3D問題中複雜計算的效率。
打個比方你就明白了。在電腦上運行3D程序,就好比是在路上開車。而矩陣,就相當於是一個額外的氮氣加速器。假設,在沒有矩陣的情況下,車子只能開10碼,那麼,有了矩陣之後,車子就能開到200碼的樣子了。
在程式設計師使用矩陣之前,他們也不知道計算機在處理矩陣的時候那麼給力,所以,這應該算是一個意外之喜吧?
好了,大致了解了前因後果之後,我們來看一看矩陣在3D遊戲編程中到底怎麼用吧。
一、虛擬3D中的3D物體需要有哪些操作?
我們再簡單複習一下遊戲中的3D世界。第一步,我們要在一個虛擬的3D坐標系中構建3D世界;第二步,我們把各種各樣的3D物體加入到這個虛擬世界中;第三步,就是用虛擬攝像機把虛擬場景拍攝下來。
這個過程中,第二步是處理3D物體的過程,現在,我們稍微深入了解一下。
假設,我們要在3D世界中加入一輛小汽車,並讓小汽車停在預定的停車位中。我們要如何解決這個問題呢?其實,方法挺容易的。
1.用3D軟體創造一個小汽車的模型,並按照一定規則導入到我們的程序中。一般來講,軟體生成的小汽車模型會有一個中心點,而小汽車模型就是圍繞這個中心點構建起來的。在程序中,我們一般用一個結構類型(第6節的知識點)來存儲這個小汽車模型,在這個結構中,會保存小汽車的所有模型數據,還會保存小汽車的其他屬性,比方說位置之類的。
2.然後,我們要確定3D世界中停車位的具體位置和朝向。有了位置,我們就可以把小汽車移動過去,有了朝向,我們就可以把小汽車轉到正確的方向上。
3.當然,我們小汽車模型的大小可能不對,所以,我們要按照虛擬世界中物體的大小來縮小或放大這個小汽車模型(由於我們有小汽車模型的所有詳細數據,不管是放大還是縮小,都不會影響小汽車的精度)。
3個步驟做完後,這個導入的小汽車就能來到我們預定的位置上了。
我們再仔細看看上面的步驟,你會發現關鍵的地方有3個:
關鍵一:移動小汽車的位置;
關鍵二:旋轉小汽車;
關鍵三:縮放小汽車。
實際上,這3個關鍵點是所有3D物體共有的特徵點,也是所有的特徵點。換句話說就是,任何一個3D物體,只要經過了這3個關鍵步驟,就能到達我們預先設想的位置上了。
這3個關鍵步驟都有專業的名詞,關鍵一叫做平移,關鍵二叫做旋轉,關鍵三叫做縮放。
但是,在3D遊戲編程中,這3個步驟的順序並不是這樣的,而是按照先縮放、再旋轉、後平移的步驟進行的。
二、從旋轉開始
為什麼是縮放->旋轉->平移的步驟呢?
還是拿小汽車為例。當小汽車從數據變成圖形的時候,最開始是處於虛擬3D坐標系原點的,而且,初始的小汽車模型數據是優化過的,其中很多數據都是對稱的或者乾脆是0,也就說,最原始的小汽車數據是最容易處理的。這3個步驟中,平移是最簡單的,縮放稍微複雜些,旋轉是最複雜的了。
如果我們先平移後旋轉,就相當於讓簡單的平移把最原始的優化數據破壞了,數據一旦被破壞就會變複雜,然後,這個「複雜化」的數據再進行旋轉的話,會加大CPU的負荷。
如果我們先旋轉後平移,相當於最優的數據先進行複雜計算,然後再進行簡單的平移計算,就能大大提高3D圖形數據的處理速度了。
明白了這一切之後,我們再深入了解一下3D物體的旋轉。
由於上面先旋轉後平移的原則,我們的旋轉都是在3D坐標系原點進行的。一開始我們會覺得旋轉很簡單,不就是轉動個角度嗎?仔細一想發現並不簡單,因為物體是自由的,它可以繞著任何一條直線進行旋轉,問題一下子就變得複雜了。
但是,根據程式設計師們的經驗,任何一個物體的複雜旋轉問題,都可以分解成相對簡單些的「繞軸」旋轉,也就是繞著x軸、y軸或者z軸之一進行旋轉。因為任一旋轉軸都可以看成是一個向量,這個向量可以分別在三個軸上進行投影,某個物體繞著該軸旋轉的時候,我們可以用三角函數的知識得到該物體繞3個軸分別旋轉了多少角度。總而言之一句話,繞著3個方向軸的旋轉,可以合成3D物體的任意旋轉。
所以,我們只需要知道,一個3D物體繞某個方向軸旋轉應該如何計算,然後,我們就可以處理3D物體的任意旋轉了。
我們可以把問題再簡化一下,這迴旋轉的不是小汽車了,而是一個立方體(旋轉的原理是一樣的),立方體的正中心就處於3D坐標軸的原點。
好了,我們試著讓這個立方體繞著z軸順時針旋轉一個角度θ。
你發現了什麼?沒錯,一個立方體繞著z軸旋轉的時候,裡面任意一點的z坐標是不變的。為什麼z坐標不變呢?你想一想,要改變立方體的z坐標,是不是需要讓立方體在z軸方向上移動呢?而現在,立方體只是繞著z軸旋轉,根本就沒有移動。如果把這個立方體投影到z軸上(降到一維),它就變成了一條直線,而旋轉的過程中,這條直線是不會變的。
也就是說,立方體繞著z軸旋轉,我們只需要重新計算旋轉後的x、y坐標就可以了,然後,這個相對複雜的3D問題一下子就變成一個2D問題了。
在3D遊戲編程中,所有的3D物體都是由一個一個小點組成的,也就是說,立方體的旋轉過程,可以簡化成一個又一個點的旋轉。接下來,我們就在2D坐標系下推導一下任意一點的旋轉計算過程。