計算機圖形學&OpenGL系列教程(三) 基本的程序框架

(本文由作者轉載自出處,轉載請保留信息)

概述

  本次教程將本着夠用原則,將對代碼進行比較精細的講解,利用glut與glew創建一個基本的能夠運行的OpenGL程序框架,同時講解一些相關概念。

glut

在第一次教程已經對glut做了簡介,這裏結合代碼使用glut創建一個支持OpenGL渲染的窗口,並加入對鍵盤按鍵的處理函數,首先是頭文件和庫文件的引入:

#include <windows.h>   //這個頭文件應當在opengl相關的頭文件之前
#include <gl/glew.h>   //這個頭文件應當在其它opengl相關的頭文件之前
#include <gl/glut.h>
#include <iostream>    //輸入輸出
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "freeglut.lib")
#ifdef _WIN64   //如果編譯64位程序
#pragma comment(lib, "glew64.lib")
#else 
#pragma comment(lib, "glew32.lib")
#endif

  這裏解釋一下代碼,Windows.h是Windows應用程序開發所需的頭文件,其中定義了很多與Windows開發有關的常量,會被OpenGL用到(如果你在非Windows平臺上開發則不需要Windows.h),因此應當在所有有關OpenGL的頭文件之前包含;雖然下一小節纔講glew,但是glew.h要求在其它OpenGL頭文件之前被包含,因此這裏仍先寫出。然後是glut.h,這是本節要講的重點。接下來的#pragma comment(lib, …)則是對庫文件使用的聲明,告訴鏈接器去鏈接後面所給出的庫文件中的二進制代碼(函數實現),(有關頭文件庫文件等實際工程方面的內容本教程不多做介紹,如感覺理解困難請參閱《程序員的自我修養——編譯、鏈接與庫》一書),如果不對此作出聲明,那麼鏈接器將會給出無法解析外部符號的錯誤導致無法生成最終的可執行文件。_WIN64是一個當你要編譯生成64位應用程序的時候會被定義的宏,使用這個宏判斷生成的目標平臺也是一個常用技巧。

  接下來寫出main函數:

const int viewWidth = 600;
const int viewHeight = 600;

int main(int argc, char* argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
    glutInitWindowSize(viewWidth, viewHeight);
    glutCreateWindow("OpenGL Demo");
    glViewport(0, 0, viewWidth, viewHeight);
    glutDisplayFunc(render);
    glutKeyboardFunc(keyEvent);
    glutSpecialFunc(specialKeyEvent);
    glutMainLoop();
    return 0;
}

  glutInit爲glut庫進行初始化。

  接下來glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE) 指定了屏幕上繪製的圖像的像素格式爲(R,G,B,A),使用雙緩衝機制。

  這裏簡單解釋一下雙緩衝機制:首先說一下緩衝區(Buffer),緩衝區是一個很廣泛的概念在之前的C/C++語言的學習中也有,OpenGL中如果不特別說明,緩衝區是指暫時置放輸出或輸入資料的主存或顯存,它可以儲存頂點或者圖像等的數據。雙緩衝機制使用兩個緩衝區儲存兩個圖像的數據,其中一個緩衝區儲存在窗口中顯示的圖像,被成爲屏幕緩衝區(Onscreen Buffer),因爲它表示的圖像被展示在屏幕上,是可見的。另一個緩衝區表示的圖像不再屏幕上,不可見,被稱爲後臺緩衝區或者離屏緩衝區(Offscreen Buffer)。

在這裏插入圖片描述

  我們把繪製圖像的一系列操作在離屏緩衝區上完成,每次繪製完成後將屏幕緩衝區與離屏緩衝區交換(一般相當於是直接交換指向兩個緩衝區的指針,非常快) ,使得離屏緩衝區變爲屏幕緩衝區,而之前的屏幕緩衝區變爲離屏緩衝區,從而使得剛完成的繪製能夠被呈現到屏幕上,同時避免了直接在屏幕緩衝區上繪製易造成畫面異常(例如閃爍)等問題。

  接下來glutInitWindowSize設定窗口內可用與繪製的區域的寬和高(以像素爲單位),再使用glutCreateWindow創建可用於OpenGL渲染的窗口,標題爲OpenGL Demo。值得注意的是,只有在這裏glutCreateWindow之後,OpenGL纔是可用的,在glutCreateWindow之前,因爲沒有創建OpenGL渲染上下文,所有有關OpenGL的函數調用都是無效的。緊接着glutCreateWindow之後是glViewport,告訴OpenGL繪製的區域左上角座標爲(0, 0),並提供寬度和高度(這裏的座標系和第二章的圖像的座標系是一致的)。

  接下來向glut註冊渲染和處理鍵盤輸入的函數,也是之前給出的代碼裏缺失的三個函數,在main函數之前加入它們的代碼:

void render() {
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    //這裏進行渲染的工作

    glutSwapBuffers();
   //glutPostRedisplay();
}

void keyEvent(unsigned char key, int, int) {
    printf("%c", key);   //參數key直接給出了輸入的字符
}
void specialKeyEvent(int keyCode, int, int) {
    switch (keyCode) {    //通過keyCode的值判斷是哪個特殊鍵
    case GLUT_KEY_F1:    //F1
        exit(0);         //實現按下F1退出程序
        break;
    default:
        break;
    }
}

   於是main函數中依次調用glutDisplayFunc(render); glutKeyboardFunc(keyEvent); glutSpecialFunc(specialKeyEvent); 依次註冊在窗口上繪製圖像的函數、處理鍵盤一般鍵位輸入的函數、處理鍵盤特殊鍵位輸入的函數。特殊鍵位指的就是鍵盤上例如方向鍵,功能鍵等有特殊含義的鍵,可以輸入GLUT_KEY_在visual studio編輯器給出的代碼提示中查看可用的特殊鍵位。而其它的鍵位就是一般鍵位,例如字母,數字等。在你敲擊鍵盤時,就會調用你註冊的這兩個函數。

   render函數中,在渲染工作之前,做了清理工作。glClearColor函數將清理用的顏色設置爲(1.0, 1.0, 1.0, 1.0),也就是純白色,接下來使用glClear函數,參數GL_COLOR_BUFFER_BIT表示用glClearColor設定的顏色清除顏色緩衝區(其實還有別的類型的緩衝區,現在先不管)。於是將窗口清理爲了純白色。注意因爲使用雙緩衝,所以實際上清理工作也是在離屏的緩衝區上進行的,因此在渲染完成後,使用glutSwapBuffers()交換屏幕緩衝區與離屏緩衝區。如果需要render函數不停被調用,可以在最後加上glutPostRedisplay()要求重繪,這對於動畫的展示是至關重要的,否則render函數只會在glut認爲窗口需要重新繪製的時候被調用。

   在main函數最後,調用glutMainLoop()即可使得程序正常運行。現在可以編譯運行看到結果:

在這裏插入圖片描述

glew

  glew是一個跨平臺的可以幫助C/C++開發者初始化高版本OpenGL API的工具,本教程也正是藉助它的力量能夠順利使用OpenGL4.0。它的使用也很簡單,在main函數之前加入這段代碼:

void initGL() {
    GLenum err = glewInit();  //初始化
    if (err != GLEW_OK) {
        MessageBox(0, TEXT("初始化GLEW失敗"), TEXT("錯誤"), 0);
        exit(1);
    }
    if (!GLEW_VERSION_4_0) {  //檢查OpenGL4.0支持
        MessageBox(0, TEXT("本計算機未支持OpenGL4.0"), TEXT("錯誤"), 0);
        exit(1);
    }
}

   MessageBox可以彈出對話框展示提示信息。在main函數內glutCreateWindow之後調用initGL函數即可,之後就可以使用現代OpenGL特性了。這段代碼是固定的,也很好理解,便不再作解釋。之後的教程的代碼也會原封不動地複製粘貼使用。

本次教程的完整代碼見這裏

  • 簡述雙緩衝機制的工作方式(題庫原題)
  • 一位同學寫了這麼一段代碼:
  • //略去頭文件,庫文件的引入以及initGL, render函數的定義
    int main(int argc, char *argv[]) {
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
        glutInitWindowSize(233, 233);
        initGL();
        glutCreateWindow("emmmmmmm");
        glViewport(0, 0, 233, 233);
        glutDisplayFunc(render);
        glutMainLoop();
        return 0;
    }
    

    成功編譯,然而運行卻彈出對話框提示初始化GLEW失敗,請指出其問題。

    發表評論
    所有評論
    還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
    相關文章