矩陣
矩陣是一種功能非常強(qiáng)大的數(shù)學(xué)工具,它大大簡化了了求解變量之間有復(fù)雜關(guān)系的方程式或者方程組的過程。矩陣在坐標(biāo)變換中運(yùn)用得非常廣泛。例如:如果在空間有一個點,由x,y和z坐標(biāo)定義,將它圍繞任意點沿任意方向旋轉(zhuǎn)一定的角度后,我們需要知道這個點現(xiàn)在的位置,就要用到矩陣。因為新的x坐標(biāo)不僅與原來的x坐標(biāo)和其他旋轉(zhuǎn)參數(shù)有關(guān),還與y和z坐標(biāo)值有關(guān)。這種變量與解之間的相關(guān)性就是矩陣最擅長解決的問題。-
基礎(chǔ)變換
由于我們的屏幕是2D的,在把3D圖形數(shù)據(jù)顯示在2D屏幕上的這個過程中,我們需要對頂點數(shù)據(jù)進(jìn)行幾何變換:視圖變換、模型變換和投影變換。變 換 應(yīng) ? 視圖 指定觀察者位置 模型 在場景中移動物體 模型視圖 描述視圖/模型變換的?二元性 投影 改變視景體?大?小和設(shè)置它的投影?方式 視口 偽變化,對窗?口上最終輸出進(jìn)?行行縮放 -
模型視圖矩陣
模型視圖矩陣是一個4x4矩陣,它表示一個變換后的坐標(biāo)系,我們可以用來放置對象和確定對象的方向。我們?yōu)閳D元提供的頂點將作為一個單列矩陣的形式來使用,并乘以一個模型視圖矩陣來獲得一個相對于視覺坐標(biāo)系的經(jīng)過變換的新坐標(biāo)。- 我們可以調(diào)用math3d庫中
m3dTranslationMatrix44函數(shù)來使用變換矩陣void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z) - 旋轉(zhuǎn)
void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z); - 縮放
void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale) - 綜合變換:將兩個矩陣相乘并返回結(jié)果
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
提示:如果不知道參數(shù)是什么意思,可以進(jìn)入看源碼,非常的清晰。
- 我們可以調(diào)用math3d庫中
-
投影矩陣
投影分為正投影和透視投影。- 正投影會產(chǎn)生一個平行投影,這種投影在繪制從遠(yuǎn)處觀察不產(chǎn)生任何透視縮短的特定物體時非常有用。
// 用這個函數(shù)設(shè)置正投影 GLFrustum::SetOrthographic(GLfloat xMin, GLfloat xMax, GLfloat yMin, GLfloat yMax, GLfloat zMin, GLfloat zMax); - 透視投影會有一個3D效果,看起來更加逼真,渲染3D圖形時常用透視投影。
// 用這個函數(shù)設(shè)置透視投影 GLFrustum::SetPerspective(float fFov, float fAspect, float fNear, float fFar);
- 正投影會產(chǎn)生一個平行投影,這種投影在繪制從遠(yuǎn)處觀察不產(chǎn)生任何透視縮短的特定物體時非常有用。
-
頂點變換管線
原始頂點數(shù)據(jù)到顯示在窗口上的變化過程,這個過程都是用矩陣記錄每一次放射變換,然后應(yīng)用到頂點數(shù)據(jù)上。
屏幕快照 2019-05-21 22.43.51.png
- 使用矩陣?yán)?/li>
// GLTool.h頭文件包含了大部分GLTool中類似C語言的獨(dú)立函數(shù)
#include "GLTools.h"
// 矩陣?工具類,?用來快速設(shè)置正/透視投影
#include "GLFrustum.h"
// 三?角形批次類,幫助類,利利?用它可以傳輸頂點/光照/紋理理/顏?色數(shù)據(jù)到存儲著?色器?中
#include "GLBatch.h"
#include "StopWatch.h"
// 數(shù)學(xué)庫
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
// 設(shè)置圖元繪制時的投影方式
GLFrustum viewFrustum;
// 存儲著?色器?管理理?工具類.
GLShaderManager shaderManager;
GLTriangleBatch torusBatch;
// 設(shè)置視口和投影矩陣
void ChangeSize(int w, int h)
{
//防止除以零
if(h == 0)
h = 1;
//將視口設(shè)置為窗口尺寸
glViewport(0, 0, w, h);
//設(shè)置透視投影
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 1000.0f);
}
//召喚場景
void RenderScene(void)
{
//清除屏幕、深度緩存區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//1.建立基于時間變化的動畫
static CStopWatch rotTimer;
//當(dāng)前時間 * 60s
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
//2.矩陣變量
/*
mTranslate: 平移
mRotate: 旋轉(zhuǎn)
mModelview: 模型視圖
mModelViewProjection: 模型視圖投影MVP
*/
M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection;
//創(chuàng)建一個4*4矩陣變量,將花托沿著Z軸負(fù)方向移動2.5個單位長度
m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f);
//創(chuàng)建一個4*4矩陣變量,將花托在Y軸上渲染yRot度,yRot根據(jù)經(jīng)過時間設(shè)置動畫幀率
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
//為mModerView 通過矩陣旋轉(zhuǎn)矩陣、移動矩陣相乘,將結(jié)果添加到mModerView上
m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
// 將投影矩陣乘以模型視圖矩陣,將變化結(jié)果通過矩陣乘法應(yīng)用到mModelViewProjection矩陣上
//注意順序: 投影 * 模型 != 模型 * 投影
m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(),mModelview);
//繪圖顏色
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
//通過平面著色器提交矩陣,和顏色。
shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vBlack);
//開始繪圖
torusBatch.Draw();
// 交換緩沖區(qū),并立即刷新
glutSwapBuffers();
glutPostRedisplay();
}
void SetupRC()
{
//1.
glClearColor(0.8f, 0.8f, 0.8f, 1.0f );
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
//2.形成一個球
gltMakeSphere(torusBatch, 0.4f, 10, 20);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(800, 600);
glutCreateWindow("ModelViewProjection Example");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
-
矩陣堆棧介紹
1. 矩陣堆棧常用函數(shù)//這個類的構(gòu)造函數(shù)允許指定堆棧的最大深度,默認(rèn)的堆棧深度為64,這個矩陣堆棧在初始化時已經(jīng)在堆棧中包含了單元矩陣。 GLMatrixStack::GLMatrixStack(int iStackDepth = 64); //在堆棧頂部載?一個單元矩陣 void GLMatrixStack::LoadIdentity(void); //在堆棧頂部載入任何矩陣.參數(shù):4*4矩陣 void GLMatrixStack::LoadMatrix(const M3DMatrix44f m); //矩陣乘以矩陣堆棧頂部矩陣,相乘結(jié)果存儲到堆棧的頂部 void GLMatrixStack::MultMatrix(const M3DMatrix44f); //使用GetMatrix 函數(shù)獲取矩陣堆棧頂部的值,這個函數(shù)可以進(jìn)行兩次重載,以適應(yīng)GLShaderMananger的使?用,或者僅僅是獲取頂部矩陣的副本 const M3DMatrix44f & GLMatrixStack::GetMatrix(void); void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix);- 壓棧.出棧
一個矩陣的真正價值在于通過壓棧操作存儲一個狀態(tài),然后通過出?;謴?fù)這個狀態(tài)。
//將當(dāng)前矩陣壓?入堆棧 void GLMatrixStack::PushMatrix(void); //將M3DMatrix44f 矩陣對象壓入當(dāng)前矩陣堆棧 void PushMatrix(const M3DMatrix44f mMatrix); //將GLFame 對象壓入矩陣對象 void PushMatrix(GLFame &frame); //出棧(出棧指的是移除頂部的矩陣對象) void GLMatrixStack::PopMatrix(void);3.仿射變換
GLMatrixStack類也內(nèi)建了對創(chuàng)建旋轉(zhuǎn)、平移和縮放矩陣的支持。相應(yīng)的函數(shù)如下://Rotate 函數(shù)angle參數(shù)是傳遞的度數(shù),而不是弧度 void MatrixStack::Rotate(GLfloat angle,GLfloat x,GLfloat y,GLfloat z); void MatrixStack::Translate(GLfloat x,GLfloat y,GLfloat z); void MatrixStack::Scale(GLfloat x,GLfloat y,GLfloat z);這三個函數(shù)都可以創(chuàng)建相應(yīng)的矩陣,然后用這個矩陣乘以矩陣堆棧頂部的元素,實際上就是對當(dāng)前矩陣添加變換。
- 壓棧.出棧
矩陣堆棧的使用完整例子,代碼如下:
#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLMatrixStack.h"
#include "GLGeometryTransform.h"
#include "StopWatch.h"
#include <math.h>
#include <stdio.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
GLShaderManager shaderManager; // 著色器管理器
GLMatrixStack modelViewMatrix; // 模型視圖矩陣
GLMatrixStack projectionMatrix; // 投影矩陣
GLFrustum viewFrustum; // 視景體
GLGeometryTransform transformPipeline; // 幾何圖形變換管道
GLTriangleBatch torusBatch; // 花托批處理
//角色幀 照相機(jī)角色幀
GLFrame cameraFrame;
void SetupRC()
{
// 初始化著色器管理器
shaderManager.InitializeStockShaders();
//開啟深度測試
glEnable(GL_DEPTH_TEST);
//設(shè)置清屏顏色到顏色緩存區(qū)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//繪制甜甜圈
gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30);
}
// 屏幕更改大小或已初始化
void ChangeSize(int nWidth, int nHeight)
{
glViewport(0, 0, nWidth, nHeight);
// 創(chuàng)建投影矩陣,。
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
//并將其加載到投影矩陣堆棧上
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// 設(shè)置變換管道以使用兩個矩陣堆棧(變換矩陣modelViewMatrix ,投影矩陣projectionMatrix)
//初始化GLGeometryTransform 的實例transformPipeline.通過將它的內(nèi)部指針設(shè)置為模型視圖矩陣堆棧 和 投影矩陣堆棧實例,來完成初始化
//當(dāng)然這個操作也可以在SetupRC 函數(shù)中完成,但是在窗口大小改變時或者窗口創(chuàng)建時設(shè)置它們并沒有壞處。而且這樣可以一次性完成矩陣和管線的設(shè)置。
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
//進(jìn)行調(diào)用以繪制場景
void RenderScene(void)
{
static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
// 基于時間動畫
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
// 清除顏色緩存區(qū)和深度緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//**3、設(shè)置照相機(jī)矩陣
M3DMatrix44f mCamera;
//**3、從camraFrame中獲取一個4*4的矩陣;
cameraFrame.GetCameraMatrix(mCamera);
//**3、將照相機(jī)矩陣壓入模型視圖堆棧中
modelViewMatrix.PushMatrix(mCamera);
// 繪制旋轉(zhuǎn)甜甜圈
//modelViewMatrix 頂部矩陣沿著z軸移動2.5單位
modelViewMatrix.Translate(0.0f, 0.0f, -2.5f);
modelViewMatrix.PushMatrix();
//modelViewMatrix 頂部矩陣旋轉(zhuǎn)yRot度
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
//使用平面著色器 變換管道中的投影矩陣 和 變換矩陣 相乘的矩陣,指定甜甜圈顏色
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(),vTorusColor);
//開始繪制
torusBatch.Draw();
// 恢復(fù)modelViewMatrix矩陣,移除矩陣堆棧
//使用PopMatrix推出剛剛變換的矩陣,然后恢復(fù)到單位矩陣
modelViewMatrix.PopMatrix();
//**恢復(fù)矩陣** push跟pop一一對應(yīng),push幾次就要pop幾次。
modelViewMatrix.PopMatrix();
// 執(zhí)行緩存區(qū)交換
glutSwapBuffers();
// 告訴glut在顯示一遍
glutPostRedisplay();
}
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800,600);
glutCreateWindow("OpenGL SphereWorld");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
