一: 着色器
- 着色器只是一種把輸入轉化為輸出的程序。着色器也是一種非常獨立的程序,因為它們之間不能相互通信;它們之間唯一的溝通只有通過輸入和輸出。
- 在最簡配置下,至少都得有兩個着色器:一個叫頂點着色器(vertex shader),它將作用於每個頂點上;另一個叫片段着色器(fragment shader),它將作用於每一個采樣點。我們采用4倍抗鋸齒,因此每個像素有四個采樣點。
- 如果我們打算從一個着色器向另一個着色器發送數據,我們必須在發送方着色器中聲明一個輸出,在接收方着色器中聲明一個類似的輸入。當類型和名字都一樣的時候,OpenGL就會把兩個變量鏈接到一起,它們之間就能發送數據了(這是在鏈接程序對象時完成的)。
二: 頂點着色器
- 頂點着色器應該接收的是一種特殊形式的輸入,否則就會效率低下。頂點着色器的輸入特殊在,它從頂點數據中直接接收輸入。
- 為了定義頂點數據該如何管理,我們使用location這一元數據指定輸入變量,這樣我們才可以在CPU上配置頂點屬性。比如:layout (location = 0)。頂點着色器需要為它的輸入提供一個額外的layout標識,這樣我們才能把它鏈接到頂點數據。
- 為了設置頂點着色器的輸出,我們必須把位置數據賦值給預定義的gl_Position變量,它在幕后是vec4類型的。在main函數的最后,我們將gl_Position設置的值會成為該頂點着色器的輸出。由於我們的輸入是一個3分量的向量,我們必須把它轉換為4分量的。我們可以把vec3的數據作為vec4構造器的參數,同時把w分量設置為1.0f(我們會在后面解釋為什么)來完成這一任務。
- 將更多的屬性數據如顏色等添加進頂點數據中:
4.1 調整頂點着色器,使它能夠接收顏色值作為一個頂點屬性輸入。需要注意的是我們用layout標識符來把aColor屬性的位置值設置為1。
4.2 因為我們添加了另一個頂點屬性,並且更新了VBO的內存,我們就必須重新配置頂點屬性指針。
三: 片段着色器
- 片段着色器(Fragment Shader)是第二個也是最后一個我們打算創建的用於渲染三角形的着色器。片段着色器所做的是計算像素最后的顏色輸出。
- 片段着色器需要一個vec4顏色輸出變量,因為片段着色器需要生成一個最終輸出的顏色。如果你在片段着色器沒有定義輸出顏色,OpenGL會把你的物體渲染為黑色(或白色)。
- 片段着色器中進行的所謂片段插值(Fragment Interpolation)的結果。當渲染一個三角形時,光柵化(Rasterization)階段通常會造成比原指定頂點更多的片段。光柵會根據每個片段在三角形形狀上所處相對位置決定這些片段的位置。
- 基於這些位置,它會插值(Interpolate)所有片段着色器的輸入變量。比如說,我們有一個線段,上面的端點是綠色的,下面的端點是藍色的。如果一個片段着色器在線段的70%的位置運行,它的顏色輸入屬性就會是一個綠色和藍色的線性結合;更精確地說就是30%藍 + 70%綠。
- 這也就是為什么只設置了頂點的顏色,畫出來的三角形都有顏色了。即:我們有3個頂點,和相應的3個顏色,從這個三角形的像素來看它可能包含50000左右的片段,片段着色器為這些像素進行插值顏色。如果你仔細看這些顏色就應該能明白了:紅首先變成到紫再變為藍色。片段插值會被應用到片段着色器的所有輸入屬性上。
四: uniform變量
- 首先,uniform是全局的(Global)。全局意味着uniform變量必須在每個着色器程序對象中都是獨一無二的,而且它可以被着色器程序的任意着色器在任意階段訪問。
- 第二,無論你把uniform值設置成什么,uniform會一直保存它們的數據,直到它們被重置或更新。
- 如果你聲明了一個uniform卻在GLSL代碼中沒用過,編譯器會靜默移除這個變量,導致最后編譯出的版本中並不會包含它,這可能導致幾個非常麻煩的錯誤。
- 給uniform變量添加數據:我們首先需要找到着色器中uniform屬性的索引/位置值。當我們得到uniform的索引/位置值后,我們就可以更新它的值了。
- 我們用glGetUniformLocation查詢uniform ourColor的位置值。我們為查詢函數提供着色器程序和uniform的名字(這是我們希望獲得的位置值的來源)。如果glGetUniformLocation返回-1就代表沒有找到這個位置值。
- 最后,我們可以通過glUniform4f函數設置uniform值。注意,查詢uniform地址不要求你之前使用過着色器程序,但是更新一個uniform之前你必須先使用程序(調用glUseProgram),因為它是在當前激活的着色器程序中設置uniform的。