OpenGL学习之路(二)
1 引子
在上一篇讀書筆記中,我們對書本中給出的例子進行詳細的分析。首先是搭出一個框架;然后填充初始化函數,在初始化函數中向OpenGL提供頂點信息(緩沖區對象)和頂點屬性信息(頂點數組對象),并啟用頂點數組對象;最后填充繪制函數,首先清空顏色緩存,然后調用glDrawArray來繪制基本圖形。例子中使用的坐標都是二維坐標,所以畫出來的圖形是二維圖形(這里是兩個三角形),而我們知道OpenGL最主要是用來進行三維圖形的渲染的,所以有必要在學習OpenGL相關API之前對三維變換做一個簡要的介紹。其實這一部分應該屬于紅寶書中第五章的內容,這里我們將其提前了,在讀書筆記(二)就拿出來介紹——這是我們三維渲染的最基本的知識點,也是最關鍵的知識點,理解起來也有一定的難度。本次讀書筆記主要講述平移、旋轉、縮放變換的變換矩陣,投影變換將在下一篇讀書筆記再做記錄。本篇讀書筆記主要是自己對一些數學概念的理解和記錄,僅供參考,如有不同理解的,大家可以一起討論哈!
2 點、坐標系與向量
討論三維變換之前,得先了解點、向量和坐標系這些基本數學概念。這部分內容可能比較抽象,下面記錄的是我對這些概念的一些理解。
2.1 位置的相對性
在日常生活中,我們在向別人描述一個陌生的地方的時候,通常會選擇一個他熟悉的地方作為一個參考點。例如:我們向老外介紹河北的一座古城邯鄲,老外知道北京,我們就會說邯鄲在北京往西南走300km;如果老外知道石家莊,那我們也可以告訴他,邯鄲在石家莊往南走100km。這說明,位置是一個相對的概念,要描述一個位置,首先要選擇參考點;參考點的選擇是任意的,所選取的參考點不同,位置的描述也就不同。
在幾何中,位置用“點”這一概念來描述,即點是一個只有位置沒有大小的量。描述一個點和描述一個位置是一回事,剛才已經說了位置是一個相對的概念,所以首先就要用到參考點。我們以最簡單的一維數軸為例來說明描述點的位置,如下圖所示:
對于數軸上同一點$A$,要描述$A$點的位置,先要選取任意一個參考點,如果選擇的參考點是$O_1$,則$A$點在$O_1$點右邊$l_1$的地方;如果選擇的參考點是$O_2$,則$A$點在$O_2$點左邊$l_2$的地方。通過數軸和參考點,我們就將數軸上的幾何點用抽象的數字表達出來了。
2.2 坐標系與向量
從圖上可以看出,數軸上的點只能沿著數軸方向進行變化,即它是一維的。如果點在一個平面上或一個空間中變化,那么數軸這一工具是無法描述的。這時就要引入二維坐標系和三維坐標系來描述點的位置。介紹坐標系之前,首先介紹一下向量的概念。
在我們還是十七八歲學習高一幾何的時候,我們就已經接觸到了向量——既有大小,又有方向的量,用一個有向線段來表示。說白了,向量定義了一個方向、一個長度和一個單位長度。如上圖中,$O_1A$和$AO_2$就是兩個向量,大小分別為$l_1$和$l_2$,方向為水平向右。
一個平面上,有無數這樣的向量。但是關于向量,有一個非常重要的法則——使用平行四邊形法則來對任何一個向量進行分解。平行四邊形法則來自于物理學中力的分解與合成,后被引入數學中加以抽象來描述向量的分解與合成。所謂平行四邊形法則,指的是任何一個平面向量都可以用一個不共線的兩個向量表示。于是,平面中無數的向量就可以用兩個不共線的向量來表示。由這兩個向量及它們的公共起點構成的數學結構就是二維坐標系,用坐標系就可以描述二維平面上的任意點,當然也可以描述二維平面上的任意向量,這兩個向量就是線性代數中的基向量。我們知道,在數學中,向量是位置無關的(即自由向量),只要大小相等,方向相同的兩個向量就是同一個向量(這和物理學中的力不一樣)。所以要描述二維空間中的點,還需要一個參考點,于是就定義了這兩個向量的公共起點作為參考點——即我們熟知的坐標原點。坐標軸向量和坐標原點就構成了坐標系,可以用坐標系來描述其中的任何向量和任一點。
三維坐標系和二維坐標系是類似的,使用兩次平行四邊形法則,從而將任意一個三維向量表示為三個不共面的三維向量(基向量)來表示,這三個向量移到一起的公共起點定義為三維坐標系的坐標原點。二維和三維笛卡爾坐標系就是基向量垂直的二維和三維坐標系,也是應用最為廣泛的坐標系,也稱為平面直角坐標系和空間直角坐標系。
下面,我們來看看向量的表示方法。同樣在我們懵懵懂懂的青春歲月里,我們就已經知道向量有兩種表示方法:第一種是符號表示法,如$\mathbf{a}, \mathbf{b}$等;另一種是坐標表示法,這里對坐標表示做較詳細的說明。剛才已經說了,任意一個二維向量都可以用兩個不共線的向量來表示,假設兩個基向量為$\mathbf{i}$和$\mathbf{j}$,且長度為1。則對任一個向量$\mathbf{a}=x_a\mathbf{i} + y_a\mathbf{j}$,這樣,向量$\mathbf{a}$可用一個有序對$(x_a, y_a)$來唯一表示,這就是向量的坐標表示。三維乃至$N$維向量的坐標表示都是一樣的。在這里,博主還是想強調一下,向量的坐標并不是該向量在坐標軸上的投影,只有笛卡爾坐標是向量在基向量上的投影。所以,在普通坐標系下,一個向量的坐標不是很好求,但在直角坐標系下,就變得很好求了——求投影,這也是笛卡爾坐標系應用的如此廣泛的原因。下面我們來看看,什么是投影,其實高一數學中也已經接觸到了,如下圖所示:
假設$c$為向量$\mathbf{a}$在向量$\mathbf{b}$上的投影,那么:
\begin{equation} c = \mathbf{a} \cos<\mathbf{a},?\mathbf{b}>\end{equation}
所以,在二維直角坐標系中,如果二維向量$\mathbf{a}$長度為$l$,該向量與$x$軸和$y$軸的夾角分別為$\alpha$和$\beta$,則我們很容易得到該向量的坐標表示為$\mathbf = (l\cos\alpha, l\cos\beta)^\text{T}$;同樣地,對三維空間向量$\mathbf{b}$,其長度為$L$,與$x$軸、$y$軸和$z$軸的夾角分別為$\alpha$、$\beta$和$\gamma$,則其坐標表示為$\mathbf{b}=(L\cos\alpha, L\cos\beta, L\cos\gamma)^\text{T}$。
2.3 點的表示
剛才我們定義了坐標系——坐標原點和三個不共面的向量組成,并且三維空間中的任意向量都可以由這三個向量唯一表示,但我們沒有講點怎么由坐標系來定義。設在三維笛卡爾坐標系中,坐標原點為$O$,三個基向量分別為$\mathbf{i}$,$\mathbf{j}$,$\mathbf{k}$,我們要求$P$點的坐標,那么
$$\vec{OP} = x_{1}\mathbf{i} + y_{1}\mathbf{j} + z_{1}\mathbf{k}$$
于是,點$P$可以表示為
$$P = x_{1}\mathbf{i} + y_{1}\mathbf{j} + z_{1}\mathbf{k} + \mathbf{O}$$
所以,要想表示一個三維的點,可以用四維坐標來表示,例如剛才的$P$可以表示為$P = (\begin{array}{cccc} x_1 & y_1 & z_1 & 1\end{array})$,這就是齊次坐標。對頂點來說,齊次坐標才是其真正的表示方式。向量可以表示為$\mathbf{v} = (\begin{array}{cccc} x_1 & y_1 & z_1 & 0\end{array})$。
3 線性變換與齊次坐標
3.1 概述
代數中的線性變換的概念很抽象,涉及到向量空間、線性映射之類的概念,在這里不做過多解釋,如下想了解可以度娘或必應。給一個通俗點的解釋,三維線性變換就是將點/向量的坐標值做一個運算,使其坐標值發生改變,這在幾何中的反映就是幾何體的形狀被改變了。在計算機圖形學中,線性變換一般是指平移、旋轉、縮放、投影(正交投影和透視投影)以及這些基本變換的綜合運算。通過剛才的描述,我們知道一下幾點信息:幾何中的點或向量由四個坐標值確定,而坐標值是由坐標系確定的,坐標系又是由三個不共面的向量和坐標原點構成。也就是說,對于同一點,在不同的坐標系下,描述它的坐標值是不一樣的,而變換就是建立這兩種不同描述之間的聯系——所以在以前我們稱之為坐標變換。例如:在坐標系$\mathbf{O}_1-\mathbf{i}_1\mathbf{j}_1\mathbf{k}_1$坐標系下,某一點可以描述為$P$點可以用四元祖$(x_1, y_1, z_1, o_1)$描述,
\begin{equation}\label{p2}P = \begin{array}{cccc} (x_1 & y_1 & z_1 & o_1)\end{array}\left(\begin{array}{c}\mathbf{i}_1 \\ \mathbf{j}_1 \\ \mathbf{k}_1 \\ \mathbf{O}_1\end{array}\right) = (\begin{array}{cccc} \mathbf{i}_1 & \mathbf{j}_1 & \mathbf{k}_1 & \mathbf{o_1}\end{array})\left(\begin{array}{c}x_1 \\ y_1 \\ z_1 \\ o_1 \end{array}\right)\end{equation}
在另一個坐標系為$\mathbf{O}_2-\mathbf{i}_2\mathbf{j}_2\mathbf{k}_2$,可以用另一個有序元組描述它,設為$(x_2, y_2, z_2, o_2)$
\begin{equation}\label{p1}P = \begin{array}{cccc} (x_2 & y_2 & z_2 & o_2)\end{array}\left(\begin{array}{c}\mathbf{i}_2 \\ \mathbf{j}_2 \\ \mathbf{k}_2 \\ \mathbf{O}_2\end{array}\right) = (\begin{array}{cccc} \mathbf{i}_2 & \mathbf{j}_2 & \mathbf{k}_2 & \mathbf{O_2}\end{array})\left(\begin{array}{c}x_2 \\ y_2 \\ z_2 \\ o_2 \end{array}\right)\end{equation}
那么怎么建立$(\ref{p1})$和$(\ref{p2})$之間的聯系呢?還是之前我們說的,任意一個三維向量都可以表示用三個不共面的向量表示,所以$\mathbf{i}_2, \mathbf{j}_2,?\mathbf{k}_2$可以用$\mathbf{i}_1,?\mathbf{j}_1,?\mathbf{k}_1$線性表出:
$$\mathbf{i}_2 = T_{11} \mathbf{i}_1 + T_{21} \mathbf{j}_1 + T_{31} \mathbf{k}_1 + 0 \mathbf{O}_1$$
$$\mathbf{j}_2 = T_{12} \mathbf{i}_1 + T_{22} \mathbf{j}_1 + T_{32} \mathbf{k}_1 + 0 \mathbf{O}_1$$
$$\mathbf{k}_2 = T_{13} \mathbf{i}_1 + T_{23} \mathbf{j}_1 + T_{33} \mathbf{k}_1 + 0 \mathbf{O}_1$$
$$\mathbf{O}_2 = T_{14} \mathbf{i}_1 + T_{24} \mathbf{j}_1 + T_{34} \mathbf{k}_1 + T_{44} \mathbf{O}_1$$
?即:
$$(\begin{array}{cccc}\mathbf{i}_2 & \mathbf{j}_2 & \mathbf{k}_2 & \mathbf{O}_2 \end{array}) = (\begin{array}{cccc}\mathbf{i}_1 & \mathbf{j}_1 & \mathbf{k}_1 & \mathbf{O}_1 \end{array})\left(\begin{array}{cccc}T_{11} & T_{12} & T_{13} & T_{14} \\T_{21} & T_{22} & T_{23} & T_{24}\\T_{31} & T_{32} & T_{33} & T_{34}\\0 & 0 &0& T_{44}\end{array}\right)$$
于是,我們就可以寫出從$(\begin{array}{cccc}x_1 & y_1 & z_1 & o_1 \end{array})^{\text{T}}$變換到$(\begin{array}{cccc}x_2 & y_2 & z_2 & o_2 \end{array})^{\text{T}}$的變換表達式為:
$$\left(\begin{array}{c}x_2 \\ y_2 \\ z_2 \\ o_2 \end{array}\right) = (\begin{array}{cccc}\mathbf{i}_1 & \mathbf{j}_1 & \mathbf{k}_1 & \mathbf{O}_1 \end{array})\left(\begin{array}{cccc}T_{11} & T_{12} & T_{13} & T_{14} \\T_{21} & T_{22} & T_{23} & T_{24}\\T_{31} & T_{32} & T_{33} & T_{34}\\0.0 & 0.0 &0.0& T_{44}\end{array}\right)\left(\begin{array}{c}x_1 \\ y_1 \\ z_1 \\ o_1 \end{array}\right)?$$
其中,將
$$T=\left(\begin{array}{cccc}T_{11} & T_{12} & T_{13} & T_{14} \\T_{21} & T_{22} & T_{23} & T_{24}\\T_{31} & T_{32} & T_{33} & T_{34}\\0.0 & 0.0 &0.0& T_{44}\end{array}\right)$$
稱為坐標變換矩陣。接下來主要就是講解怎么求基本的坐標變換(仿射變換)矩陣。
3.2 縮放
縮放應該是所有線性變換中最簡單的變換了。執行縮放操作,例如將一個向量縮放為原來的$s$倍,相當于原點不變,$x$、$y$、$z$三個坐標軸縮放為原來的$s$倍。根據3.1介紹的,縮放操作的變換矩陣為:
$$T_s = \left(\begin{array}{cccc}s & 0 & 0 & 0 \\ 0 & s & 0 & 0 \\ 0 & 0 & s & 0 \\ 0 & 0 & 0 & 1\end{array}\right)$$
3.3 平移
所謂平移,就是在坐標系中的三個坐標軸保持不變,原點沿著平移向量移動到新位置。假設平移向量為$v_p = (\begin{array}{cccc}x_p & y_p & z_p & 0 \end{array})$同樣,根據可以得到,平移操作的變換矩陣為:
$$T_p = \left(\begin{array}{cccc}1 & 0 & 0 & x_p \\ 0 & 1 & 0 & y_p \\ 0 & 0 & 1 & z_p \\ 0 & 0 & 0 & 1\end{array}\right)$$
3.4 旋轉
最后來推導最難的旋轉變換矩陣。與平移、旋轉矩陣的不同,旋轉矩陣就不那么直觀了。下面,我們來具體看一下旋轉矩陣的推導,這個推導是執行三次向量的平行四邊形法則進行分解得到,整個分解過程如下圖所示:
三次分解由不同的顏色表示出來了,分別是紅色、淺藍色和紫色。
已知條件:$\mathbf{i}$、$\mathbf{j}$和$\mathbf{k}$是三維笛卡爾坐標系的基向量,原點為$O$,旋轉軸為$\mathbf{u}$,也是單位向量,向量$\mathbf{i'}$為$x$方向的基向量$\mathbf{i}$繞旋轉軸$\mathbf{u}$旋轉$\theta$后的新向量——旋轉后坐標系$x$軸的基向量。
我們的目的:將向量$\mathbf{i'}$用基向量$\mathbf{i}$、$\mathbf{j}$和$\mathbf{k}$表示出來。
第一步向量分解:將$\mathbf{i'}$分解為沿著旋轉軸$\mathbf{u}$的向量$\vec{OA}$和垂直于$\mathbf{u}$的向量$\vec{OB}$,則:
\begin{equation}\label{341}\mathbf{i'} = \vec{OA} + \vec{OB}\end{equation}
且:
\begin{equation}\label{342}\vec{OA} = u_x \mathbf{u} = u_x^2\mathbf{i} + u_xu_y\mathbf{j} + u_xu_z\mathbf{k}\end{equation}
第二步向量分解:將$\mathbf{i}$分解為沿著旋轉軸$\mathbf{u}$的向量$\vec{OA}$和垂直于$\mathbf{u}$的向量$\vec{OC}$,則
$$\mathbf{i} = \vec{OA} + \vec{OC}$$
且:
\begin{equation}\label{344}\vec{OC} = \mathbf{i} - u_x\mathbf{u} = (1-u_x^2)\mathbf{i} - u_xu_y\mathbf{j} - u_xu_z\mathbf{k} \end{equation}
第三步向量分解:建立$\vec{OB}$與$\vec{OC}$之間的聯系,將向量$\vec{OB}$分解為沿著$\vec{OC}$方向的向量$\vec{OD}$和垂直于$\vec{OB}$的向量$\vec{OE}$,則
\begin{equation}\label{345}\vec{OB} = \vec{OD} + \vec{OE}\end{equation}
根據$\ref{344}$可得:
\begin{equation}\label{346}\vec{OD} = |\vec{OB}|\cos\theta\frac{\vec{OC}}{|\vec{OC}|} = \cos\theta?\vec{OC} = \cos\theta(\mathbf{i} - u_x\mathbf{u}) = (1-u_x^2)\cos\theta \mathbf{i} - u_xu_y\cos\theta \mathbf{j} -u_xu_z\cos\theta \mathbf{k} \end{equation}
另外,注意到$\vec{OE}$和$\vec{OC}$垂直,$\mathbf{u}$是旋轉軸,則$\mathbf{u}$與平面$OEBD$垂直,所以$\mathbf{u}$與$OE$垂直,則$\vec{OE}$在向量$\mathbf{u}$和向量$\vec{OC}$的叉乘向量上,假設 $\vec{OF} = \mathbf{u}?\times?\vec{OC}$,于是:
$$\vec{OE} = k\vec{OF} = k\mathbf{u}\times \vec{OC}=k\mathbf{u}\times (\mathbf{i} - u_x\mathbf{u}) = k \mathbf{u}\times \mathbf{i}$$
所以現在求出$k$就可以了,由叉乘定義:$|\vec{OF}| = |\mathbf{u}||\vec{OC}|sin(90) = |\vec{OC}| = |\vec{OB}|$,所以:$k=\sin\theta$,最后得到
\begin{equation}\label{349}\vec{OE}=\sin\theta \mathbf{u}\times (\mathbf{i} - u_x\mathbf{u}) = \sin\theta \left(\begin{array} \mathbf{i} & \mathbf{j} & \mathbf{k} \\ u_x & u_y & u_z \\ 1 & 0 & 0 \end{array}\right) = ?u_z\sin\theta\mathbf{j} -u_y\sin\theta\mathbf{k}\end{equation}
將$(\ref{346})$和$(\ref{349})$代入$(\ref{345})$得:
\begin{equation}\label{3410}\vec{OB} = (1-u_x^2)\cos\theta\mathbf{i} - (u_xu_y\cos\theta - u_z\sin\theta)\mathbf{j} - (u_xu_z\cos\theta + u_y\sin\theta)\mathbf{k} \end{equation}
最后,將$(\ref{342})$和$(\ref{3410})$代入$(\ref{341})$可得
$$\mathbf{i'} = (\cos\theta + u_x^2(1-\cos\theta))\mathbf{i} + (u_xu_y(1-cos\theta) + u_z\sin\theta)\mathbf{j} + (u_xu_z(1-\cos\theta) - u_y\sin\theta)\mathbf{k} + 0\mathbf{O}$$
其余兩個變換后的基向量$\mathbf{i'}$和$\mathbf{j'}$也可以由$\mathbf{i}$、$\mathbf{j}$和$\mathbf{k}$表示出來,最終得到齊次旋轉矩陣為
$$M_r = \left(\begin{array}{cccc}\cos\theta+u_x^2(1-\cos\theta)& u_xu_y(1-\cos\theta)-u_z\sin\theta & u_xu_z(1-\cos\theta+u_y\sin\theta & 0 \\ u_yu_x(1-\cos\theta)+u_z\sin\theta & \cos\theta+u_y^2(1-\cos\theta)?& u_yu_z(1-\cos\theta)-u_x\sin\theta & 0 \\ u_zu_x(1-\cos\theta)-u_y\sin\theta & u_zu_y(1-\cos\theta)+u_x\sin\theta & \cos\theta+u_z^2(1-\cos\theta) & 0 \\ 0 & 0 & 0 & 1\end{array}\right)$$
4 總結
最后總結一下,在這篇博文中我們講述了點及其相對性,接著介紹了向量的概念,由平行四邊形法則引出坐標系的概念,然后介紹了點在坐標系下的表示,最后介紹了坐標變換和變換矩陣的概念,給出了三種基本變換——平移變換、旋轉變換和縮放變換的變換矩陣。這些矩陣綜合運用,就構成了三維空間中復雜的變換了,三維變換是三維圖形繪制的基礎,也是學習OpenGL時較難理解的知識點之一。
轉載于:https://www.cnblogs.com/lijihong/p/5396819.html
總結
以上是生活随笔為你收集整理的OpenGL学习之路(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 调DHT22
- 下一篇: install kinect drive