OpenGL學習整理------著色器
1.著色器
著色器(Shader)是執行在GPU上的小程式。這些小程式為圖形渲染管線的某個特定部分而執行。從基本意義上說,著色器
只是一種把輸入轉化為輸出的程式。著色器也是一種非常獨立的程式,因為它們之間不能互相通訊;
著色器之間唯一的溝通只有通過輸入和輸出;
2.GLSL
著色器是用一種叫GLSL(OpenGL Shader Luanguage) 的類C語言寫成的。
典型的著色器結構如下
#version version_number in type in_variable_name; in type in_variable_name; out type out_variable_name; uniform type uniform_name; int main() { // 處理輸入並進行一些圖形操作 ... // 輸出處理過的結果到輸出變數 out_variable_name = weird_stuff_we_processed; }
特別對於頂點著色器,每個輸入變數也叫頂點屬性(Vertex Attribute)。我們能宣告的頂點屬性是有上限的,一般由硬體決定。
OpenGL確保至少有16個包含4分量的頂點屬性可用,有些硬體或許允許更多的頂點屬性,可以查詢GL_MAX_VERTEX_ATTRIBS來獲取具體上限:
int nrAttributes; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes); std::cout << "Maximum nr of vertex attributes supported:" << nrAttributes << std::endl;
3.資料型別
GLSL有資料型別可以來指定變數的種類。
資料型別: int、float、double、uin(unsigned int )t、bool;
容器型別: Vector、Matrix;
4.向量
GLSL中的向量是一個可以包含有1、2、3或者4個分量的容器,分量的型別可以是前面預設基礎型別的任意一個。它們可以是下面的形式(n
代表分量的數量):
型別 | 含義 |
vecn | 包含n 個float分量的預設向量 |
bvecn | 包含n 個bool分量的向量 |
ivecn | 包含n 個int分量的向量 |
uvecn | 包含n 個unsigned int分量的向量 |
dvecn | 包含n 個double分量的向量 |
大多數時候我們使用vecn
,因為float足夠滿足大多數要求了。
5.輸入輸出
我們希望每個著色器都有輸入和輸出,這樣才能進行資料交流和傳遞。
GLSL定義了in
和out
關鍵字專門來實現這個目的。每個著色器使用這兩個關鍵字設定輸入和輸出,只要一個輸出變數與下一個著色器階段的輸入匹配,它就會傳遞下去。
但在頂點和片段著色器中會有點不同。
頂點著色器應該接收的是一種特殊形式的輸入,否則就會效率低下。頂點著色器的輸入特殊在,它從頂點資料中直接接收輸入。
為了定義頂點資料該如何管理,我們使用location
這一元資料指定輸入變數,這樣我們才可以在CPU上配置頂點屬性。
我們已經在前面的教程看過這個了,layout (location = 0)
。頂點著色器需要為它的輸入提供一個額外的layout
標識,這樣我們才能把它連結到頂點資料。
你也可以忽略layout (location = 0)識別符號,通過在OpenGL程式碼中使用glGetAttribLocation查詢屬性位置值(Location),但是我更喜歡在著色器中設定它們,這樣會更容易理解而且節省你(和OpenGL)的工作量。
另一個例外是片段著色器,它需要一個vec4
顏色輸出變數,因為片段著色器需要生成一個最終輸出的顏色。
如果你在片段著色器沒有定義輸出顏色,OpenGL會把你的物體渲染為黑色(或白色)。
6.Uniform
Uniform是一種從CPU中的應用向GPU中的著色器傳送資料的方式,但uniform和頂點屬性有些不同。
首先,uniform是全域性的(Global)。全域性意味著uniform變數必須在每個著色器程式物件中都是獨一無二的,而且它可以被著色器程式的任意著色器在任意階段訪問。
第二,無論你把uniform值設定成什麼,uniform會一直儲存它們的資料,直到它們被重置或更新。
頂點著色器中不需要這個uniform,所以我們不用在那裡定義它。
如果你聲明瞭一個uniform卻在GLSL程式碼中沒用過,編譯器會靜默移除這個變數,導致最後編譯出的版本中並不會包含它,這可能導致幾個非常麻煩的錯誤,記住這點!
當我們在著色器中定義了Uniform後,我們還沒有給它新增任何資料,所以下面我們就做這件事。
我們首先需要找到著色器中uniform屬性的索引/位置值。當我們得到uniform的索引/位置值後,我們就可以更新它的值了。
這次我們不去給畫素傳遞單獨一個顏色,而是讓它隨著時間改變顏色:
float timeValue = glfwGetTime(); float greenValue = (sin(timeValue) / 2.0f) + 0.5f; int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); glUseProgram(shaderProgram); glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);