著色器(Shader)是運行在GPU上的小程序哆键,為圖形渲染管線的某個特定部分而運行慈省。
著色器只是一種把輸入轉化為輸出的程序憔维。
著色器是一種非常獨立的程序炬守,因為它們之間不能相互通信牧嫉;它們之間唯一的溝通只有通過輸入和輸出剂跟。
GLSL
- 著色器是使用一種叫GLSL的類C語言寫成的减途。
- 著色器的開頭總是要聲明版本,
接著是輸入和輸出變量曹洽、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分量的頂點屬性可用煤杀。
數據類型
GLSL有數據類型可以來指定變量的種類。
GLSL中包含C等其它語言大部分的默認基礎數據類型:
int
谒出、float
隅俘、double
、uint
和bool
GLSL也有兩種容器類型:
向量(Vector)
和矩陣(Matrix)
向量
GLSL中的向量是一個可以包含有1笤喳、2为居、3或者4個分量的容器。
分量的類型可以是前面默認基礎類型的任意一個杀狡。
類型 | 含義 |
---|---|
vecn | 包含n個float分量的默認向量 |
bvecn | 包含n個bool分量的向量 |
ivecn | 包含n個int分量的向量 |
uvecn | 包含n個unsigned int分量的向量 |
dvecn | 包含n個double分量的向量 |
- 大多數時候我們使用vecn
- 一個向量的分量可以通過vec.x這種方式獲取蒙畴,這里x是指這個向量的第一個分量。你可以分別使用.x呜象、.y膳凝、.z和.w來獲取它們的第1八孝、2、3鸠项、4個分量干跛。
- GLSL也允許你對顏色使用rgba,或是對紋理坐標使用stpq訪問相同的分量祟绊。
向量的重組(Swizzling):
重組允許這樣的語法:
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
我們也可以把一個向量作為一個參數傳給不同的向量構造函數楼入,以減少需求參數的數量:
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
輸入與輸出
每個著色器都有輸入和輸出,這樣才能進行數據交流和傳遞牧抽。
GLSL定義了in
和out
關鍵字專門來實現這個目的嘉熊。
每個著色器使用這兩個關鍵字設定輸入和輸出,只要一個輸出變量與下一個著色器階段的輸入匹配阐肤,它就會傳遞下去。
但也有例外讲坎,在頂點和片段著色器中:
頂點著色器的輸入特殊在孕惜,它從頂點數據中直接接收輸入。
頂點著色器需要為它的輸入提供一個額外的layout標識晨炕,這樣我們才能把它鏈接到頂點數據衫画。
如layout (location = 0)
另一個例外是片段著色器,它需要一個vec4顏色輸出變量瓮栗,因為片段著色器需要生成一個最終輸出的顏色削罩。如果你在片段著色器沒有定義輸出顏色,OpenGL會把你的物體渲染為黑色(或白色)费奸。
Uniform
Uniform是一種從CPU中的應用向GPU中的著色器發(fā)送數據的方式弥激。
- uniform是全局的(Global):
全局意味著uniform變量必須在每個著色器程序對象中都是獨一無二的,而且它可以被著色器程序的任意著色器在任意階段訪問愿阐。 - 無論你把uniform值設置成什么微服,uniform會一直保存它們的數據,直到它們被重置或更新换况。
我們可以在一個著色器中添加uniform關鍵字至類型和變量名前來聲明一個GLSL的uniform职辨。從此處開始我們就可以在著色器中使用新聲明的uniform了。
#version 330 core
out vec4 FragColor;
uniform vec4 ourColor; // 在OpenGL程序代碼中設定這個變量
void main()
{
FragColor = ourColor;
}
如果你聲明了一個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);
我們通過glfwGetTime()獲取運行的秒數.
然后我們使用sin函數讓顏色在0.0到1.0之間改變,將結果儲存到greenValue里
接著榜聂,我們用glGetUniformLocation查詢uniform ourColor的位置值
最后搞疗,我們可以通過glUniform4f函數設置uniform值
注意,查詢uniform地址不要求你之前使用過著色器程序须肆,但是更新一個uniform之前你必須先使用程序(調用glUseProgram)匿乃,因為它是在當前激活的著色器程序中設置uniform的
uniform對于設置一個在渲染迭代中會改變的屬性是一個非常有用的工具,它也是一個在程序和著色器間數據交互的很好工具豌汇。
更多屬性幢炸!
在前面的教程中,我們了解了如何填充VBO拒贱、配置頂點屬性指針以及如何把它們都儲存到一個VAO里宛徊。這次,我們同樣打算把顏色數據加進頂點數據中逻澳。我們將把顏色數據添加為3個float值至vertices數組闸天。我們將把三角形的三個角分別指定為紅色、綠色和藍色:
float vertices[] = {
// 位置 // 顏色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 頂部
};