1. 程式人生 > >現代OpenGL(一):我的第一個OpenGL程式

現代OpenGL(一):我的第一個OpenGL程式

OpenGL簡介

OpenGL是一種應用程式程式設計介面(Application Programming Interface,API)它是一種可以對圖形硬體裝置特徵進行訪問的軟體庫。
在OpenGL 3.0以前的版本或者使用相容模式的OpenGL環境,OpenGL包含一個固定管線(fixed-function pipeline),它可以在不使用著色器的環境下處理幾何與畫素資料。我們看到的glBegin()、glRectf()以及glEnd()這些函式都是以前固定管線模式中所使用的API函式。
從3.1版本開始,固定管線從核心模式中去除,因此我們必須使用著色器來完成工作。現代OpenGL渲染管線嚴重依賴著色器來處理傳入的資料,我們一般會使用GLSL(OpenGL Shading Language)編寫著色器程式,GLSL語法類似於C語言,GLSL編譯以後執行在GPU端。

OpenGL的可程式設計管線包含如下過程(下圖來自OpenGL紅寶書《OpenGL Programming Guide》第八版):
OpenGL Pipeline
可以看到從開始的頂點資料到最後在介面上的顯示需要經過很多過程,這裡我比較重要的是和必經的階段包括Vertex Shader(頂點著色階段)、Rasterization(光柵化階段)和Frgament Shader(片元著色階段)。
下面的圖詳細說明了這幾個幾段內部圖形的處理與變化《摘自https://open.gl/drawing》:
graphics pipeline
頂點著色階段將接受你在頂點快取物件中給出的頂點資料,獨立處理每個頂點。這個階段對於所有的OpenGL程式都是必需的,而且必需繫結一個著色器。
光柵化就是把頂點資料轉換為片元的過程。片元中的每一個元素對應於幀緩衝區中的一個畫素。
片元著色階段會處理OpenGL光柵化之後生成的獨立片元,並且這個階段也必需繫結一個著色器。
總結一下:
一個用來渲染影象的OpenGL程式需要執行的主要操作如下:
1. 從OpenGL的幾何圖元中設定資料,用於構建形狀。
2. 使用不同的著色器(shader)對輸入的圖元資料執行計算操作,判斷它們的位置、顏色,以及其他渲染屬性。
3. 將輸入圖元的數學描述轉化為與螢幕位置對應的畫素片元(fragment)。這一步也稱為光柵化(rasterization)。
4. 最後,針對光柵化過程產生的每個片元,執行片元著色器(fragment shader),從而決定這個片元的最終顏色和位置。
5. 如果有必要,還需要對每個片元執行一些額外的操作,例如判斷片元對應的物件是否可見,或者將片元的顏色與當前螢幕位置的顏色進行融合。

OpenGL開發環境搭建

說了OpenGL的基本原理,下面來看看開發現代OpenGL程式需要準備的前期開發環境。
在我的上篇博文《OpenGL+Visual Studio 2010開發環境搭建 》中提到:
OpenGL主要由以下庫函式組成:
OpenGL核心庫:包含115個最基本的命令函式,它們都是以”gl“為字首,可以在任何OpenGL的工作平臺上應用。這部分函式用於常規的、核心的圖形處理。
OpenGL實用庫函式:包含43個函式,以”glu“作為字首,在任何OpenGL平臺上都可以應用。這部分函式通過呼叫核心庫的函式來實現一些複雜的操作。
OpenGL輔助庫函式:OpenGL Utility Toolkit (GLUT)包含31個函式,以”aux“作為字首,但它們不能在所有的OpenGL平臺上使用。OpenGL的輔助庫函式主要用於視窗管理、輸入輸出處理以及繪製一些簡單的三維形體。

其實GLUT主要用於視窗管理、輸入輸出處理以及繪製一些簡單的三維形體。而且GLUT不是開源的,所以現在有很多GLUT的替代庫,比如GLUT的開源版本Freeglut和OpenGLUT。在https://open.gl/context這個教程中,作者提到了三個用於取代GLUT的第三方庫:SFML、SDL、GFLW有興趣的朋友可以自己Google一下這些庫。由於SFML(Simple and Fast Multimedia Library)是使用C++編寫的,我本人比較喜歡使用C++而非C語言,所以下面的示例程式會使用SFML庫。

此外,還需要介紹一個庫GLEW(OpenGL Extension Wrangler)。GLEW是一個跨平臺的C++擴充套件庫,基於OpenGL圖形介面。GLEW能自動識別你的平臺所支援的全部OpenGL高階擴充套件涵數。如果沒有GLEW,我們可能還需要執行相當多的工作才能夠執行程式。

第三方庫的配置

由於我們這裡需要用到好些第三方庫,這裡順便說一下在Visual Studio中如何使用第三方的C++庫。
首先,下載官方提供的庫檔案並解壓,有的只提供了原始檔,需要我們自己編譯。一般的至少都會包含三個目錄:include資料夾、lib資料夾和bin資料夾。include資料夾裡面包含了我們所需要的標頭檔案;lib資料夾中有的會提供靜態連結庫,有的會提供動態連結所用的連結庫檔案(Windows下特有的);bin資料夾下是動態連結庫(Windows下是dll檔案,Linux下是so檔案)。當然還可能會有一些其他檔案。
然後,我們在Visual Studio中新建一個C++工程,並且新建一個C++原始檔(cpp檔案)。在工程上右鍵Properties,我習慣在C/C++→General→Additional Include Directories中新增庫的include目錄,將標頭檔案包含進來。接下來在Linker→General→Additional Library Directories中新增lib庫所在目錄,在Linker→Input→Additional Dependencies中新增所需要的dll檔案(這裡需要新增那些dll檔案和你程式中所要使用到的函式功能有關)。
最後,記得將庫檔案所在的bin目錄新增到你的path環境變數中。Windows下在高階系統設定→環境變數中進行設定。

所以,怎麼使用SFML和GLEW庫應該不用多說了吧!如果有朋友遇到問題了,可以百度其它部落格,上面應該有更詳細的介紹或者說明。

HelloWorld示例程式

下面我們新建一個C++控制檯程式,然後再新建一個cpp檔案,配置好需要的SFML和GLEW庫,開始編寫程式碼。
這裡我們需要配置的連結庫檔案包括:
opengl32.lib
glu32.lib
glew32.lib
sfml-system-d.lib
sfml-window-d.lib

#include <GL/glew.h>
#include <SFML/Window.hpp>

#define GLSL(src) "#version 150 core\n" #src

// Vertex渲染器程式碼片段
const GLchar* vertexSource = GLSL(
    in vec2 position;
    in vec3 color;
    out vec3 mColor;
    void main() {
        mColor = color;
        gl_Position = vec4(position, 0.0f, 1.0f);
    }
);

// Fragment渲染器片段
const GLchar* fragmentSource = GLSL(
    in vec3 mColor;
    out vec4 oColor;
    void main() {
        oColor = vec4(mColor, 1.0f);
    }
);

// 建立指定型別的Shader
GLuint createShader(GLenum type, const GLchar* src)
{
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, &src, nullptr);
    glCompileShader(shader);
    return shader;
}

int main(int argc, char* argv[])
{
    // 初始化Window視窗
    sf::ContextSettings settings;
    settings.depthBits = 24;
    settings.stencilBits = 8;

    const unsigned int WIDTH = 800;
    const unsigned int HEIGHT = 600;
    const sf::String TITLE = "Modern OpenGL";
    sf::Window window(sf::VideoMode(WIDTH, HEIGHT, 32), TITLE, 
        sf::Style::Titlebar | sf::Style::Close, settings);

    // 初始化GLEW
    glewExperimental = GL_TRUE;
    glewInit();

    // 頂點資料
    GLfloat vertices[] = {
        0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
        -0.5f, -0.5f, 0.0f, 0.0f, 1.0f
    };

    // 建立Vertex Array Object
    GLuint VAO;
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // 建立Vertex Buffer Object並裝載資料
    GLuint VBO;
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 建立Vertex渲染器和Fragment渲染器
    GLuint vertexShader = createShader(GL_VERTEX_SHADER, vertexSource);
    GLuint fragmentShader = createShader(GL_FRAGMENT_SHADER, fragmentSource);

    // 將Vertex渲染器和Fragment渲染器關聯到Shader Program
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glUseProgram(shaderProgram);

    // 設定Vertex資料的佈局屬性(這裡包括postion和color兩個屬性)
    GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
    glEnableVertexAttribArray(posAttrib);
    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), 0);

    GLint colorAttrib = glGetAttribLocation(shaderProgram, "color");
    glEnableVertexAttribArray(colorAttrib);
    glVertexAttribPointer(colorAttrib, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));

    // 這個While迴圈是SFML的固定模式用於做事件處理
    while (window.isOpen())
    {
        // 內層While迴圈用於處理事件響應
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }
        window.setActive();
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        // 繪製圖形
        glDrawArrays(GL_TRIANGLES, 0, 3);
        window.display();
    }
    // 釋放資源
    glDeleteProgram(shaderProgram);
    glDeleteShader(fragmentShader);
    glDeleteShader(vertexShader);

    glDeleteBuffers(1, &VBO);
    glDeleteVertexArrays(1, &VAO);

    return EXIT_SUCCESS;
}

下面大概說明一下上面的程式,具體的函式說明,等有時間了再後續的部落格中再一一道來。
1-2行聲明瞭包含了標頭檔案,一個為glew標頭檔案,一個為SFML的Window標頭檔案
4行是一個巨集定義,用於將GLSL的原始檔和前面的版本宣告資訊連結起來。
7-15行是一個以字串表示的GLSL源程式,是一個Vertex Shader。用於接收輸入的頂點位置和顏色資訊,並輸出顏色資訊傳遞給下一個渲染階段。
18-24行也是一個以字串表示的GLSL源程式,是一個Fragment Shader。接收顏色資訊的輸入,並輸出用於Fragment渲染。
對於Vertex Shader和Fragment Shader維基百科中的解釋如下:

A Vertex Shader in OpenGL is a piece of C like code written to the GLSL specification which influences the attributes of a vertex. Vertex shaders can be used to modify properties of the vertex such as position, color, and texture coordinates.

A Fragment Shader is similar to a Vertex Shader, but is used for calculating individual fragment colors. This is where lighting and bump-mapping effects are performed.

27-33行定義了一個用於建立Shader的函式。
38-46使用SFML庫定義了顯示圖形的視窗。
49-50初始化GLEW。
53-69定義頂點資料,建立VAO和VBO物件,並在VBO中裝載資料。對於VAO和VBO,維基百科給出了這樣的解釋:

A Vertex Array Object (VAO) is an object which contains one or more Vertex Buffer Objects and is designed to store the information for a complete rendered object. In our example this is a diamond consisting of four vertices as well as a color for each vertex.

A Vertex Buffer Object (VBO) is a memory buffer in the high speed memory of your video card designed to hold information about vertices. In our example we have two VBOs, one that describes the coordinates of our vertices and another that describes the color associated with each vertex. VBOs can also store information such as normals, texcoords, indicies, etc.

71-79行建立Vertex渲染器和Fragment渲染器並將其關聯到Shader Program。
82-88行設定Vertex資料的佈局屬性(這裡包括postion和color兩個屬性),將頂點資料傳遞給GLSL程式。
91-106行用於使用者視窗事件處理,同時在While迴圈裡面繪製圖形。
108-113是最後資源的釋放。

相關推薦

現代OpenGL一個OpenGL程式

OpenGL簡介 OpenGL是一種應用程式程式設計介面(Application Programming Interface,API)它是一種可以對圖形硬體裝置特徵進行訪問的軟體庫。 在OpenGL 3.0以前的版本或者使用相容模式的OpenGL環境,Ope

Learn OpenGL 開啟視窗

原文 Hello Window 作者 JoeyDeVries 翻譯 Geequlim, Krasjet 環境VS2015+OpenGL3.3 #includ

java基礎對java的三個環境變數的簡單理解和配置

首先說說java的三個環境變數:java_home,classpath,path java_home:jdk的安裝路徑【你一層一層點開安裝路徑,直到當前目錄有一個bin目錄,然後在位址列裡面右鍵單擊複製地址就是jdk的安裝路徑(eg:D:\Java\jdk1.8.0_65)】,沒有其實也可以,在用到jdk的

別人犯錯給自己的警醒老了會不會也那樣?

最近和一位政治老師有了一些聊天和溝通,這位老師絕對是一個負責任、有點情懷和理想的人,她每天都會在微信上寫一些文字,她說有些想法不寫下來就會忘記,寫下了一能夠留存,二便於以後反思。然後我看了她的微信,有一些是個人生活方面的感受,一些是生活的瑣事,我有時候會感覺她只是需要聽眾,

小甲魚《零基礎學習Python》課後筆記和Python的第一次親密接觸

0.  Python是什麼型別的語言? Python是指令碼語言。Python相比C和Java,它實現一個功能可能10行,而Java要100行,C要1000行,這只是一個比喻,不過也說明Python是一種高階的語言。Python因為其有全球開發者建立了數量眾多的庫,使得開發

Java學習第一章 計算機、程式和Java概述

第一章 計算機、程式和Java概述   (1)匯流排--》    儲存裝置、記憶體、CPU、通訊裝置、輸入裝置、輸出裝置; (2)語言: 機器語言: 二進位制形式

菜鳥WDF驅動開發系列2除錯一個KMDF驅動程式

根據系列上一篇的內容,已經基本作好了驅動除錯環境的配置,現在著手開始試一下怎麼用WinDBG除錯。每一位新手在開始學習驅動開發的時候相信總會看大量的資料,如我第一篇提到的幾本書的確是不錯的,但名著總有一個問題就是,開篇一開始總是會鋪陳太多的基本理論,這會讓我們覺得手足無措,

android 開發問題集SDK更新後 執行程式報錯Location of the Android SDK has not been setup in the preferences

問題描述:android跟新後報錯location of the android sdk has not been setup in the preferences。eclipse裡的SDK Manager也打不開 原因:android跟新後ADT的版本過低造成的,但是有的人又因為跟新ADT失敗導致,也

Android1建立一個android工程檔案

在啟動了android模擬器之後,便可以開始新建android工程了 (1)File--》New--》Android Application Project(或者File--》New--》Other--》Android--》Android Application Proje

ASP.NET Core系列建立一個.Net Core 專案

前面講過 .NET Core簡介及開發環境安裝,本章會講一講ASP.NET Core 2.0的專案結構,檢視完整的ASP.NET Core系列文章:https://www.cnblogs.com/zhangweizhong/category/1477144.html   新建專案 新建專案,

thrift學習筆記 thrift簡介及第一個helloword程式

簡介 facebook開源的RPC框架,秉承了Facebook一貫的只管拉屎不管擦屁股的作風. Thrift是一個軟體框架,用來進行可擴充套件且跨語言的服務的開發。它結合了功能強大的軟體堆疊和程式碼生成引擎,以構建在 C++, Java, Python, P

在VMware虛擬機器下安裝Android StudioAS以及執行一個HelloWorld程式

1、JDK 1.1下載  百度一下“JDK”即可 1.2安裝 選擇安裝目錄 C:\java JAVA_HOME→C:\java         PATH→C:\java\bin 2、android studio (AS) 2.2安裝  遇到的問題: 1.新建

現代電子系統設計學習總結

短短兩週的現代電子系統設計就這樣結束了,有些意猶未盡啊。 關於PSoC部分的總結已經單獨列出來了, 這裡再補充幾點吧。 一:USB HID例程很好,可以仔細分析一下,可以實現插上電腦就能輕易搜尋和拷貝電腦上的資料,搜尋所有的doc檔案拷貝到移動盤,真是非常黑客的應用啊。xc

對hyperledger fabric1.1.0的執著基本環境整理

我用的作業系統為:centos7.4,語言準備用java開發。 整理包括:linux核心升級(若升級失敗,可參考https://blog.csdn.net/tianshuhao521/article/details/84021064),jdk安裝,docker(docker ce,docker-

現代通訊原理緒論

1、談談通訊發展的歷史     前些年做通訊講座, 都用一個笑話開頭:在通訊發展的歷史上,犯了兩個錯誤。第一個錯誤是,原以為電話應該是有線的,後來才發現其實是無線的。第二個錯誤是,原以為電視應該是無線的,後來才發現其實是有線的。     當然這個笑話也是我聽來

對hyperledger fabric的執著環境整理

我用的作業系統為:centos7.4,語言準備用java開發。 整理包括:linux核心升級,jdk安裝,docker(docker ce,docker-compose)安裝,go語言環境安裝。 1、Docker要求的linux核心版本最低為3.10,故需將linux核心

OpenGL學習筆記(七)建立一個Qt5.9.3 OpenGL工程模版與平臺無關

        main.cpp中輸入原始碼,原始碼如下: #include <iostream> #include <process.h> #include <GLFW/glfw3.h> #include <glad/glad.h> #include <

的Unity遊戲開發筆記之特效基礎粒子效果面板

如何在Unity中實現粒子效果? 首先,右鍵點選 Hierarchy欄,選擇effects->Particle System,這樣你就新建了一個粒子系統,如圖所示: 下面來介紹右側監視器(Inspector)內的內容: 首先是Transform,分別為Positio

《你的月亮的C》型別定義(typedef)

    “typedef關鍵字儘管在語法上是一種儲存型別,但正如其名所示,它用來定義新的型別名稱,而不是定義新的變數或函式”                                                                            

的計算機視覺之路轉行CV

        2009年,我剛剛高考完時,電力行業就業形勢大好,國家對航空航天行業的剛性需求帶領著北航一路躥紅,而電子通訊行業正直鼎盛時期。計算機專業雖然也勢頭正好,但與今天相比就差的遠了,街坊鄰居都說計算機專業要飽和了。當時年幼無知,就聽信了這些話,選擇了電子通訊行業