OpenGL學習筆記(十一)

着色器

着色器(Shader)是運行在GPU上的小程序,這些小程序爲圖形渲染管線的某個特定部分而運行。從基本意義上來說,着色器只是一種把輸入轉化爲輸出的程序。着色器也是一種非常獨立的程序,因爲它們之間不能相互通信;它們之間唯一的溝通只有通過輸入和輸出。

GLSL

着色器是使用一種叫GLSL的類C語言寫成的。GLSL是爲圖形計算量身定製的,它包含一些針對向量和矩陣操作的有用特性。

着色器的開頭總是要聲明版本,接着是輸入和輸出變量、uniform和main函數。每個着色器的入口點都是main函數,在這個函數中我們處理所有的輸入變量,並將結果輸出到輸出變量中。

一個典型的着色器有下面的結構:

#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;

通常情況下它至少會返回16個,大部分情況下是夠用了。

輸入與輸出

雖然着色器是各自獨立的小程序,但是它們都是一個整體的一部分,出於這樣的原因,我們希望每個着色器都有輸入和輸出,這樣才能進行數據交流和傳遞。GLSL定義了in和out關鍵字專門來實現這個目的。每個着色器使用這兩個關鍵字設定輸入和輸出,只要一個輸出變量與下一個着色器階段的輸入匹配,它就會傳遞下去。但在頂點和片段着色器中會有點不同。

頂點着色器應該接收的是一種特殊形式的輸入,否則就會效率低下。頂點着色器的輸入特殊在,它從頂點數據中直接接收輸入。爲了定義頂點數據該如何管理,我們使用location這一元數據指定輸入變量,這樣我們纔可以在CPU上配置頂點屬性。頂點着色器需要爲它的輸入提供一個額外的layout標識,這樣我們才能把它鏈接到頂點數據。

另一個例外是片段着色器,它需要一個vec4顏色輸出變量,因爲片段着色器需要生成一個最終輸出的顏色。如果你在片段着色器沒有定義輸出顏色,OpenGL會把你的物體渲染爲黑色(或白色)。

所以,如果我們打算從一個着色器向另一個着色器發送數據,我們必須在發送方着色器中聲明一個輸出,在接收方着色器中聲明一個類似的輸入。當類型和名字都一樣的時候,OpenGL就會把兩個變量鏈接到一起,它們之間就能發送數據了(這是在鏈接程序對象時完成的)。

Uniform

Uniform是一種從CPU中的應用向GPU中的着色器發送數據的方式,但uniform和頂點屬性有些不同。首先,uniform是全局的(Global)。全局意味着uniform變量必須在每個着色器程序對象中都是獨一無二的,而且它可以被着色器程序的任意着色器在任意階段訪問。第二,無論你把uniform值設置成什麼,uniform會一直保存它們的數據,直到它們被重置或更新。

如果你聲明瞭一個uniform卻在GLSL代碼中沒用過,編譯器會靜默移除這個變量,導致最後編譯出的版本中並不會包含它,這可能導致幾個非常麻煩的錯誤,記住這點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章