OpenGL 繪制鍵盤可控制的正方形

在繪制正方形圖形跟繪制三角形圖形基本上是一樣的流程,要用鍵盤控制圖形移動,還是要了解 OpenGL 下鍵位是如何控制的。

鍵位的宏

/* function keys */
#define GLUT_KEY_F1         1
#define GLUT_KEY_F2         2
#define GLUT_KEY_F3         3
#define GLUT_KEY_F4         4
#define GLUT_KEY_F5         5
#define GLUT_KEY_F6         6
#define GLUT_KEY_F7         7
#define GLUT_KEY_F8         8
#define GLUT_KEY_F9         9
#define GLUT_KEY_F10            10
#define GLUT_KEY_F11            11
#define GLUT_KEY_F12            12
/* directional keys */
#define GLUT_KEY_LEFT           100
#define GLUT_KEY_UP         101
#define GLUT_KEY_RIGHT          102
#define GLUT_KEY_DOWN           103
#define GLUT_KEY_PAGE_UP        104
#define GLUT_KEY_PAGE_DOWN      105
#define GLUT_KEY_HOME           106
#define GLUT_KEY_END            107
#define GLUT_KEY_INSERT         108

可以很清晰看出每個鍵位的功能,在 OpennGL 下不光有單鍵位控制,還有組合鍵位控制。

下面介紹兩種控制圖形移動的方式

  • 頂點坐標法
  • 矩陣變換法

頂點坐標法

頂點坐標法是一個以點即點的方法,為什么這么說呢,因為要換算每一個頂點坐標,通過每一個頂點坐標計算獲取最終移動后的頂點坐標。可以想象對于坐標點很多的圖形,此方法是不是顯得過于笨拙呢。直接上代碼:

  • 首先還是要初始化配置
GLShaderManager shaderManager;
// 正方形批次容器
GLBatch quaBatch;
// 距離坐標軸的邊長
GLfloat blockSize = 0.1;
// 正方形4個頂點坐標
GLfloat vVerts[] = {
    -blockSize, -blockSize, 0.0f,
    blockSize, -blockSize, 0.0f,
    blockSize, blockSize, 0.0f,
    -blockSize, blockSize, 0.0f
};

int main(int argc, char *argv[]) {
    
    // 區分頂點坐標法和矩陣變換法
    normal = false;
    
    gltSetWorkingDirectory(argv[0]);
    
    glutInit(&argc, argv);
    
    /*
     * 設置雙緩沖區
     * GLUT_DOUBLE 雙緩沖窗口
     * GLUT_RGBA RGBA顏色模式
     * GLUT_DEPTH 深度測試
     * GLUT_STENCIL 模板緩沖區
     */
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
    
    // 設置窗口大小
    glutInitWindowSize(1000, 1000);
    // 設置窗口名稱
    glutCreateWindow("Qua");
    
    // 注冊重塑函數
    glutReshapeFunc(changeSize);
    // 注冊顯示函數
    glutDisplayFunc(renderScene);
    
    // 注冊鍵盤監聽
    glutSpecialFunc(dealKeys);
    
    /*
     * 初始化一個GLEW庫,確保OpenGL API對程序完全可用
     * 在渲染之前, 檢查驅動程序的初始化過程沒有問題
     */
    GLenum status = glewInit();
    if (GLEW_OK != status) {
        
        printf("GLEW Error:%s\n",glewGetErrorString(status));
        return 1;
    }
    
    // 設置渲染環境
    setupRC();
    // 開啟事件循環
    glutMainLoop();
    
    return 0;
}

每個方法是做什么的,注釋已經寫的很清楚了

  • 窗口改變設置
/*
 * 在窗口大小改變時,接收新的寬度&高度。
 */
void changeSize(int w, int h) {
    
    // 通常x、y都為0
    glViewport(0, 0, w, h);
}
  • 渲染場景設置
// 每一次圖形移動都會調用
void renderScene() {
    
    /*
     * 清除一個或者一組特定的緩沖區
     * 緩沖區是一塊存在圖像信息的儲存空間,RGBA通常一起作為顏色緩沖區或像素緩沖區引用
     * OpenGL 中不止一種緩沖區(顏色緩沖區、深度緩沖區、模板緩沖區)
     * GL_COLOR_BUFFER_BIT 顏色緩沖區
     * GL_DEPTH_BUFFER_BIT 深度緩沖區
     * GL_STENCIL_BUFFER_BIT 模塊緩沖區
     ****/
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    // 設置畫筆顏色
    GLfloat vRed[] = {1.0, 0.0, 0.0, 1.0};
    
    /*
     * 傳遞到存儲著色器(固定管線)
     * GLT_SHADER_IDENTITY 頂點著色器
     */
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);

    // 提交著色器
    quaBatch.Draw();
    // 將后臺緩沖區進行渲染,完成后交給前臺
    glutSwapBuffers();
}
  • 注冊鍵位監聽
void dealKeys(int key, int x, int y) {
    /*
     * 可以監聽鍵盤的案件很多包括單鍵、組合鍵等
     * 我們只需要監聽上下左右就可以
     * 加上邊界測試
     */
    
    // 移動多遠
    GLfloat stepSize = 0.025;
    
    /*
     * d-----c
     * |     |
     * |     |
     * a-----b
     *
     * a和d、b和c x軸坐標相等 a和b、c和d y軸坐標相等
     */
    // 取d點坐標 通過d點坐標換算a、b、c坐標
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[10];
   
    switch (key) {
        case GLUT_KEY_UP:
            blockY += stepSize;
            break;
        case GLUT_KEY_DOWN:
            blockY -= stepSize;
            break;
        case GLUT_KEY_LEFT:
            blockX -= stepSize;
            break;
        case GLUT_KEY_RIGHT:
            blockX += stepSize;
            break;
        default:
            break;
    }
    
    // 左移
    if (blockX < -1.0f) {
        blockX = -1.0f;
    }
    // 右移 減去2倍邊長
    if (blockX > (1.0 - blockSize * 2)) {
        blockX = 1.0 - blockSize * 2;
    }
    // 上移
    if (blockY > 1.0f) {
        blockY = 1.0f;
    }
    // 下移
    if (blockY < -1.0f + blockSize * 2) {
        blockY = -1.0f + blockSize * 2;
    }
    
    // 為啥不處理vVerts[2]/vVerts[5]/vVerts[8] 因為是z軸,z軸沒有變化
    // a
    vVerts[0] = blockX;
    vVerts[1] = blockY - blockSize * 2;
    //b
    vVerts[3] = blockX + blockSize * 2;
    vVerts[4] = blockY - blockSize * 2;
    //c
    vVerts[6] = blockX + blockSize * 2;
    vVerts[7] = blockY;
    //d
    vVerts[9] = blockX;
    vVerts[10] = blockY;
    
    //需要重新提交渲染
    quaBatch.CopyVertexData3f(vVerts);
    glutPostRedisplay();
}

可以會有疑問,為什么邊長是0.1?在 OpenGL 坐標系中 x、y 、z 軸的范圍都是(-1,1),所以邊長就是0.1了,是一個比例值。具體的實現流程代碼中已經解釋的很清楚了,值得注意的是:函數
glutPostRedisplay() 是提交重新渲染,因為圖形移動了就要重新繪制,當然還是回再次調用函數 renderScene()。這也是函數 glutMainLoop() 的意義所在。

  • 設置渲染環境
void setupRC() {
     
    // 設置背景顏色
    glClearColor(1.0, 0.7, 0.7, 1.0);
    
    // 初始化著色器, 沒有著色器是沒有辦法渲染的
    shaderManager.InitializeStockShaders();
    
    // 批次提交
    quaBatch.Begin(GL_TRIANGLE_FAN, 4);
    quaBatch.CopyVertexData3f(vVerts);
    quaBatch.End();
};

OpenGL 支持三角形圖元裝配,示意圖如下:

三角形圖元裝配示意圖
  • GL_TRIANGLES 繪制一系列單獨的三角形
  • GL_TRIANGLES_STRIP 構建當前三角形的頂點的連接順序依賴于要和前面已經出現過的2個頂點組成三角形的當前頂點的序號的奇偶性。如果是頂點是偶數v2,以v0-v1-v2這樣順序排列。如果是奇數v3,以v2-v1-v3這樣順序排列。這種順序是為了保證所有的三角形都是按照相同的方向繪制。
  • GL_TRIANGLES_FAN 繪制各三角形形成一個扇形序列,以v0為起始點,以v0-v1-v2、v0-v2-v3、v0-v3-v4這樣的順序排列。

為什么說一下三角形圖元裝配呢,在代碼中,用的是GL_TRIANGLES_FAN 宏繪制的正方形,當然也可以使用 GL_QUADS 宏繪制正方形。
這樣一個可移動的正方形的實現就完成了。下面看一下效果圖

效果圖

總結一下繪制流程

頂點坐標法流程圖

矩陣變換法

矩陣變換法相當于以點即面,不需要換算每一個頂點坐標。通過一個中心點去換算全部的頂點坐標。繪制的流程跟頂點坐標法差不多,只是有幾個地方不同。直接上代碼:

  • 定義全局變量
// 中心點
GLfloat xPos = 0.0f;
GLfloat yPos = 0.0f;
  • 坐標變換
void dealKeys(int key, int x, int y) {
    /*
     * 可以監聽鍵盤的案件很多包括單鍵、組合鍵等
     * 我們只需要監聽上下左右就可以
     * 加上邊界測試
     */
    
    // 移動多遠
    GLfloat stepSize = 0.025;
    
    /*
     * d-----c
     * |     |
     * |     |
     * a-----b
     *
     * a和d、b和c x軸坐標相等 a和b、c和d y軸坐標相等
     */
   switch (key) {
          case GLUT_KEY_UP:
              yPos += stepSize;
              break;
          case GLUT_KEY_DOWN:
              yPos -= stepSize;
              break;
          case GLUT_KEY_LEFT:
              xPos -= stepSize;
              break;
          case GLUT_KEY_RIGHT:
              xPos += stepSize;
              break;
          default:
              break;
      }
      
      // 左移
      if (xPos < -1.0f + blockSize) {
          xPos = -1.0f + blockSize;
      }
      // 右移
      if (xPos > 1.0 - blockSize) {
          xPos = 1.0 - blockSize;
      }
      // 上移
      if (yPos > 1.0f - blockSize) {
          yPos = 1.0f - blockSize;
      }
      // 下移
      if (yPos < -1.0f + blockSize) {
          yPos = -1.0f + blockSize;
      }
      glutPostRedisplay();
}

這里就不需要 quaBatch.CopyVertexData3f(vVerts) 復制頂點數據了,因為是整體再改變。

  • 圖形繪制
// 每一次圖形移動都會調用
void renderScene() {
    
    /*
     * 清除一個或者一組特定的緩沖區
     * 緩沖區是一塊存在圖像信息的儲存空間,RGBA通常一起作為顏色緩沖區或像素緩沖區引用
     * OpenGL 中不止一種緩沖區(顏色緩沖區、深度緩沖區、模板緩沖區)
     * GL_COLOR_BUFFER_BIT 顏色緩沖區
     * GL_DEPTH_BUFFER_BIT 深度緩沖區
     * GL_STENCIL_BUFFER_BIT 模塊緩沖區
     ****/
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    // 設置畫筆顏色
    GLfloat vRed[] = {1.0, 0.0, 0.0, 1.0};
    /*
     * 矩陣變換 定義一個4*4的矩陣 M3DMatrix44f 是一個一維數組
     * 從類型就能看出來 44代表4*4
     * 紅 綠 藍 縮放比例
     */
    M3DMatrix44f mTransfromMatrix;
    /*
     * 得到矩陣變化后的坐標
     **/
    m3dTranslationMatrix44(mTransfromMatrix, xPos, yPos, 0.0f);
    /*
     * GLT_SHADER_FLAT 平面著色器
     */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, mTransfromMatrix, vRed);
    // 提交著色器
    quaBatch.Draw();
    // 將后臺緩沖區進行渲染,完成后交給前臺
    glutSwapBuffers();
}

這里增加了矩陣函數,把頂點著色器的宏 GLT_SHADER_IDENTITY 換成了 平面著色器的宏 GLT_SHADER_FLAT。這樣就完成了圖形矩陣坐標變換。

總結一些繪制流程:

矩陣變換法流程圖

demo地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372