OpenGL超級寶典筆記(一)數學基礎與基礎變換
建立更多圖形
批量三角形
GlTriangleBatch 可以塞進去多個三角形,用處是把重複的點剔除掉,壞處是當三角形加了太多了之後每次新增都會更慢一些。 幾個關鍵的api如下
GLTriangleBatch b;
b.BeginMesh(200); // 200個頂點
b.AddTriangle(M3DVector3f verts[3], M3DVector3f vNorms[3], M3DVector3f vTexCoords[3]);
b.End();
b.Draw();
e.g.
建立一個不斷轉動的甜甜圈。
首先看ChangeSize。
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
g_viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 100.0f);
// 壓棧投影矩陣到投影矩陣棧
g_projectionMatrix.LoadMatrix(g_viewFrustum.GetProjectionMatrix());
// 這是一個渲染管線,需要一個modelMatrix和一個投影矩陣
g_transformPipeline.SetMatrixStacks(g_modelViewMatrix, g_projectionMatrix);
}
frustum翻譯為視體,Perspective則代表著透視。這個方法的意義是讓當前的矩陣去乘以一個透視矩陣。
void SetPerspective(float fFov, float fAspect, float fNear, float fFar);
設定一個透視矩陣。 讓projection(投影)矩陣從frustum那裡拿到一個矩陣。 g_transformPipeline的型別是GLGeometryTransform,中文是幾何變換。 所以就是繫結矩陣的作用,一個model的矩陣,一個透視的矩陣。
看一下SetupRC。
void SetupRC()
{
glClearColor(0.91f , 0.97f, 0.96f, 1.0f);
// OpenGL必須要有shader才能著色
g_shaderManager.InitializeStockShaders();
gltMakeTorus(g_torusBatch, 0.4f, 0.15f, 30, 30);
g_floorBatch.Begin(GL_LINES, 324);
for (GLfloat x = -20.0f; x <= 20.0f; x += 0.5f)
{
g_floorBatch.Vertex3f(x, -0.55f, 20.0f);
g_floorBatch.Vertex3f(x, -0.55f, -20.0f);
g_floorBatch.Vertex3f(20.0f, -0.55f, x);
g_floorBatch.Vertex3f(-20.0f, -0.55f, x);
}
g_floorBatch.End();
}
兩個注意點: - 傳給torus的batch不需要add任何定點,一個空的batch足矣。 - 地板的線要研究一下怎麼弄的,應該就是普通的網格而已
在這裡說一下opengl的座標系:
renderScene
void RenderScene()
{
std::cout << "RenderScene" << std::endl;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
static GLfloat vFloorColor[] = { 0, 1.0f, 0, 1.0f };
static GLfloat vTorusColor[] = { 1.0f, 0, 0, 1.0f };
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.f;
// 複製棧頂(單位矩陣)到棧頂,避免破壞資料
g_modelViewMatrix.PushMatrix();
g_shaderManager.UseStockShader(GLT_SHADER_FLAT, g_transformPipeline.GetModelViewProjectionMatrix()
,vFloorColor);
g_floorBatch.Draw();
// 把matrix旋轉某個角度,再在上面畫東西
g_modelViewMatrix.Translate(0, 0, -2.5f);
g_modelViewMatrix.Rotate(yRot, 0, 1.0f, 0);
// 這個時候管線裡面的modelMatrix已經被改變了
// 就是把渲染管道中的兩個矩陣相乘之後返回出來
g_shaderManager.UseStockShader(GLT_SHADER_FLAT, g_transformPipeline.GetModelViewProjectionMatrix()
, vTorusColor);
g_torusBatch.Draw();
// 丟棄棧頂,恢復成單位矩陣,以便下一幀可以用
g_modelViewMatrix.PopMatrix();
glutSwapBuffers();
glutPostRedisplay();
}
先看一下timer是啥。這就是返回一個已流逝的時間,然後乘以60,說明速度是60。 接下來進入modelMatrix的上下文,然後做位移,旋轉,然後在這之上繪製model,最後退出上下文。 用的shader是flatshader,引數是轉換器返回的已投影變換的model矩陣,以及顏色。 最後這句很關鍵了,就繼續再繪製一次,相當於無限render。
過程相當於: 1. 建立transformer,繫結一個modelMatrix和一個投影matrix 2. 渲染的時候在matrix的上下文下旋轉modelMatrix,然後畫圖 3. 2說的畫圖用的是flat的shader,引數是經過model * modelMatrix * projection變換之後的矩陣
換個寫法是這樣的:
M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection;
// 位移矩陣
m3dTranslationMatrix44(mTranslate, 0, 0, -2.5f);
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0, 1.0f, 0);
// 把model層做位移和轉動變換
m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
// 將投影矩陣和model矩陣相乘,結果返回在mModelViewProjection中
m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(), mModelview);
GLfloat vBlack[] = {0, 0, 0, 0};
// 所以最終的目的就是把變換矩陣傳給shader了
shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vBlack);
torusBatch.Draw();
glutSwapBuffers();
glutPostRedisplay();
上面的乘法要記得順序就是。
變換管線
首先是每個點都是一個1x4矩陣,w是縮放因子。要先拿去乘以模型檢視矩陣,然後乘以投影矩陣,然後再去裁剪啥的。
由於這樣子很麻煩,所以使用矩陣堆疊。就是上面那個例子了。 既然叫做stack,就是一個棧了,最大高度64,可以隨意推matrix進去,一get就能get到棧頂。另外乘法運算也可以直接呼叫stack的方法,使用棧頂來乘以另一個矩陣,最後的結果會被推到棧頂去。
stack還有放射變換的api
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);