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

本教程討論了頂點輸出引數和片段輸入引數。假定您已熟悉 “最小著色器”部分。
在本教程中,我們將編寫一個著色器來渲染一個類似於左側所示的 RGB 立方體。表面上每個點的顏色由其座標決定;例如,位於位置 的點具有顏色 。例如,點 對映到顏色 ,即純藍色。(這是左側圖形右下角的藍色角落。)
由於我們要建立一個 RGB 立方體,您首先需要建立一個立方體遊戲物件。如 “最小著色器”部分 中對球體的描述,您可以透過從主選單中選擇 GameObject > 3D Object > Cube 來建立一個立方體遊戲物件。繼續建立材料和著色器物件,並將著色器附加到材料,並將材料附加到立方體,如 “最小著色器”部分 中所述。
以下是著色器程式碼,您應該將其複製並貼上到您的著色器物件中
Shader "Cg shader for RGB cube" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert // vert function is the vertex shader
#pragma fragment frag // frag function is the fragment shader
// for multiple vertex output parameters an output structure
// is defined:
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : TEXCOORD0;
};
vertexOutput vert(float4 vertexPos : POSITION)
// vertex shader
{
vertexOutput output; // we don't need to type 'struct' here
output.pos = UnityObjectToClipPos(vertexPos);
output.col = vertexPos + float4(0.5, 0.5, 0.5, 0.0);
// Here the vertex shader writes output data
// to the output structure. 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.
return output;
}
float4 frag(vertexOutput input) : COLOR // fragment shader
{
return input.col;
// Here the fragment shader returns the "col" input
// parameter with semantic TEXCOORD0 as nameless
// output parameter with semantic COLOR.
}
ENDCG
}
}
}
如果您的立方體沒有正確著色,請檢查控制檯中的錯誤訊息(透過從主選單中選擇 Window > General > Console),確保您已儲存著色器程式碼,並檢查您是否已將著色器物件附加到材料物件,並將材料物件附加到遊戲物件。
我們著色器的主要任務是在片段著色器中將片段輸出顏色(即帶有語義 COLOR 的片段輸出引數)設定為頂點著色器中可用的頂點位置。實際上,情況並非完全如此:Unity 預設立方體中帶有語義 POSITION 的頂點輸入引數的座標介於 -0.5 和 +0.5 之間,而我們希望顏色分量介於 0.0 和 1.0 之間;因此,我們需要在 x、y 和 z 分量中新增 0.5,這是透過此表示式完成的:vertexPos + float4(0.5, 0.5, 0.5, 0.0)。
然而,主要問題是:我們如何從頂點著色器獲取任何值到片段著色器?事實證明,執行此操作的 唯一 方法是使用 具有相同語義 的頂點輸出引數和片段輸入引數對(在本例中為 TEXCOORD0)。事實上,只有語義用於確定哪些頂點輸出引數對應於哪些片段輸入引數。我們可以使用其他語義,例如 COLOR,而不是語義 TEXCOORD0,這裡實際上沒有區別,除了帶有語義 COLOR 的引數通常被鉗制到 0 到 1 之間的數值(在本例中是沒問題的)。然而,通常情況下,對各種引數使用語義 TEXCOORD0、TEXCOORD1、TEXCOORD2 等。
下一個問題是指定多個頂點輸出引數。由於 return 指令只能返回一個值,因此通常為所有必需的頂點輸出引數定義一個結構。這裡,此結構稱為 vertexOutput
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : TEXCOORD0;
};
透過將此結構用作片段著色器函式的引數,我們確保語義匹配。請注意,在 Cg 中(與 C 相反),我們不必在定義此型別變數時編寫 struct vertexOutput,而是可以使用名稱 vertexOutput(不帶 struct)來表示相同的型別。
使用輸出結構的另一種方法是使用頂點著色器函式帶有 out 限定符的引數,例如
Shader "Cg shader for RGB cube" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert // vert function is the vertex shader
#pragma fragment frag // frag function is the fragment shader
void vert(float4 vertexPos : POSITION,
out float4 pos : SV_POSITION,
out float4 col : TEXCOORD0)
{
pos = mul(UNITY_MATRIX_MVP, vertexPos);
col = vertexPos + float4(0.5, 0.5, 0.5, 0.0);
return;
}
float4 frag(float4 pos : SV_POSITION,
float4 col : TEXCOORD0) : COLOR
{
return col;
}
ENDCG
}
}
}
但是,在實踐中使用輸出結構更為常見,並且可以確保頂點輸出引數和片段輸入引數具有匹配的語義。
RGB 立方體代表了可用顏色的集合(即顯示器的色域)。因此,它也可以用來展示顏色變換的效果。例如,顏色到灰度變換可以計算紅色、綠色和藍色顏色分量的平均值,即 ,然後將此值放入片段顏色的所有三個顏色分量中,以獲得具有相同亮度的灰度值。除了平均值之外,還可以使用相對亮度,即 。當然,任何其他顏色變換(更改飽和度、對比度、色調等)也適用。
此著色器的另一種變體可以計算一個 CMY(青色、洋紅、黃色)立方體:對於位置 ,你可以從純白色中減去與 成比例的紅色量,以產生青色。此外,你會減去與 分量成比例的綠色量,以產生洋紅,以及與 成比例的藍色量,以產生黃色。
如果你真的想變得更復雜,你可以計算一個 HSV(色調、飽和度、亮度)圓柱體。對於 和 座標介於 -0.5 和 +0.5 之間,你可以得到一個角度 ,介於 0 到 360° 之間,在 Cg 中使用 180.0+degrees(atan2(z, x)),以及一個距離 ,介於 0 到 1 之間,從 軸使用 2.0 * sqrt(x * x + z * z)。Unity 內建圓柱體的 座標介於 -1 和 1 之間,可以透過 轉換為介於 0 到 1 之間的亮度值 。從 HSV 座標計算 RGB 顏色在 維基百科關於 HSV 的文章 中有描述。
頂點輸出引數的插值
[edit | edit source]關於頂點輸出引數和片段輸入引數的故事還沒有結束。如果你選擇立方體遊戲物件,你會在場景檢視中看到它只包含 12 個三角形和 8 個頂點。因此,頂點著色器可能只調用八次,並且只有八個不同的輸出被寫入頂點輸出引數。然而,立方體上有更多不同的顏色。這是怎麼回事呢?
事實上,頂點著色器只針對每個三角形的每個頂點呼叫。然而,不同頂點頂點輸出引數的不同值會在三角形上進行插值。然後,片段著色器針對三角形覆蓋的每個畫素呼叫,並接收頂點輸出引數的插值作為片段輸入引數。 這種插值的細節在 “光柵化”部分 中有描述。
如果你想確保片段著色器從頂點著色器接收一個確切的、非插值的值(所謂平面著色),你可以確保頂點著色器為三角形的每個頂點寫入頂點輸出引數的相同值,或者你可以使用 HLSL 儲存類修飾符 nointerpolation。在本例中,你可以使用這個結構體
struct vertexOutput {
float4 pos : SV_POSITION;
nointerpolation float4 col : TEXCOORD0;
};
總結
[edit | edit source]本教程到此結束。恭喜你!在其他內容中,你已經瞭解到
- 什麼是 RGB 立方體。
- 什麼是輸出結構,以及如何定義它。
- 輸出結構如何用於確保頂點輸出引數具有與片段輸入引數相同的語義。
- 寫入頂點輸出引數的值如何在三角形上進行插值,然後作為片段著色器的輸入引數接收。
進一步閱讀
[edit | edit source]如果你想了解更多
- 關於進出頂點著色器和片段著色器的資料流的資訊,你應該閱讀 “可程式設計圖形管道”部分 中的描述。
- 關於向量和矩陣運算(例如表示式
vertexPos + float4(0.5, 0.5, 0.5, 0.0)),你應該閱讀 “向量和矩陣運算”部分。 - 關於頂點輸出引數的插值,你應該閱讀 “光柵化”部分。
- 關於 Unity 關於在 Unity 的 ShaderLab 中編寫頂點著色器和片段著色器的官方文件,你應該閱讀 Unity 的 ShaderLab 參考關於“編寫頂點和片段著色器”。