1. 程式人生 > >【計算機圖形學】OpenGL+VS2015相關類庫配置

【計算機圖形學】OpenGL+VS2015相關類庫配置

1 WiKi

OpenGL一直是事實上的計算機圖形學標準,截止2016年06月,OpenGL版本已經更新到4.5。不過DirectX發展迅速,大有OpenGL落後的態勢,知乎上的該話題的討論:https://www.zhihu.com/question/23241456。所以OpenGL為了追趕DirectX,推出 了glNext(Vulkan API),相關討論https://www.zhihu.com/question/28039310

2 相關資料

圖形學的經典書籍是《Computer Graphics with OpenGL》,即《計算機圖形學》,以OpenGL描述,在介紹圖形學基礎理論的同時,也提供了OpengGL的相關API和例項程式。目前(2016年6月)本書已經更新到第4版。網上第3版的PDF文件比較多,尚未發現第4版的PDF文件,不過第3版和第4版差別很小,第4版重要的更新是提供了GLSL的一個章節。
《計算機圖形學》的重要性在於全面的理論知識,不過它基本不涉及Shader程式設計,仍然沿用固定管程的教學內容,然而當前的OpengGL都是Shader程式設計,讀完本書你會發現對Shader瞭解很少。
下面三本書都是Shader程式設計。
《OpenGL超級寶典》和《OpenGL程式設計指南》是兩本實用的OpengGL學習書籍,區別在於前者有更多例項,更容易按步驟進行操作,而後者著重介紹API,最好先閱讀前者。
《OpenGL 4.0 Shading Language Cookbook.pdf》主要介紹Shader程式設計。
《OpenGL 4.0 Shading Language Cookbook.pdf》下載地址:

http://download.csdn.net/detail/brillianteagle/9541959

3 相關庫檔案

3.1 gl、glu和glut

《計算機圖形學》詳細介紹了3個類庫,總結起來就是
glu庫:函式glu開頭,設定視景和投影矩陣、用線和多邊形近似法來描述物件、用線性近似法描述quadric和B-spline,處理表面繪製操作。
glut庫:跨平臺套件,包括了視窗操作、輸入管理。
注意,包括GLUT標頭檔案就不必包括GL和GLU標頭檔案。
配置好這三個庫就可以執行《計算機圖形學》中所有的例項,但是不能進行Shader程式設計。

3.2 glew和freeglut

GLU庫獲得是比較陳舊的OpenGL API版本,可以從glew獲得最新的OpenGL API版本。freeglut則是用來取代glut庫的。
所以,配置好glew和freeglut兩個庫,完全取代glu和glut庫,同時可以獲得最新版本的API,可以進行Shader程式設計。

3.3 GLTools

GLTools是《OpenGL超級寶典》作者提供的工具包,在執行本書的例項時要用到。

3.4 GLM

GLM (OpenGL Mathematics) 也是一個工具包,在執行《OpenGL 4.0 Shading Language Cookbook.pdf》的程式時會用到。

4 庫檔案配置

一般配置都有兩種方式,要麼相關檔案放到VC目錄下,這樣不必每次都配置;要麼相關檔案放到工程目錄下,幷包含標頭檔案目錄和lib檔案目錄,這樣程式在在其他電腦上也可以開啟。各有好處。這裡介紹第二種方式。

4.1 glew和freeglut配置

glew官網:http://glew.sourceforge.net/
glew官方GitHub主頁:https://github.com/nigels-com/glew
freeglut官網:https://sourceforge.net/projects/freeglut/
下載glew的最新Binaries版本(截止2016年6月9日的)為glew-1.13.0-win32.zip,freeglut最新版本為3.0版本freeglut-MSVC-3.0.0-2.mp.zip。
在VS2015中新建VC控制檯程式OpenGL-Demo,在【工程目錄】下新建GL資料夾和lib資料夾,然後執行下面四個步驟:
(1) 將glew-1.13.0-win32\glew-1.13.0\include\GL目錄下的標頭檔案和freeglut-MSVC-3.0.0-2.mp\freeglut\include\GL目錄下的標頭檔案都放到【工程目錄】下新建GL資料夾中;
(2) 將glew-1.13.0-win32\glew-1.13.0\lib\Release\Win32目錄下的glew32.lib檔案和freeglut-MSVC-3.0.0-2.mp\freeglut\lib目錄下的freeglut.lib放到【工程目錄】下新建lib資料夾中,這裡要注意,我使用的是32位版本的lib包,即便我的電腦是64位的。因為我們建立的VC工程預設是32位的。
(3) 將glew-1.13.0-win32\glew-1.13.0\bin\Release\Win32目錄下的glew32.dll和freeglut-MSVC-3.0.0-2.mp\freeglut\bin目錄下的freeglut.dll放到工程目錄下。
(4) 在專案上右鍵選單中開啟配置,設定附加標頭檔案目錄和附加lib檔案目錄,如圖。注意,$(ProjectDir)就是當前目錄的意思。
這裡寫圖片描述
這裡寫圖片描述
可以用以下程式碼測試一下,繪製一個白色背景的視窗:

// OpenGL-Demo.cpp : Defines the entry point for the console application.
//
#pragma comment(lib,"glew32.lib")
#include <glew.h>
#include <freeglut.h>
#include <iostream>
using namespace std;
/* GLUT callback Handlers */

static void resize(GLint width, GLint height) {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)width, 0.0, (GLdouble)height);
    glClear(GL_COLOR_BUFFER_BIT);
}
/**繪製 函式,這裡是空的*/
static void display() {
}
/**更換螢幕顏色設定矩陣模式
選擇正攝投影的範圍*/
static void init() {
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(0.0, 200.0, 0.0, 150.0);//視窗的左下角是原點
}
/* Program entry point */

int main(int argc, char *argv[]) {
    glutInit(&argc, argv);
    glutInitWindowSize(640, 480);
    glutInitWindowPosition(100, 100);//相對螢幕的左上角
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);//正在使用單個幀快取,顏色模式//為RGB,這是預設的顏色模式
    glutCreateWindow("Draw Curves");
    init();
    if (glewInit() == GLEW_OK) {
        cout << "glew初始化成功!" << endl;
    }
    glutDisplayFunc(display);
    glutReshapeFunc(resize);
    glutMainLoop();
}

4.2 GLTools生成

這個庫由《OpenGL超級寶典》的作者提供,但是需要自己進行編譯使用。這裡我將本書原始碼進行了上傳。
part1下載地址:http://download.csdn.net/detail/brillianteagle/9545333
part2下載地址:http://download.csdn.net/detail/brillianteagle/9545335
只有兩個都下載後才能解壓。解壓後SB5\Src目錄下就有GLTools資料夾。該資料夾的目錄下有include和src兩個資料夾。由於GLTools的編譯依賴於glew,可以看到SB5\Src\GLTools\include\GL目錄下就是某個版本的glew標頭檔案,而SB5\Src\GLTools\src目錄下有glew.c原始檔。
由於我們已經採用了glew的1.13版本,所以這裡要替換SB5\Src\GLTools\include\GL目錄下的所有標頭檔案,並採用1.13版本glew.c原始檔。在glew的官網http://glew.sourceforge.net/下載Source版本,替換SB5\Src\GLTools\src目錄下有glew.c原始檔。
在VS2015中新建VC靜態庫程式GLTools,然後按下面步驟配置工程:
(1)將 SB5\Src\GLTools的include資料夾放到【工程目錄】下,將SB5\Src\GLTools\src目錄下的原始檔放到【工程目錄】下;
(2)將SB5\Src\GLTools\src目錄下的所有原始檔放到【工程目錄】下;
(3)在工程上點選右鍵選單中的屬性,新增標頭檔案。
這裡寫圖片描述
然後,就可以在relseas模式下編譯該工程,結束後GLTools\Release目錄下生成GLTools.lib。

4.3 GLTools配置

如何把GLTools整合到自己的工程呢?
在之前的OpenGL-Demo【專案目錄】下新建GLTools資料夾。
(1) 將更新過glew最新版本的SB5\Src\GLTools的include資料夾下的所有檔案拷貝到GLTools資料夾,並刪除【專案目錄】的GL資料夾,因為此時GL資料夾已經存在於GLTools目錄下。
(2) 將GLTools.lib新增到lib資料夾。
(3) 更改附加標頭檔案目錄(ProjectDir)GL(ProjectDir) GLTools
值得注意的是,GLTools依賴於glew,所有,在新增GLTools到自己的工程時要注意依賴關係。這樣,引入GLTools,也就引入了glew。
測試一下《OpengGL超級寶典(第5版)》的第一個例子,藍色背景下繪製紅色三角形。

// OpenGL-Demo.cpp : Defines the entry point for the console application.
// Created by 曹豔豐
//2016-06-09
//#pragma comment(lib,"glew32.lib")//不用

#pragma comment(lib,"GLTools.lib")

//#include <glew.h>//不用
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GL\freeglut.h>
GLBatch triangleBatch;
GLShaderManager shaderManager;
///////////////////////////////////////////////////////////////////////////////  
// Window has changed size, or has just been created. In either case, we need  
// to use the window dimensions to set the viewport and the projection matrix.  
void ChangeSize(int w, int h)
{
    glViewport(0, 0, w, h);
}


///////////////////////////////////////////////////////////////////////////////  
// This function does any needed initialization on the rendering context.   
// This is the first opportunity to do any OpenGL related tasks.  
void SetupRC()
{
    // Blue background  
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);

    shaderManager.InitializeStockShaders();

    // Load up a triangle  
    GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,
        0.5f, 0.0f, 0.0f,
        0.0f, 0.5f, 0.0f };

    triangleBatch.Begin(GL_TRIANGLES, 3);
    triangleBatch.CopyVertexData3f(vVerts);
    triangleBatch.End();
}



///////////////////////////////////////////////////////////////////////////////  
// Called to draw scene  
void RenderScene(void)
{
    // Clear the window with current clearing color  
    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);
    triangleBatch.Draw();

    // Perform the buffer swap to display back buffer  
    glutSwapBuffers();
}


///////////////////////////////////////////////////////////////////////////////  
// Main entry point for GLUT based programs  
int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Triangle");
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);

    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }

    SetupRC();

    glutMainLoop();
    return 0;
}

4.4 GLM配置

GLM只有標頭檔案,只需要在官網下載後解壓,glm-master\glm-master目錄下的glm放到OpenGL-Demo【工程目錄】下,並新增$(ProjectDir)glm即可。
glm官網:https://sourceforge.net/projects/ogl-math/
下面是《OpenGL 4.0 Shading Language Cookbook.pdf》中第一個例子,也是繪製三角形。

#pragma comment(lib,"glew32.lib")//這裡要載入該靜態包
#include <GL/glew.h>
#include <GL/glut.h>
#include <glm\glm.hpp>//斜槓和反斜槓都可以
#include <iostream>

/*
完整的shader過程。
初始化→編譯→連結到程式
*/
using namespace std;

/*傳入shader的引用,和GLSL字串,對shader進行編譯*/
void compileShader(GLint& shader, const GLchar* shaderCode) {
    const GLchar* codeArray[] = { shaderCode };
    glShaderSource(shader, 1, codeArray, NULL);
    glCompileShader(shader);//編譯shader

                            /*判斷編譯後的結果*/
    GLint result;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &result);

    if (result == GL_FALSE) {
        cout << "shader編譯錯誤!" << endl;
        GLint logLen;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);

        if (logLen>0) {
            char* log = (char*)malloc(logLen);
            GLsizei written;
            glGetShaderInfoLog(shader, logLen, &written, log);
            cout << log << endl;
            free(log);

        }
        exit(1);
    }
    else
    {
        cout << "shader編譯成功!" << endl;
    }
}
void render(GLuint programHandle) {
    float positionData[] = {
        -0.8f, -0.8f, 0.0f,
        0.8f, -0.8f, 0.0f,
        0.0f, 0.8f, 0.0f
    };
    float colorData[] = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 1.0f
    };

    GLuint vboHandle[2];
    glGenBuffers(2, vboHandle);
    GLuint positionbufferHandle = vboHandle[0];
    GLuint colorBufferHandle = vboHandle[1];
    glBindBuffer(GL_ARRAY_BUFFER, positionbufferHandle);
    glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), positionData, GL_STATIC_DRAW);//繫結頂點資料
    glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
    glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), colorData, GL_STATIC_DRAW);//繫結顏色資料

    GLuint vaoHandle;
    glGenVertexArrays(1, &vaoHandle);
    glBindVertexArray(vaoHandle);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, positionbufferHandle);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte*)NULL);
    glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte*)NULL);
    glBindVertexArray(vaoHandle);
    //  繪製三角形
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glFlush();


}
void link() {
    //判斷glew是否能初始化
    if (glewInit() != GLEW_OK) {
        cout << "glewInit()失敗!" << endl;
        exit(1);
    }
    /*編譯vertexShader*/
    const GLchar* shaderCode = "#version 400 \n in vec3 VertexPosition;in vec3 VertexColor;out vec3 Color;void main() {Color = VertexColor;gl_Position = vec4(VertexPosition, 1.0);}";
    GLint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    if (vertexShader == 0) {
        cout << "vertexShader無法獲取!" << endl;
        exit(1);
    }
    compileShader(vertexShader, shaderCode);

    /*編譯fragmentShader*/
    const GLchar* fragmentCode = "#version 400 \n in vec3 Color;out vec4 FragColor;void main() {FragColor=vec4(Color,1.0);}";
    GLint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    if (fragmentShader == 0) {
        cout << "fragmentShader無法獲取!" << endl;
        exit(1);
    }
    compileShader(fragmentShader, fragmentCode);

    /*獲得程式programHandle*/
    GLuint programHandle = glCreateProgram();
    if (programHandle == 0)
    {
        cout << "Program無法獲取!" << endl;
        exit(1);

    }
    /*將shader連結到programHandle*/
    glAttachShader(programHandle, vertexShader);
    glAttachShader(programHandle, fragmentShader);
    glBindAttribLocation(programHandle, 0, "VertexPosition");
    glBindAttribLocation(programHandle, 1, "VertexColor");
    glLinkProgram(programHandle);

    /*判斷連結狀態*/
    GLint status;
    glGetProgramiv(programHandle, GL_LINK_STATUS, &status);
    if (status == GL_FALSE)
    {
        cout << "link失敗!" << endl;
        GLint logLen;
        glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logLen);

        if (logLen>0) {
            char* log = (char*)malloc(logLen);
            GLsizei written;
            glGetProgramInfoLog(programHandle, logLen, &written, log);
            cout << log << endl;
            free(log);

        }
        exit(1);
    }
    else {
        cout << "link成功!" << endl;
        glUseProgram(programHandle);//使用該程式
        render(programHandle);
    }
}

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);;
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize(900, 600);
    glutCreateWindow("First GLUT Sample");
    /*link函式是GLSL編寫的主體*/
    glutDisplayFunc(link);
    glutMainLoop();
    return 0;
}