OpenGL 學習 03 移動的矩形

執著丶執念發表於2018-06-02

OpenGL 學習 03   移動的矩形

學習書籍: OpenGL 超級寶典(中文第五版) 密碼:fu4w

書籍原始碼:OpenGL 超級寶典第五版原始碼 密碼:oyb4

環境搭建:OpenGL 學習 01 - Mac 搭建 OpenGL 環境

上一節我們學了如何在視窗上渲染一個三角形,這節我們在此基礎上加入圖形的移動,並且圖形從三角形變成矩形。

畫矩形

上一節我們設定三角形資料程式碼如下:

//開始構建批次,GL_TRIANGLES 表示三角形,後面引數是頂點數
triangleBatch.Begin(GL_TRIANGLES, 3);
//批次增加儲存屬性,頂點資料
triangleBatch.CopyVertexData3f(vVerts);
//結束批次屬性設定,表明完成資料複製操作,不能再新增新屬性
triangleBatch.End();
複製程式碼

和三角形相似,而設定矩形資料用到的是 GL_TRIANGLE_FAN, 頂點數為 4 個

//開始構建批次,GL_TRIANGLE_FAN 表示四邊形,後面引數是頂點數
squareBatch.Begin(GL_TRIANGLE_FAN, 4);
//批次增加儲存屬性,頂點資料
squareBatch.CopyVertexData3f(vVerts);
//結束批次屬性設定,表明完成資料複製操作,不能再新增新屬性
squareBatch.End();
複製程式碼

這個具體有多少種圖形型別(也叫圖元),以後我們會逐步介紹,這裡就不詳細講了

按鍵移動

檢測鍵盤的方向鍵點選,需要呼叫 GLUT 的鍵盤特殊按鍵回撥註冊函式,和視窗大小變化、視窗渲染回撥註冊函式形式一樣

//註冊視窗大小變化回撥函式
glutReshapeFunc(ChangeSize);
//註冊視窗渲染回撥函式
glutDisplayFunc(RenderScene);
//註冊鍵盤特殊按鍵點選回撥函式
glutSpecialFunc(SpecialKeys);
複製程式碼

注意:這裡是鍵盤特殊按鍵點選回撥註冊(glutSpecialFunc),GLUT 語法中,特殊按鍵指功能鍵或者方向鍵(上、下、左、右箭頭鍵等),它還有另外一個鍵盤普通按鍵點選的回撥註冊(glutKeyboardFunc),比如空格。

/* 
 * 引數 key 表示按鍵的唯一標識,(x, y) 是發生按鍵事件時滑鼠點所處位置(以左上角為起點)
 * 上、下、左、右箭頭按鍵分別對應的 key 值為: 
 * GLUT_KEY_UP、GLUT_KEY_DOWN、GLUT_KEY_LEFT、GLUT_KEY_RIGHT
 */
void SpecialKey(GLint key, GLint x, GLint y);
複製程式碼

邊界檢測

我們不希望矩形移動出我們的視窗範圍,這裡就需要在移動時進行邊界檢測,在做邊界檢測前,我們需要了解視窗的座標系

OpenGL 學習 03   移動的矩形

視窗座標系預設是從 -1.0f 到 1.0f,中央是座標原點,而我們的矩形起始位置的中央也是原點,邊長為 0.4f,所以頂點座標為:

// 這裡用 blockSize 來儲存矩形的半邊長,便於之後修改矩形邊長
GLfloat blockSize = 0.2f;
GLfloat vVerts[] = {
    -blockSize, -blockSize, 0.0f, //左下角
    blockSize, -blockSize, 0.0f,  //右下角
    blockSize,  blockSize, 0.0f,  //右上角
    -blockSize,  blockSize, 0.0f  //左上角
};
複製程式碼

OpenGL 學習 03   移動的矩形

邊界檢測,這裡我以左下角頂點為標準進行判斷,如上圖可以得到以下邊界檢測程式碼:

//左下角座標的 (x, y)
GLfloat blockX = vVerts[0];
GLfloat blockY = vVerts[1];
//中間進行移動
...
//邊界檢測,讓矩形沒法超出邊界
if(blockX < -1.0f) blockX = -1.0f;
if(blockX > (1.0f - blockSize * 2)) blockX = 1.0f - blockSize * 2;
if(blockY < -1.0f) blockY = -1.0f;
if(blockY > (1.0f - blockSize * 2)) blockY = 1.0f - blockSize * 2;
複製程式碼

矩形按鍵移動

#include <iostream>
#include <GLShaderManager.h>
#include <GLTools.h>
#include <glut/glut.h>

GLBatch squareBatch;
GLShaderManager shaderManager;

GLfloat blockSize = 0.2f;
GLfloat vVerts[] = {
    -blockSize, -blockSize, 0.0f, //左下角
    blockSize, -blockSize, 0.0f,  //右下角
    blockSize,  blockSize, 0.0f,  //右上角
    -blockSize,  blockSize, 0.0f  //左上角
};

void  BourceFunction(int key) {
    //每次按鍵的矩形移動距離
    GLfloat stepSize = 0.025f;
    //左下角座標的 (x, y)
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[1];
    
    //判斷鍵盤點選的是哪個按鈕,計算下一個點的位置
    if(key == GLUT_KEY_UP) blockY += stepSize;
    if(key == GLUT_KEY_DOWN) blockY -= stepSize;
    if(key == GLUT_KEY_LEFT) blockX -= stepSize;
    if(key == GLUT_KEY_RIGHT) blockX += stepSize;
    
    //邊界檢測,讓矩形沒法超出邊界
    if(blockX < -1.0f) blockX = -1.0f;
    if(blockX > (1.0f - blockSize * 2)) blockX = 1.0f - blockSize * 2;
    if(blockY < -1.0f) blockY = -1.0f;
    if(blockY > (1.0f - blockSize * 2)) blockY = 1.0f - blockSize * 2;
    
    //設定下個位置的 4 個頂點座標
    //左下角
    vVerts[0] = blockX;
    vVerts[1] = blockY;
    //右下角
    vVerts[3] = blockX + blockSize * 2;
    vVerts[4] = blockY;
    //右上角
    vVerts[6] = blockX + blockSize * 2;
    vVerts[7] = blockY + blockSize * 2;
    //左上角
    vVerts[9] = blockX;
    vVerts[10] = blockY + blockSize * 2;
    
    //批次裡面的資料更新
    squareBatch.CopyVertexData3f(vVerts);
}

// 監控特殊按鍵(功能鍵和方向鍵)點選
void SpecialKeys(int key, int x, int y) {
    //計算下一個矩形位置
    BourceFunction(key);
    
    //觸發重畫事件 -> RenderScene
    glutPostRedisplay();
}

//視窗渲染
void RenderScene(void) {
    //清除一個或一組特定的緩衝區
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //利用單位著色器設定矩形顏色為紅色
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    //畫圖
    squareBatch.Draw();
    
    //將在後臺緩衝區進行渲染,然後在結束時交換到前臺
    glutSwapBuffers();
}

//為程式作一次性的設定
void SetupRC() {
    //設定背景顏色為藍色
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    
    //著色器初始化
    shaderManager.InitializeStockShaders();
    
    //批次初始化
    squareBatch.Begin(GL_TRIANGLE_FAN, 4);
    squareBatch.CopyVertexData3f(vVerts);
    squareBatch.End();
}

//視窗大小改變時接受新的寬度和高度,其中0,0代表視窗中視口的左下角座標,w,h代表畫素
void ChangeSize(int width, int height) {
    glViewport(0, 0, width, height);
}

//程式入口
int main(int argc, char* argv[]) {
    //設定當前工作目錄,針對MAC OS X
    gltSetWorkingDirectory(argv[0]);
    
    //初始化GLUT庫
    glutInit(&argc, argv);
    
    /*初始化渲染模式,其中標誌GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分別指
     雙緩衝視窗、RGBA顏色模式、深度測試、模板緩衝區*/
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    
    //初始化視窗大小
    glutInitWindowSize(800, 600);
    //建立視窗
    glutCreateWindow("MoveDemo");
    
    //註冊視窗大小變化回撥函式
    glutReshapeFunc(ChangeSize);
    //註冊視窗渲染回撥函式
    glutDisplayFunc(RenderScene);
    //註冊鍵盤特殊按鍵點選回撥函式
    glutSpecialFunc(SpecialKeys);
    
    //確保驅動程式的初始化中沒有出現任何問題。
    GLenum err = glewInit();
    if(GLEW_OK != err) {
        fprintf(stderr, "glew error:%s\n", glewGetErrorString(err));
        return 1;
    }
    
    //初始化設定
    SetupRC();
    
    //進入呼叫迴圈
    glutMainLoop();
    return 0;
}
複製程式碼

OpenGL 學習 03   移動的矩形

矩形自動移動,按鍵控制方向

這裡我們注意到在 SpecialKeys 方法中呼叫了 glutPostRedisplay 方法觸發視窗渲染,得到的效果是我們按一下矩形就走一下,如果我們想讓矩形自己動,我們只控制方向,怎麼辦呢?

矩形自己動,就需要自動持續重新整理,只需要修改視窗渲染 RenderScene 程式碼為以下程式碼即可:

//視窗渲染
void RenderScene(void) {
    //清除一個或一組特定的緩衝區
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //利用單位著色器設定矩形顏色為紅色
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    //畫圖
    squareBatch.Draw();
    
    //將在後臺緩衝區進行渲染,然後在結束時交換到前臺
    glutSwapBuffers();
    
    //計算下一個矩形位置,specialKey 是全域性記錄的特殊按鍵 key
    BounceFunction(specialKey);

    //觸發重畫事件 -> RenderScene
    glutPostRedisplay();
}
複製程式碼

注意:這裡不是一個無限迴圈,重繪訊息實際上是一條傳遞到一個內部訊息迴圈中的訊息,在螢幕重新整理的間隔中,也會發生其他視窗事件,我們仍可以檢測按鍵、滑鼠移動、改變視窗大小等動作。

specialKey 的記錄程式碼如下:

int specialKey = GLUT_KEY_RIGHT;
// 監控特殊按鍵(功能鍵和方向鍵)點選
void SpecialKeys(int key, int x, int y) {
    specialKey = key;
}
複製程式碼

Demo 原始碼在這裡:02-MoveDemo

有什麼問題可以在下方評論區提出,求關注求贊,O(∩_∩)O哈哈~

相關文章