opengl——繪製一個點
背景
這將是我們第一次遇到 GLEW,GLEW 是OpenGL的擴充套件功能庫。它能幫助你解決OpenGL不斷擴充套件的問題。一旦你初始化它之後,它在你的系統上將查詢所有可用的擴充套件功能,自動的載入它們並且通過一個單一的標頭檔案提供簡單的介面。
在這一節中,我們將第一次看見頂點緩衝區物件(VBOs)的使用。顧名思義,它們是被用來儲存頂點資料的。載入頂點進入 GPU 最有效率的方法是 VBOs。它們是可以儲存在視訊記憶體中的緩衝區,使得 GPU 訪問資料的速度最快。
這一節和下一節的內容在這本教程中是唯一講述固定管線而不是可程式設計管線的部分,事實上這兩節內容中我們也不會用到任何變換,我們僅僅是讓將資料傳遞給管線而已。
最後,光柵器根據指定繪圖命令(可以在下面的原始碼解析中看到)的拓撲結構畫出圖元。因為我們沒有繫結任何shader到管線中,所有頂點沒有進行矩陣變化。這將意味著為了在螢幕中繪製出這些點我們僅僅需要傳遞給程式一些在上述範圍之內的值就可以了。事實上,我們選取在螢幕中的零點作為x、y兩條軸的中心點,換句話說,零點是在螢幕的正中心。
apt-get install libglew1.6 libglew1.6-dev
來安裝它。
Sourcewalkthrough
#include<GL/glew.h>
這裡我們包含了一個單一的 GLEW
標頭檔案。如果你包含其他 OpenGL 標頭檔案,你必須把 GLEW
標頭檔案放在其他標頭檔案之前。
#include"math_3d.h"
在這一節我們開始使用其他的一些輔助工具比如 vector
。我們以後將會擴充套件這個標頭檔案。
GLenum res =glewInit();
if (res != GLEW_OK)
{
fprintf(stderr, "Error: '%s'n", glewGetErrorString(res));
return 1;
}
這裡我們初始化 GLEW
和檢查是否存在一些錯誤。這必須要在 GLUT
被初始化之前完成。
Vector3fVertices[1];
Vertices[0] =Vector3f(0.0f, 0.0f, 0.0f);
我們建立了一個型別為 Vector3f
(這個型別被定義在 math_3d.h
)的陣列,同時初始化 X、Y、Z為0,這將會使我們繪製的點出現在螢幕中間。
GLuint VBO;
我們分配一個全域性變數 GLuint
儲存頂點快取區物件的控制代碼,你稍後將看到大部分 OpenGL 物件都是通過一個 GLuint
型別的變數來訪問的。
glGenBuffers(1,&VBO);
OpenGL為了生成不同型別的物件定義幾個 glGen*
函式。這些函式通常有兩個引數,第一個引數指定你想要建立物件的數量,第二個引數是一個存放 GLuint
型別陣列的地址,這個陣列用於存放函式生成的控制代碼(確定陣列是足夠大來存放你的所申請的緩衝區控制代碼)。以後呼叫這個函式將不會生成相同的控制代碼,除非你使用 glDeleteBuffers
函式刪除它。在這個地方,你不需要指定用這些 buffers 用於做什麼,所以建立的是普通的 buffers
,指定這個 buffer
的功能是下面一個函式的任務
glBindBuffer(GL_ARRAY_BUFFER,VBO);
OpenGL 有一個獨一無二的方法使用控制代碼。在許多的 API 中,控制代碼只是簡單的通過一些相關的函式來使用。在 OpenGL 中我們繫結控制代碼到到一個目標上,然後在目標上執行命令。這些命令的執行隻影響到已經繫結的控制代碼,直到當前控制代碼被代替或者將0繫結到目標上。目標 GL_ARRAY_BUFFER
表示緩衝區用於儲存頂點陣列。另一個有用的目標是 GL_ELEMENT_ARRAY_BUFFER
表示緩衝區用於儲存陣列的索引。我們也可以將控制代碼繫結到別的目標上,我們將在下一節看到。
glBufferData(GL_ARRAY_BUFFER,sizeof(Vertices),Vertices,GL_STATIC_DRA);
當我們將緩衝區繫結到目標上之後,我們需要用資料進行填充。
上面函式的引數包括:目標點名稱(和上面緩衝區繫結的目標點一樣),資料的位元組數,頂點陣列的地址,以及一個表示資料使用方式的列舉量。因為我們不需要改變緩衝區中的內容,所謂我們將資料指定為 GL_STATIC_DRAW
,與之相反的我們可以將其設定為 GL_DYNAMIC_DRAW
。通過設定這個值,OpenGL可以對資料進行一些優化(比如在記憶體中哪個地方最適合儲存緩衝區)。
glEnableVertexAttribArray(0);
在 shaders 章節中,你將看見 Shader 中用到的頂點屬性(位置,法線等)有一個對映到它們的索引,使我們在 C/C++ 專案中建立的資料能與 Shader 內的屬性名之間進行繫結。此外你必須啟用每個頂點屬性的索引。在這一節中我們還沒使用任何 Shader,但是我們載入到緩衝器的頂點位置屬性在固定函式管線(在沒有繫結著色器的時候就自動啟用固定管線)中被看做頂點屬性索引為0。你必須啟用所有的頂點屬性,否則管線將接收不到資料。
glBindBuffer(GL_ARRAY_BUFFER,VBO);
由於將要呼叫繪圖函式,所以這裡我們再一次綁定了緩衝區。在這個小的專案中我們只有一個頂點緩衝區,所以在每一幀都呼叫這個函式是多餘的。但是在更復雜的專案中可能存在很多個緩衝區來儲存不同型別的變數,所以你必須用你打算使用的緩衝區來更新渲染管線狀態。
glVertexAttribPointer(0,3, GL_FLOAT, GL_FALSE, 0, 0);
這個函式呼叫告訴管線如何在緩衝區內部解釋資料。
第一個引數指定了屬性的索引。我們都知道0是預設的,但是當我們開始使用 Shaders 的時候,我們在 Shader 中需要明確的設定索引。第二個引數是構成屬性的分量的個數(X,Y,Z共三個分量)。第三個引數是每個分量的資料型別。
第四個引數表明屬性在管線中使用之前是否需要被規範化。
第五個引數是在緩衝區中兩個相同屬性值之間的間隔的位元組數,當只有一個屬性時(比如緩衝區只包含頂點位置)並且資料是緊挨著的,那麼我們設定這個值為0。如果我們有一個包含位置屬性和法線屬性的陣列(每個屬性都是一個float型別的三維向量),我們將這個值設定為6*4=24。
最後一個引數在上一個例子中是有用的。我們需要指定在緩衝區中儲存資料的偏移值,這樣管線才會找到資料。當頂點的位置和法線相鄰儲存時時,我們設定頂點位置的偏移值為0而頂點法線的偏移值為12。
glDrawArrays(GL_POINTS,0, 1);
最後,呼叫這個函式來畫幾何體。這裡就是 GPU 真正開始工作的地方。它現在將結合繪圖命令的引數,然後建立一個點並將結果渲染到螢幕。
OpenGL 提供多個型別的渲染命令,每一個都適合於不同的情況。通常你可以把它們分成兩個個類別——有序渲染和索引渲染。有序渲染相對來說比較簡單。GPU 遍歷你的頂點緩衝區,一個接著一個的訪問頂點,然後根據在函式中指定的引數類別解釋資料,例如,如果你指定 GL_TRIANGLES,那麼頂點0-2成為第一個三角形,3-5是第二個三角形等。如果你想讓相同的頂點出現在不同的三角形中,你將需要在頂點緩衝區指定它兩次,但是這樣將會浪費空間。
使用索引渲染相對來說複雜一點,並且涉及到了一個額外的緩衝區——索引緩衝區。索引緩衝區存放的是頂點在頂點緩衝區中的索引值。GPU 遍歷索引緩衝區,與順序渲染類似,他會將索引為0-2描述為第一個三角形。如果你想要相同的頂點在兩個三角形中,那麼只需要在索引緩衝區中簡單的指定該頂點的索引兩次,頂點緩衝區僅僅只需要包含一份資料。索引緩衝區在遊戲使用的更加普遍,因為大部分模型的表面(人的面板,城堡的牆等等)都是由共享頂點的三角形組成的。
在這一節中,我們使用最簡單的繪圖命令——glDrawArrays。這是一個已經順序繪製命令,所以這裡沒有儲存索引緩衝區。我們指定點的拓撲結構為 GL_POINTS ,這意味著頂點緩衝區中的每一個頂點都被繪製成一個點。下一個引數是開始繪製的第一個頂點的索引值。因為我們想要從緩衝區的開始來繪圖,所以我們指定引數為0。這使我們可以在相同的緩衝區儲存多個模型,在繪製圖形的時候只需要只需要選擇不同的偏移量就行了。最後一個引數是要用於繪製的頂點的數量。
glDisableVertexAttribArray(0);
當頂點不是要立即被使用的時候,禁用所有的頂點屬性是一個很不錯的方法。當著色器不使用頂點的時候將頂點屬性啟用,這是給自己自找麻煩。
操作結果
學習內容來源:https://www.bootwiki.com/opengl/opengl-modern-opengl-tutorial.html