1. 基礎變換
1.1 平移

平移
1.2 旋轉

旋轉
1.3 縮放

縮放
1.4 組合變換
平移和旋轉

先旋轉再平移

先平移再旋轉
對比上面2個變換,我們可以發(fā)現:在組合變換中,變換的順序是不可以隨意修改的。
- 數學分析:這里分析2D變換。
//先旋轉再平移
┏ cosθ sinθ 0 ┓┏ 1 0 0 ┓
[X, Y, 1] = [x, y, 1]┃ -sinθ cosθ 0 ┃┃ 0 1 0 ┃
┗ 0 0 1 ┛┗ dx dy 1 ┛
//先平移再旋轉
┏ 1 0 0 ┓┏ cosθ sinθ 0 ┓
[X, Y, 1] = [x, y, 1]┃ 0 1 0 ┃┃ -sinθ cosθ 0 ┃
┗ dx dy 1 ┛┗ 0 0 1 ┛
這個的問題本質原因是矩陣的乘法不滿足交換律。
1.5 代碼示例

效果
- 先移動再旋轉
GLfloat xPos = 0.0f;
GLfloat yPos = 0.0f;
//紅色
GLfloat vRed[] = {1.0,0.0,0,1};
void RenderScene(void)
{
//清除屏幕、深度緩存區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//1.建立基于時間變化的動畫
static CStopWatch rotTimer;
//當前時間 * 60s
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
//2.矩陣變量
/*
mView: 平移
mModel: 旋轉
mModelView: 模型視圖
mModelViewProjection: 模型視圖投影MVP
*/
M3DMatrix44f mView, mModel, mModelView, mModelViewProjection;
//mModel旋轉矩陣,繞y軸旋轉yRot度
m3dRotationMatrix44(mModel, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
//mView平移矩陣,沿z軸移動-2.5
m3dTranslationMatrix44(mView, 0.0f, 0.0f, -2.5f);
//mModelview = mView * mModel
m3dMatrixMultiply44(mModelview, mView, mModel);
//mModelViewProjection = ProjectionMatrix * mView * mModel
m3dMatrixMultiply44(mModelViewProjection,
viewFrustum.GetProjectionMatrix(),
mModelview);
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
shaderManager.UseStockShader(GLT_SHADER_FLAT,
mModelViewProjection,
vBlack);
torusBatch.Draw();
glutSwapBuffers();
glutPostRedisplay();
}
2. 矩陣棧
我們在矩陣儲存時可能用到以下實例:
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrame cameraFrame;
GLFrame objectFrame;
其中,cameraFrame用于存儲觀察者矩陣,objectFrame用于存儲模型矩陣。projectionMatrix只用于存儲投影矩陣,我們操作最多的是modelViewMatrix。
- 模型矩陣繞世界坐標系y軸旋轉-5.0度
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
- 觀察者矩陣向后退15.0
//GLFrame中默認的朝向是z軸的負方向,即(0.0, 0.0, -1.0)
//向前走-15.0,即(0.0, 0.0, -1.0 * -15.0) = (0.0, 0.0, 15.0)
cameraFrame.MoveForward(-15.0f);
- 渲染過程中矩陣棧操作,獲取MVP矩陣的計算結果
//壓棧
modelViewMatrix.PushMatrix();
//獲取觀察者矩陣
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
//棧頂矩陣乘以傳入矩陣,相乘的結果簡存儲在棧頂
modelViewMatrix.MultMatrix(mCamera);
//獲取模型矩陣
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
//由于先乘的觀察者矩陣,現在再乘以模型矩陣
//棧頂 = M_view * M_model
modelViewMatrix.MultMatrix(mObjectFrame);
...
//由于初始化時傳入了,modelViewMatrix和projectionMatrix的引用
//transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
//下面的代碼會計算projectionMatrix * modelViewMatrix
//結合上面的代碼,等價于M_projection * M_view * M_model
transformPipeline.GetModelViewProjectionMatrix()
...
//出棧
modelViewMatrix.PopMatrix();
2.1 優(yōu)化矩陣棧操作

不同的實現方式
之前我們用cameraFrame和objectFrame來記錄相機和模型的變化,用到了兩個對象。但我們可以固定一個對象,變化另一個對象。
例如,之前的做法需要觀察者向后退,同時物體旋轉,把變化作用到了兩個物體上,所以用到了兩個矩陣分別記錄兩個物體的變化,最后再使用矩陣相乘把兩個變化合并起來。
如果固定一個物體,那么根據相對運動,就是把之前兩個物體的變化,相對地作用到另一個物體上,就可以少用一個矩陣了。
下面我們看看代碼如何實現:
-
目前的做法
void SetupRC() { ... // 相機向z軸正方向移動2.5 cameraFrame.MoveForward(-2.5f); } void RenderScene(void) { ... // 模型每次刷新時多轉-1.0度 objectFrame.RotateWorld(m3dDegToRad(-1.0), 0.0f, 1.0f, 0.0f); // 入棧 modelViewMatrix.PushMatrix(); // 獲取觀察者矩陣 M3DMatrix44f mCamera; cameraFrame.GetCameraMatrix(mCamera); modelViewMatrix.MultMatrix(mCamera); // 獲取模型矩陣 M3DMatrix44f mModelview; objectFrame.GetMatrix(mModelview); modelViewMatrix.MultMatrix(mModelview); ... // 出棧 modelViewMatrix.PopMatrix(); // 交換緩沖區(qū),并立即刷新 glutSwapBuffers(); glutPostRedisplay(); } -
只使用
objectFramevoid SetupRC() { ... // 相對運動 // 模型向z軸負方向移動2.5 objectFrame.MoveForward(2.5f); } void RenderScene(void) { ... // 模型每次刷新時多轉-1.0度 objectFrame.RotateWorld(m3dDegToRad(-1.0), 0.0f, 1.0f, 0.0f); // 入棧 modelViewMatrix.PushMatrix(); // 獲取模型矩陣 M3DMatrix44f mModelview; objectFrame.GetMatrix(mModelview); modelViewMatrix.MultMatrix(mModelview); ... // 出棧 modelViewMatrix.PopMatrix(); // 交換緩沖區(qū),并立即刷新 glutSwapBuffers(); glutPostRedisplay(); }又因為
GLMatrixStack的 PushMatrix(GLFrame& frame) 方法,可以一次解決多行代碼:void PushMatrix(GLFrame& frame) { M3DMatrix44f m; frame.GetMatrix(m); PushMatrix(m); }使用上面的API可以讓代碼更簡潔:
void SetupRC() { ... // 相對運動 // 模型向z軸負方向移動2.5 objectFrame.MoveForward(2.5f); } void RenderScene(void) { ... // 模型每次刷新時多轉-1.0度 objectFrame.RotateWorld(m3dDegToRad(-1.0), 0.0f, 1.0f, 0.0f); // 入棧模型矩陣 modelViewMatrix.MultMatrix(objectFrame); ... // 出棧 modelViewMatrix.PopMatrix(); // 交換緩沖區(qū),并立即刷新 glutSwapBuffers(); glutPostRedisplay(); }
2.1 矩陣棧過程

矩陣棧過程
上面就是矩陣棧的操作過程,其中:
- 入棧,是為了保存當前的矩陣棧狀態(tài)
- 出棧,是為了恢復入棧前的矩陣棧狀態(tài)
這個操作類似于iOS的Core Graphic中context的保存與恢復。
//保存
void CGContextSaveGState(CGContextRef c);
//恢復
void CGContextRestoreGState(CGContextRef c);