OpenGL 3.x教學: 第1章: Shader Based OpenGL

| | 4 Comments| 13:48
Categories:

從OpenGL 進入 3.0 版之後,很多舊有的 Function 開始標記成 Deprecated。從 3.1版開始把 Fixed-Function Pipeline 的相關 OpenGL Function 通通移至 Compatible Profile , Core Profile 只留下 Programmable pipeline 相關的 Function 。在 OpenGL Shading Language (GLSL) 的部分,則隨著新功能的增加而改版 。此外,最近 Khronos Group 發表了OpenGL 3.3 與 OpenGL 4.0,相對於 3.2 版增加新功能,並沒有移除更多的 Function 。以下是 OpenGL 版本與 GLSL 版本發表時間的對應。

OpenGL Version OpenGL Shading Language Version
OpenGL 4.0 GLSL 4.0
OpenGL 3.3 GLGL 3.3
OpenGL 3.2 GLSL 1.5
OpenGL 3.1 GLSL 1.4
OpenGL 3.0 GLSL 1.3
OpenGL 2.1 GLSL 1.2
OpenGL 2.0 GLSL 1.1

所 以OpenGL 3.x 之後的 Core Profile 與 OpenGL ES 2.0 (未來的WebGL) 都只剩下 Programmable Rendering Pipeline。因此,不管要畫什麼,既使只是一個簡單的三角形,都需要撰寫 Vertex Shader 與 Fragment Shader 才行。

而在 OpenGL 3.x 的資料結構上,也變成以 Buffer Object 為主。所以幾乎所有資料,都需要放到各種的 Buffer Object來使用,下面兩張圖是由 OpenGL 管理單位 Khronos Group 簡報 OpenGL 3 跟 OpenGL 4 的 Buffer Object Model 架構。

buffer buffer4

Khronos Group 在09年 GDC 簡報的OpenGL 3 資料

Khronos Group 在 2010年 GDC 簡報的OpenGL 4 資料

參考資料來 源網站: http://www.khronos.org/

現在第一個範例:畫出個三角形,我們以第 0 章的程式為基礎,若要畫出一個三角形,只要添加下面流程 2、 3、 4 部分的程式碼:

  1. Initial OpenGL Canvas
  2. Load 繪圖所需的資料 / 建立儲存資料的資料結構
  3. Initial OpenGL state 與 各種 OpenGL buffer objects
  4. Draw and Display
  5. 接 受各種事件,根據事件變更資料結構的內容、OpenGL State 或是 OpenGL buffer objects
  6. 反覆流程 4 與流程 5 直至程式結束

首先是流程 2,在這個例子裡只要準備三角形的資料、Vertex Shdaer 程式碼與 Fragment Shader 程式碼,因為都直接寫死放在程式的變數內,所以只需要有變數的宣告就可以把Init () 省略。

首先三角形的資料很簡單,使用一個 Array 儲存三角形頂點資料即可。

GLfloat afVertex[][3]=
{
{-0.75,-0.5,0.0},
{0.75,-0.5,0.0},
{0.0,0.75,0.0}
};
三個三角形頂點資料

而 Vertex Shader 與 Fragment Shader,也簡單使用 char pointer 來存。

Vertex Shader 程式碼
const char* cpVShader = {
"#version 150
"
""
"in vec3 vPos;"
""
"void main() {"
" gl_Position = vec4(vPos,1);"
"}"
};

"in vec3 vPos;" 表示 Vertex Shader 會接受 三個 float 的 Vertex Attribute 「Vpos」變數輸入。

然後 Vertex Shader 的預設 Vertex 輸出為內建的變數 gl_Position,Type 為 4個 float 的 Vector 。

因為只有要畫單純的三角形,並沒有要對三角形多做處理,所以在Vertex Shader 裡面 直接把輸入的 vPos 變成 homogenious coordinate 也就是後面再加個 1,放到 gl_Position 裡 。

Fragment Shader 程式碼
const char* cpFShader = {
"#version 150
"
""
"out vec4 fColor;"
""
"void main() {"
" fColor = vec4( 1, 1, 1, 1 );"
"}"
};

"out vec4 fColor;" 宣告輸出的 Fragment 變數名稱。

然後設定三角形上面的每個 Fragment 用 ( 1, 1, 1, 1) 的白色輸出。

接下來資料都準備好了,開始進行流程 3 撰寫 initGL 的內容。

void initGL()
{
glGenBuffers(1,&uVbo );
glBindBuffer(GL_ARRAY_BUFFER,uVbo);
glBufferData(GL_ARRAY_BUFFER,sizeof(afVertex), afVertex, GL_STATIC_DRAW );
產生一個 Vertex Buffer Object ,並且把GL_ARRAY_BUFFER 設定為 uVbo ,然後將Array的頂點資料存到這個 GL_ARRAY_BUFFER 設定的 uVbo 這個buffer object裡。
uProgram = glCreateProgram();
AttachProgram( uProgram, cpVShader , GL_VERTEX_SHADER );
AttachProgram( uProgram, cpFShader , GL_FRAGMENT_SHADER );
glLinkProgram( uProgram );
產生一個 Shader Program,然後分別把Vertex Shader、Fragment Shader attach 至program上,最後 Link 起來可以在 Draw時使用這個 Shader Program。
uPos = glGetAttribLocation( uProgram, "vPos" ); 取得 Shader Program 裡面 vPos 這個變數的位置。
glClearColor( 0.0, 0.0, 1.0, 1.0 ); 設 定OpenGL Canvus 背景顏色。
}

這邊 InitGL() 主要作了四件事,分別是產生一個 Vertex Buffer Object ,一個 Shader Program ,取得 Shader Program 裡面 vPos 變數的輸入位置。

最後就是顯示的部分 Display 這個 Function:

glUseProgram(uProgram); 設定現在使用的 Shader Program為 uProgram
glBindBuffer( GL_ARRAY_BUFFER, uVbo ); 設定現在使用的 GL_ARRAY_BUFFER 為uVbo
glVertexAttribPointer( uPos, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) ); 設定 GL_ARRAY_BUFFER 裡面 Vertex 資料的格式,並且設定資料是當作 Shader Program 內 uPos 位置的變數內容
glEnableVertexAttribArray( uPos ); Enable uPos 這個 Vertex attribute array
glDrawArrays( GL_TRIANGLES, 0, 3 ); 最後在 Vertex Array 裡從第 0 個開始取三個 Vertex 當成三角形來畫

這樣就可以畫出一個白色的三角形了。

一些概念

OpenGL 的運作是一個 State Machine,所以程式執行是根據 OpenGL 內部各種 State 的狀況,因此常見的模式就是設定某些 State 狀態,呼叫 Function 針對現在的 State 執行,透過如此的反覆進行完成一個 OpenGL程式。

以下是這個範例的原始檔: Lession 1.zip

4 thoughts on “OpenGL 3.x教學: 第1章: Shader Based OpenGL”

  1. 您好我想在linux環境下編譯opengl 3.0以上的版本也就是用到glsl,請問我要怎麼建制我的環境需要哪些lib,compile command要用什麼,希望大大不吝指教謝謝

  2. 我這邊的linux使用的都是配有nvidia顯示卡的主機,所以就是安裝nvidia的driver,然後安裝glew這套library,使用其header file.link時的library就照常使用-lGL -lGLU 以及如果是用glut就加上-lglut. 這邊使用到的GL library 就是安裝nvidia driver時提供的library.

  3. 請問一下使用shader真的可以幫opengl加速繪製嗎?
    譬如我一條現有200頂點
    我總共有1萬條線
    我該如何利用opengl渲染來加速?

  4. shader是為了能夠更彈性的繪製圖形,至於要使用OpenGL加速,只要使用OpenGL的API寫程式繪圖,然後搭配上支援OpenGL的顯示卡與正確的驅動程式即可.

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *