跳轉到內容

Cg 程式設計/Unity/世界空間中的著色

來自 Wikibooks,開放世界中的開放書籍
一些變色龍能夠根據周圍的世界改變它們的顏色。

本教程介紹了制服引數。它假設您熟悉“最小著色器”部分“RGB 立方體”部分“著色器除錯”部分

在本教程中,我們將研究一個著色器,它根據片段在世界中的位置改變片段顏色。這個概念並不複雜;然而,它有極其重要的應用,例如用燈光和環境貼圖進行著色。我們也將看看現實世界中的著色器;也就是說,為了讓非程式設計師使用您的著色器需要做些什麼?

從物件空間到世界空間的變換

[編輯 | 編輯原始碼]

“著色器除錯”部分所述,語義為POSITION的頂點輸入引數指定了物件座標,即網格區域性物件(或模型)空間中的座標。物件空間(或物件座標系)是特定於每個遊戲物件的;然而,所有遊戲物件都轉換到一個共同的座標系——世界空間。

如果一個遊戲物件直接放置到世界空間中,則物件到世界的轉換由遊戲物件的 Transform 元件指定。要檢視它,在場景檢視層次結構視窗中選擇該物件,然後在檢查器視窗中找到 Transform 元件。Transform 元件中有“位置”、“旋轉”和“縮放”引數,它們指定了頂點如何從物件座標轉換為世界座標。(如果一個遊戲物件是物件組的一部分,這在層次結構視窗中透過縮排表示,則 Transform 元件僅指定從遊戲物件的座標到父物件的座標的轉換。在這種情況下,實際的物件到世界轉換是由物件的轉換與其父級、祖父母等的轉換的組合給出的。)頂點透過平移、旋轉和縮放的變換,以及變換的組合及其作為 4×4 矩陣的表示,在“頂點變換”部分中討論。

回到我們的示例:從物件空間到世界空間的轉換被放入一個 4×4 矩陣中,該矩陣也被稱為“模型矩陣”(因為這種轉換也被稱為“模型變換”。這個矩陣在制服引數unity_ObjectToWorld中可用,它是由 Unity 以這種方式自動定義的

         uniform float4x4 unity_ObjectToWorld;

由於它是自動定義的,我們不需要定義它(實際上我們不能定義它)。相反,我們可以在以下著色器中使用未定義的制服引數unity_ObjectToWorld

Shader "Cg shading in world space" {
   SubShader {
      Pass {
         CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 
 
         // uniform float4x4 unity_ObjectToWorld; 
            // automatic definition of a Unity-specific uniform parameter

         struct vertexInput {
            float4 vertex : POSITION;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 position_in_world_space : TEXCOORD0;
         };

         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output; 
 
            output.pos =  UnityObjectToClipPos(input.vertex);
            output.position_in_world_space = 
               mul(unity_ObjectToWorld, input.vertex);
               // transformation of input.vertex from object 
               // coordinates to world coordinates;
            return output;
         }
 
         float4 frag(vertexOutput input) : COLOR 
         {
             float dist = distance(input.position_in_world_space, 
               float4(0.0, 0.0, 0.0, 1.0));
               // computes the distance between the fragment position 
               // and the origin (the 4th coordinate should always be 
               // 1 for points).
            
            if (dist < 5.0)
            {
               return float4(0.0, 1.0, 0.0, 1.0); 
                  // color near origin
            }
            else
            {
               return float4(0.1, 0.1, 0.1, 1.0); 
                  // color far from origin
            }
         }
 
         ENDCG  
      }
   }
}

通常,應用程式必須設定制服引數的值;然而,Unity 負責始終設定諸如unity_ObjectToWorld之類的預定義制服引數的正確值;因此,我們不必擔心它。

此著色器將頂點位置變換到世界空間,並將它傳遞給輸出結構中的片段著色器。對於片段著色器,輸出結構中的引數包含片段在世界座標中的插值位置。根據此位置到世界座標系原點的距離,將設定兩種顏色之一。因此,如果您在編輯器中移動具有此著色器的物件,它將在世界座標系原點附近變為綠色。遠離原點,它將變為深灰色。

更多 Unity 特定的制服

[編輯 | 編輯原始碼]

有一些內建制服引數,它們是由 Unity 自動定義的,類似於float4x4矩陣unity_ObjectToWorld。以下列出了幾個在幾個教程中使用的制服(包括unity_ObjectToWorld

   uniform float4 _Time, _SinTime, _CosTime; // time values
   uniform float4 _ProjectionParams;
      // x = 1 or -1 (-1 if projection is flipped)
      // y = near plane; z = far plane; w = 1/far plane
   uniform float4 _ScreenParams; 
      // x = width; y = height; z = 1 + 1/width; w = 1 + 1/height
   uniform float3 _WorldSpaceCameraPos;
   uniform float4x4 unity_ObjectToWorld; // model matrix
   uniform float4x4 unity_WorldToObject; // inverse model matrix 
   uniform float4 _WorldSpaceLightPos0; 
      // position or direction of light source for forward rendering

   uniform float4x4 UNITY_MATRIX_MVP; // model view projection matrix 
      // in some cases, UnityObjectToClipPos() just uses this matrix 
   uniform float4x4 UNITY_MATRIX_MV; // model view matrix
   uniform float4x4 UNITY_MATRIX_V; // view matrix
   uniform float4x4 UNITY_MATRIX_P; // projection matrix
   uniform float4x4 UNITY_MATRIX_VP; // view projection matrix
   uniform float4x4 UNITY_MATRIX_T_MV; 
      // transpose of model view matrix
   uniform float4x4 UNITY_MATRIX_IT_MV; 
      // transpose of the inverse model view matrix
   uniform float4 UNITY_LIGHTMODEL_AMBIENT; // ambient color

有關 Unity 內建制服的官方列表,請參閱 Unity 手冊中的“內建著色器變數”部分。

其中一些制服實際上是在檔案UnityShaderVariables.cginc中定義的,該檔案從 Unity 的 4.0 版本開始被自動包含。

還有一些內建制服沒有被自動定義,例如_LightColor0,它是在UnityLightingCommon.cginc中定義的。因此,我們必須要麼顯式地定義它(如果需要)

      uniform float4 _LightColor0;

要麼包含具有其定義的檔案

      #include "UnityLightingCommon.cginc"

要麼包含包含UnityLightingCommon.cginc的檔案,例如

      #include "Lighting.cginc"

Unity 並不總是更新所有這些制服。特別是,_WorldSpaceLightPos0_LightColor0僅針對標記適當的著色器通道正確設定,例如,使用Tags {"LightMode" = "ForwardBase"}作為Pass {...} 塊中的第一行;另請參閱“漫反射”部分

使用者指定的制服:著色器屬性

[編輯 | 編輯原始碼]

還有一種重要的制服引數型別:可以由使用者設定的制服。實際上,它們在 Unity 中被稱為著色器屬性。您可以將它們視為著色器的使用者指定的制服引數。沒有引數的著色器通常僅由其程式設計師使用,因為即使是最小的必要更改也需要一些程式設計。另一方面,使用具有描述性名稱的引數的著色器可以由其他人使用,即使是非程式設計師,例如 CG 藝術家。想象一下,您在一個遊戲開發團隊中,一位 CG 藝術家要求您為 100 次設計迭代中的每一次調整您的著色器。很明顯,一些引數,即使 CG 藝術家也可以使用它們,可能會為您節省大量時間。此外,想象一下您想要出售您的著色器:引數通常會顯著提高您的著色器的價值。

由於Unity 對著色器屬性的描述非常好,這裡只舉一個如何在我們的示例中使用著色器屬性的例子。我們首先宣告屬性,然後定義相同名稱和對應型別的制服。

Shader "Cg shading in world space" {
   Properties {
      _Point ("a point in world space", Vector) = (0., 0., 0., 1.0)
      _DistanceNear ("threshold distance", Float) = 5.0
      _ColorNear ("color near to point", Color) = (0.0, 1.0, 0.0, 1.0)
      _ColorFar ("color far from point", Color) = (0.3, 0.3, 0.3, 1.0)
   }
   
   SubShader {
      Pass {
         CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 
 
         #include "UnityCG.cginc" 
            // defines unity_ObjectToWorld and unity_WorldToObject

         // uniforms corresponding to properties
         uniform float4 _Point;
         uniform float _DistanceNear;
         uniform float4 _ColorNear;
         uniform float4 _ColorFar;

         struct vertexInput {
            float4 vertex : POSITION;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 position_in_world_space : TEXCOORD0;
         };

         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output; 
 
            output.pos =  UnityObjectToClipPos(input.vertex);
            output.position_in_world_space = 
               mul(unity_ObjectToWorld, input.vertex);
            return output;
         }
 
         float4 frag(vertexOutput input) : COLOR 
         {
             float dist = distance(input.position_in_world_space, 
               _Point);
               // computes the distance between the fragment position 
               // and the position _Point.
            
            if (dist < _DistanceNear)
            {
               return _ColorNear; 
            }
            else
            {
               return _ColorFar; 
            }
         }
 
         ENDCG  
      }
   }
}

使用這些引數,非程式設計師可以修改我們著色器的效果。這很好;但是,著色器的屬性(實際上,制服本身)也可以由指令碼設定!例如,附加到使用著色器的遊戲物件的 C# 指令碼可以使用以下幾行程式碼設定屬性

GetComponent<Renderer>().sharedMaterial.SetVector("_Point", new Vector4(1.0f, 0.0f, 0.0f, 1.0f));
GetComponent<Renderer>().sharedMaterial.SetFloat("_DistanceNear", 10.0f);
GetComponent<Renderer>().sharedMaterial.SetColor("_ColorNear", new Color(1.0f, 0.0f, 0.0f));
GetComponent<Renderer>().sharedMaterial.SetColor("_ColorFar", new Color(1.0f, 1.0f, 1.0f));

GetComponent<Renderer>() 返回Renderer元件。(您也可以編寫(效率較低)GetComponent(typeof(Renderer)) as Renderer;GetComponent("Renderer") as Renderer。)如果您想更改使用此材質的所有物件的引數,請使用sharedMaterial;如果您只想更改一個物件的引數,請使用material。(但請注意,material可能會建立一個新的材質例項,該例項不會在遊戲物件被銷燬時自動銷燬!)透過指令碼,您可以,例如,將_Point設定為另一個物件的位置(即其 Transform 元件的position)。透過這種方式,您可以透過在編輯器中移動另一個物件來指定一個點。為了編寫這樣的指令碼,在專案視窗中選擇+/建立 > C# 指令碼,並將其命名為ShadingInWorldSpace,然後複製並貼上此程式碼

using UnityEngine;

[ExecuteInEditMode, RequireComponent(typeof(Renderer))]
public class ShadingInWorldSpace : MonoBehaviour {
	public GameObject other;
	Renderer rend;
	void Start() {
		rend = GetComponent<Renderer>();
	}

	// Update is called once per frame
	void Update () {
		if(other != null) {
			rend.sharedMaterial.SetVector("_Point", other.transform.position);
		}
	}
}

然後,您應該將指令碼附加到具有著色器的物件(例如,透過將指令碼拖放到物件上),並將另一個物件拖放到指令碼的檢查器視窗中的other變數上。現在,您可以透過更改另一個物件的位置來更改材質的_Point變數的值。

恭喜你,你成功了!我們討論了

  • 如何將頂點轉換為世界座標。
  • Unity 支援的最重要的 Unity 特定的制服。
  • 如何透過新增著色器屬性使著色器更加有用和有價值。

進一步閱讀

[編輯 | 編輯原始碼]

如果您想了解更多

< Cg 程式設計/Unity

除非另有說明,本頁所有示例原始碼均授予公有領域。
華夏公益教科書