目前在OpenGL中,矩陣的變換主要涉及兩種觀察方式:
- 觀察者不動(dòng),物體動(dòng)
- 觀察者動(dòng),物體不動(dòng)
兩種方式涉及步驟大致總結(jié)如下:
-
ChangeSize函數(shù)
設(shè)置投影方式,得到投影矩陣,并往矩陣堆棧中壓入一個(gè)單元矩陣(單元矩陣的壓入可省略)
//創(chuàng)建投影矩陣,并將它載入投影矩陣堆棧中
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//調(diào)用頂部載入單元矩陣
modelViewMatrix.LoadIdentity();
-
SetupRC函數(shù)
設(shè)置觀察者的位置 -
SpecialKeys函數(shù)
決定物體/觀察者圍繞旋轉(zhuǎn)的坐標(biāo)軸 -
RenderScene函數(shù)
完成矩陣的相應(yīng)變換:入棧、相乘、出棧
下面我們來分別說說兩種方式的區(qū)別
觀察者不動(dòng),物體動(dòng)

觀察者不動(dòng),物體動(dòng)
在05_OpenGL 點(diǎn)/線/線段/線環(huán)/金字塔/六邊形/圓柱的副本(觀察者不動(dòng),物體動(dòng)-objectFrame)的代碼中
- 設(shè)置觀察者的位置
==> GLFrame中默認(rèn)的方向是z軸的負(fù)方向 -- (0.0f, 0.0f, -1.0f)
==> 參數(shù):表示離屏幕之間的距離。 負(fù)數(shù),是往屏幕后面移動(dòng);正數(shù),往屏幕前面移動(dòng)
cameraFrame.MoveForward(-15.0f);
- 在SpecialKeys函數(shù)中,進(jìn)行旋轉(zhuǎn)是
物體
例如,按上鍵位,物體圍繞x軸旋轉(zhuǎn) -5度
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
- RenderScene函數(shù)中,同時(shí)涉及觀察者矩陣和物體矩陣的變換
因?yàn)橛^察者設(shè)置位置時(shí),往屏幕后面平移了15個(gè)像素點(diǎn),所以觀察者發(fā)生了變化
在我們選中特殊鍵位移動(dòng)圖形時(shí),物體進(jìn)行了旋轉(zhuǎn),所以物體也發(fā)生了變化
因此最后所需的mvp矩陣,需要按照壓棧cameraFrame- 壓棧objectFrame - 投影矩陣這個(gè)順序進(jìn)行矩陣變換,這個(gè)順序是OpenGL規(guī)定的,且順序是不能交換的,因?yàn)榫仃囅喑瞬粷M足交換律, 即矩陣A * 矩陣B != 矩陣B * 矩陣A
//壓棧
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
//矩陣乘以矩陣堆棧的頂部矩陣,相乘的結(jié)果隨后簡(jiǎn)存儲(chǔ)在堆棧的頂部
modelViewMatrix.MultMatrix(mCamera);
M3DMatrix44f mObjectFrame;
//只要使用 GetMatrix 函數(shù)就可以獲取矩陣堆棧頂部的值,這個(gè)函數(shù)可以進(jìn)行2次重載。用來使用GLShaderManager 的使用。或者是獲取頂部矩陣的頂點(diǎn)副本數(shù)據(jù)
objectFrame.GetMatrix(mObjectFrame);
//矩陣乘以矩陣堆棧的頂部矩陣,相乘的結(jié)果隨后簡(jiǎn)存儲(chǔ)在堆棧的頂部
modelViewMatrix.MultMatrix(mObjectFrame);
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
其中矩陣堆棧的變化過程如下:

矩陣堆棧的變化過程
-
PushMatrix()函數(shù),根據(jù)源碼分析可知:在沒有任何參數(shù)的情況下,矩陣堆棧會(huì)將棧頂單元矩陣拷貝一份,并將拷貝的單元矩陣入棧
PushMatrix函數(shù)源碼
觀察者動(dòng),物體不動(dòng)

觀察者動(dòng),物體不動(dòng)
在Demo05_OpenGL 點(diǎn)/線/線段/線環(huán)/金字塔/六邊形/圓柱的副本(觀察者動(dòng),物體不動(dòng)-cameraFrame)代碼中
- 設(shè)置觀察者的位置
此時(shí)的objectFrame是指觀察者,并不是物體
objectFrame.MoveForward(15.0f);
- 在SpecialKeys函數(shù)中,進(jìn)行旋轉(zhuǎn)是
觀察者
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
- RenderScene函數(shù)中,僅涉及觀察者的矩陣變換
==> 設(shè)置觀察者位置時(shí),觀察者平移了15個(gè)像素點(diǎn)
==> 特殊鍵位觸發(fā)時(shí),觀察者繞著相應(yīng)的軸旋轉(zhuǎn)了一定的弧度
==> 所以objectFrame不僅記錄了往前移動(dòng)的狀態(tài),還記錄了旋轉(zhuǎn)的轉(zhuǎn)臺(tái),所以只需要將objectFrame載入矩陣堆棧即可, 即 把往前的變化、旋轉(zhuǎn)的變化都 放入 模型矩陣堆棧中
modelViewMatrix.PushMatrix(objectFrame);
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
其中矩陣堆棧的變化過程如下:

矩陣堆棧的變化過程
觀察者矩陣中包含了觀察者的所有變換,會(huì)直接在變換管道中完成矩陣相乘,最后通過變換管道得到mvp矩陣。
總結(jié)
其實(shí)選擇哪種觀察方式,還是需要結(jié)合需求,個(gè)人建議,哪種方便用哪個(gè)
- 在觀察者不動(dòng),物體動(dòng)時(shí),觀察者也并非是一動(dòng)不動(dòng),需要調(diào)整一個(gè)好的觀察位置
- 觀察者動(dòng),主要?jiǎng)拥氖荲_local,其實(shí)就是通過
moveForward方法設(shè)置的觀察者位置 - 物體動(dòng),主要?jiǎng)拥氖莔vp
疑問
- 在觀察者不動(dòng),物體動(dòng)時(shí),為什么不能直接這樣寫
modelViewMatrix.PushMatrix(cameraFrame)?
我們從PushMatrix(cameraFrame)的源碼分析可知,如圖所示,傳入push函數(shù)的frame在 PushMatrix方法中獲取的是GetMatrix,而我們需要的是GetCameraMatrix,所以不能直接將cameraFrame直接push到矩陣堆棧中

源碼分析
``
