GLSL 程式設計/Unity/RGB 立方體

本教程介紹了**變化量變數**。它基於“最小著色器”部分。
在本教程中,我們將編寫一個著色器來渲染類似於左側所示的 RGB 立方體。表面上每個點的顏色由其座標決定;即,位於位置 的點具有顏色。例如,點 對映到顏色,即純藍色。(這是左側圖形右下角的藍色角。)
由於我們要建立一個 RGB 立方體,您首先需要建立一個立方體遊戲物件。如“最小著色器”部分中對球體的描述,您可以透過從主選單中選擇**GameObject > Create Other > Cube** 來建立一個立方體遊戲物件。繼續建立材質和著色器物件,並將著色器附加到材質,並將材質附加到立方體,如“最小著色器”部分中所述。
以下是著色器程式碼,您應該將其複製並貼上到您的著色器物件中
Shader "GLSL shader for RGB cube" {
SubShader {
Pass {
GLSLPROGRAM
#ifdef VERTEX // here begins the vertex shader
varying vec4 position;
// this is a varying variable in the vertex shader
void main()
{
position = gl_Vertex + vec4(0.5, 0.5, 0.5, 0.0);
// Here the vertex shader writes output data
// to the varying variable. We add 0.5 to the
// x, y, and z coordinates, because the
// coordinates of the cube are between -0.5 and
// 0.5 but we need them between 0.0 and 1.0.
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif // here ends the vertex shader
#ifdef FRAGMENT // here begins the fragment shader
varying vec4 position;
// this is a varying variable in the fragment shader
void main()
{
gl_FragColor = position;
// Here the fragment shader reads intput data
// from the varying variable. The red, gree, blue,
// and alpha component of the fragment color are
// set to the values in the varying variable.
}
#endif // here ends the fragment shader
ENDGLSL
}
}
}
如果您的立方體沒有正確著色,請檢查控制檯中的錯誤訊息(透過從主選單中選擇**Window > Console**),確保您已儲存著色器程式碼,並檢查您是否已將著色器物件附加到材質物件,並將材質物件附加到遊戲物件。
我們的著色器的主要任務是在片段著色器中將輸出片段顏色(gl_FragColor)設定為頂點著色器中可用的位置(gl_Vertex)。實際上,這並不完全正確:Unity 預設立方體的 gl_Vertex 中的座標介於 -0.5 和 +0.5 之間,而我們希望顏色分量介於 0.0 和 1.0 之間;因此,我們需要向 x、y 和 z 分量新增 0.5,這由以下表達式完成:gl_Vertex + vec4(0.5, 0.5, 0.5, 0.0)。
然而,主要問題是:我們如何從頂點著色器獲取任何值到片段著色器?事實證明,做到這一點的**唯一**方法是使用變化量變數(簡稱變化量)。頂點著色器的輸出可以寫入變化量變數,然後片段著色器可以將其作為輸入讀取。這正是我們需要的。
要指定變化量變數,必須在頂點著色器和片段著色器中任何函式之外使用修飾符 varying(在型別之前)來定義它;在我們的示例中:varying vec4 position;。接下來是關於變化量變數的最重要規則
| 頂點著色器中變化量變數定義的型別和名稱必須與片段著色器中變化量變數定義的型別和名稱完全匹配,反之亦然。 |
這是為了避免 GLSL 編譯器無法確定頂點著色器的哪個變化量變數應該與片段著色器的哪個變化量變數匹配的歧義情況。
頂點和片段著色器中變化量變數定義必須匹配的要求通常會導致錯誤,例如,如果程式設計師更改了頂點著色器中變化量變數的型別或名稱,但忘記在片段著色器中更改它。幸運的是,Unity 中有一個很好的技巧可以避免這個問題。考慮以下著色器
Shader "GLSL shader for RGB cube" {
SubShader {
Pass {
GLSLPROGRAM // here begin the vertex and the fragment shader
varying vec4 position;
// this line is part of the vertex and the fragment shader
#ifdef VERTEX
// here begins the part that is only in the vertex shader
void main()
{
position = gl_Vertex + vec4(0.5, 0.5, 0.5, 0.0);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif
// here ends the part that is only in the vertex shader
#ifdef FRAGMENT
// here begins the part that is only in the fragment shader
void main()
{
gl_FragColor = position;
}
#endif
// here ends the part that is only in the fragment shader
ENDGLSL // here end the vertex and the fragment shader
}
}
}
正如此著色器中的註釋所解釋的那樣,行 #ifdef VERTEX 實際上並沒有標記頂點著色器的開頭,而是標記了**僅**在頂點著色器中的一部分的開頭。類似地,#ifdef FRAGMENT 標記了僅在片段著色器中的一部分的開頭。實際上,兩個著色器都以行 GLSLPROGRAM 開頭。因此,GLSLPROGRAM 和第一個 #ifdef 行之間的任何程式碼都將由頂點著色器和片段著色器共享。(如果您熟悉 C 或 C++ 預處理器,您可能已經猜到了這一點。)
這對於變化量變數的定義非常完美,因為這意味著我們只需要鍵入一次定義,它將被放入頂點和片段著色器中;因此,保證了匹配的定義!即,我們只需要鍵入更少的程式碼,並且由於變化量變數定義之間的不匹配而不可能產生編譯錯誤。(當然,代價是我們必須鍵入所有這些 #ifdef 和 #end 行。)
RGB 立方體代表了可用的顏色集(即顯示器的色域)。因此,它也可以用來顯示顏色變換的效果。例如,顏色到灰度的變換將計算紅色、綠色和藍色顏色分量的平均值,即,然後將此值放入片段顏色的所有三個顏色分量中以獲得相同強度的灰色值。除了平均值之外,還可以使用相對亮度,即。當然,任何其他顏色變換(更改飽和度、對比度、色調等)也適用。
此著色器的另一種變體可以計算一個CMY(青色、洋紅色、黃色)立方體:對於位置 你可以從純白色中減去一個與 成比例的紅色,以產生青色。此外,你將減去一個與 分量成比例的綠色,以產生洋紅色,以及一個與 成比例的藍色,以產生黃色。
如果你真的想變得更花哨,你可以計算一個HSV(色調、飽和度、明度)圓柱體。對於 和 座標介於 -0.5 和 +0.5 之間,你可以得到一個角度 介於 0 和 360° 之間,在 GLSL 中用 180.0+degrees(atan(z, x)) 表示,以及一個距離 介於 0 和 1 之間,從 軸用 2.0 * sqrt(x * x + z * z) 表示。Unity 內建圓柱體的 座標介於 -1 和 1 之間,可以透過 轉換為介於 0 和 1 之間的數值 。從 HSV 座標計算 RGB 顏色在 維基百科上的 HSV 文章 中有描述。
關於變化變數的故事還沒有結束。如果你選擇立方體遊戲物件,你將在場景檢視中看到它只包含 12 個三角形和 8 個頂點。因此,頂點著色器可能只被呼叫了八次,並且只有八個不同的輸出被寫入變化變數。然而,立方體上有更多顏色。這是怎麼發生的?
答案隱含在名稱 變化 變數中。之所以這樣稱呼它們,是因為它們在三角形中變化。實際上,頂點著色器只針對每個三角形的每個頂點被呼叫。如果頂點著色器針對不同頂點將不同值寫入變化變數,則這些值會在整個三角形中插值。然後為三角形覆蓋的每個畫素呼叫片段著色器,並接收變化變數的插值值。這種插值的詳細資訊在 “光柵化”部分 中有描述。
如果你想確保片段著色器接收頂點著色器的一個確切的、非插值值,你必須確保頂點著色器對三角形的每個頂點都將相同的值寫入變化變數。
本教程到此結束。恭喜你!在其他內容中,你已經瞭解了
- 什麼是 RGB 立方體。
- 變化變數有什麼用以及如何定義它們。
- 如何確保變化變數在頂點著色器和片段著色器中具有相同的名稱和型別。
- 頂點著色器寫入變化變數的值如何在三角形中插值,然後被片段著色器接收。
如果你想了解更多
- 關於進出頂點和片段著色器的資料流,你應該閱讀 “OpenGL ES 2.0 管道”部分 中的描述。
- 關於向量和矩陣運算(例如表示式
gl_Vertex + vec4(0.5, 0.5, 0.5, 0.0);),你應該閱讀 “向量和矩陣運算”部分。 - 關於變化變數的插值,你應該閱讀 “光柵化”部分。
- 關於 Unity 官方文件中在 Unity 的 ShaderLab 中編寫頂點著色器和片段著色器的描述,你應該閱讀 Unity 的 ShaderLab 參考關於“GLSL 著色器程式”。