1. 程式人生 > >OpenGL超級寶典筆記(一)數學基礎與基礎變換

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的座標系: 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);