《3D Math Primer for Graphics and Game Development》讀書筆記1
本文是《3D Math Primer for Graphics and Game Development》第一版的讀書筆記。第二版貌似還沒有中文版。
本書網站gamemath.com。中文版居然給了翻譯公司的網址,而且里面還什么有用的都沒有,囧。
第2章 笛卡爾坐標系統
左手坐標系的記憶方法
伸出左手,手指依次是(1)大拇指、(2)食指、(3)中指;坐標軸按字母表依次是(1)X軸、(2)Y軸、(3)Z軸。他們分別對應起來,用左手擺成下圖的樣子(不錯的pose啊),就是左手坐標系。
右手坐標系的記憶方法
同上,用右手就行了。
約定俗成和習慣
傳統的計算機圖形學使用左手坐標系,線性代數則傾向於右手坐標系。
兩種坐標系沒有優劣之分,只是使用習慣不同。
本書使用左手坐標系。
第3章 多坐標系
攝像機坐標系
本書約定的攝像機坐標系,攝像機在原點,X軸向右,Z軸向前,Y軸向上,如下圖所示。
許多圖形學書中習慣使用右手系,Z軸向外,即從屏幕指向讀者。
坐標系變換
坐標系變換的意思:知道某一點P在坐標系A中的坐標,如何獲取P在另一坐標系B中的坐標?
只需在坐標系A中定位坐標系B(描述B的原點和軸在A中的值)。后文會詳述。
包圍盒
向量軸對齊包圍盒是axially aligned bounding box(AABB)的翻譯。我就是覺得包圍盒這個翻譯很不錯。
第4章 向量
相對位置
"50英里每小時的速度向北"能用向量表示。
向量能描述的是相對位置。相對位置的想法是很直接的:某個物體的位置,能通過描述它與已知點之間的相對關系來指明。
由此引出一個問題,這些"已知"點在哪兒?什么是"絕對"位置?令人吃驚的是不存在這樣的東西。因為在描述一個點的位置時,總要描述它和其它一些點的關系。這就沒完沒了了。
既然"已知"點不存在,那么如何通過所謂的"已知"點來描述位置呢?我的思路是:假設存在一個已知點的位置。假設已經找到了一個已知點,這樣就不必無限地去追溯"已知"點了。
相對論的一個重要觀點就是不存在絕對參考系。
第5章 向量運算
向量和點的關系
向量[x, y]描述了原點到點(x, y)的位移量。
向量和點在概念上不同,而在數學上等價。
等價是什么意思?等價就是兩者存在一一對應的關系。一一對應是什么意思?就是即使你和我毫不相干,但是你有一個什么東西,我就有一個相應的什么東西;反過來也一樣。比如你在照鏡子,你看到鏡子里的人臉上粘個米粒,就知道自己什么情況了。
向量投影
給定兩個向量v和n,可以把v分成兩部分:v||和v⊥。它們分別平行和垂直於n, 並滿足v = v|| + v⊥。我們把平行分量v||稱作v在n上的投影。
投影的計算公式:
垂直分量的公式:
后文會有很多地方用到這兩個公式。總之你知道有這兩個公式存在就行了,需要的時候拿來用。畢竟不需要做數學家。
第6章 3D向量類
類接口
好的類設計首先要回答下列問題:"這個類將提供什么操作?"、"在哪些數據上執行這些操作?"
從這些代碼和設計思路中就可以感受到作者的認真態度和深厚功底。
設計決策
如果世界不超過1英里,那么32位的float類型就足夠,因為24位尾數能提供1/250英寸的精度。
如果世界超過200英里(321.8688千米),比如整個江蘇省,那么32位float就不夠了。
不存在Point3類
有了Vector3類,就不需要Point3類。避免重復代碼以及滿世界的向量與點的轉換。
關於優化
過早的優化是一切罪惡的根源。優化那些非瓶頸的代碼,使代碼復雜化,卻沒有得到相應的回報。
在過去,定點數是一種優化技術。當今的處理器已經可以快速處理浮點數,這個技術就不需要了。
不要為了2%的優化付出100%的代碼復雜性。
簡單點說,就是別優化,我的技術水平沒那么高。
第7章 矩陣
矩陣用來描述兩個坐標系間的關系,通過定義一種運算來將一個坐標系中的向量/點轉換到另一個坐標系中。換句話說,就是已知一個向量/點在坐標系A中的坐標,又知坐標系A和坐標系B的關系,求其在坐標系B中的坐標。
向量是標量的數組,矩陣是向量的數組。
矩陣的下標從1開始。
矩陣乘法
記r×n矩陣A與n×c矩陣B的乘積為C。C的任意元素Cij如下:
矩陣乘法設計成這樣,是因為有實際意義,數學上也有研究價值。或者說,正是因為它反映了現實世界的某些東西,才會有數學意義。
本書給了一種非常好的記憶方法:
我擴展了一下,可以將A的各個列拆開,同時將B的各個行拆開:
變成下圖所示的樣子:
可以看到,矩陣的乘法運算,可以把A的各列拆開,B的各行拆開,分別運算,最后相加。拆分時,只要A的列和B的行的拆分方式相同即可。
還有另一種拆法:把A的各行拆開,B的各列拆開,分別運算,最后拼起來。
而且,還可以同時進行這兩種拆分。這時,你可以看做先進行第一種拆分,然后進行第二種拆分,這樣(對我來說)比較容易理解。這樣就能理解矩陣分塊計算的原理。
約定和習慣
本書默認使用行向量進行與矩陣的運算。
DirectX使用的是行向量。
OpenGL使用的是列向量。
線性變換
線性變換保留了模型中原有的直線和平行線,原點也保持不動。長度、角度、體積可能會改變。從非技術意義上說,線性變換可能"拉伸"坐標系,但不會"彎曲"或"卷折"坐標系。
旋轉、縮放、投影、鏡像是線性變換。
仿射(線性變換后平移)不是線性變換。
方陣能描述線性變換。
矩陣運算和3D變換
設有一組基向量p0=(1, 0, 0),q0=(0, 1, 0),r0=(0, 0, 1),此時向量v=(x, y, z)就是v在p0=(1, 0, 0),q0=(0, 1, 0),r0=(0, 0, 1)這個坐標系里的坐標,即有v = xp0 + yq0 + zr0。
設p、q、r為任意一組基向量。例如p=(1, 0, 0),q=(0, 1, 0),r=(0, 0, 1)(互相垂直);或p=(0.8, 0.6, 0),q=(-0.6, 0.8, 0),r=(0, 0, 1) (互相垂直);或p=(1/√5)(2,-1,0),q= (1/√45)(2,4,5),r= (1/3)(1,2,-2) (互相垂直)或p=(1, 1, 1-√2 ),q=(1-√2, 1, 1),r=(1, 1-√2, 1)(並非互相垂直)……
然后,我們用p、q、r構造矩陣。沒什么理由,就這么做了。
來看看向量v=(x, y, z)乘以矩陣會出現什么?
從左邊到第一個等號右邊,說明這個乘法運算給v賦予了新的坐標值。廢話。
從第一個等號右邊到第二個等號右邊,說明這個新的坐標值與基向量p、q、r的關系等同於v與原始基向量p0=(1, 0, 0),q0=(0, 1, 0),r0=(0, 0, 1)的關系,這就是新坐標值的意義所在。
這也是矩陣乘法設計成這樣的價值所在。
所以,把3×3矩陣M的3行看做轉換后的3個基向量,那么任意一個1×3的行向量v乘以M,就得到了v轉換后的坐標。這就是說,乘以該矩陣就相當於執行了一次坐標轉換。若有aM=b,我們就可以說,M將a轉換到b。
如何建立需要的矩陣?
上一節說明了,3×3矩陣的每一行都能解釋為轉換后的基向量。初始坐標系中的向量/點經過轉換,得到了各自新的坐標值。那么,初始坐標系中的基向量p0=(1, 0, 0),q0=(0, 1, 0),r0=(0, 0, 1)經過轉換,得到的新坐標值是?
觀察這三個式子,你會發現新的基向量恰好組成了我們需要的轉換矩陣。
一般情況下,我們是不能預先得到轉換矩陣的。而三個式子就給出了獲取轉換矩陣的方法。就是說,只要我們能先用別的什么方法求出新的基向量的坐標值,就能拼出轉換矩陣,然后就可以從任意一個向量/點的初始坐標值得到其新的坐標值!
總之,矩陣等價於變換后的基向量。
2D示例
比如這個矩陣:
這個矩陣代表的變換是什么?
首先,從矩陣中抽出基向量p和q:
下圖展示了p0=(1, 0), q0=(0, 1)轉換到p,q后的樣子:
加個圖片會看起來更直觀:
“不幸的是,沒有人能告訴您矩陣像什么——您必須自己去感受。”