Cg 程式設計/Unity/最小影像效果

本教程介紹了在 Unity 中建立最小影像效果以對攝像機檢視進行影像後期處理的基本步驟。如果您不熟悉紋理處理,您應該先閱讀 “紋理球體”一節。
虛擬攝像機渲染影像後,通常需要對影像應用一些影像後期處理。這樣做有藝術上的原因(例如,實現特定的視覺風格),但也有技術上的原因(例如,在影像後期處理中實現動態環境光遮蔽或景深通常比將其作為渲染的一部分實現這些效果更有效)。
在 Unity 中,每個影像後期處理步驟稱為“影像效果”。每個影像效果都包含一個 C# 指令碼和一個著色器檔案,該檔案指定一個片段著色器,該著色器計算輸出影像的畫素。為了應用著色器,我們還需要建立一個材質,如下所述。
為影像效果建立 Cg 著色器並不複雜:在 **專案視窗** 中,單擊 **建立** 並選擇 **著色器 > 影像效果著色器**。專案視窗中應該出現一個名為“NewImageEffectShader”的新檔案。雙擊它開啟它(或右鍵單擊並選擇 **開啟**)。應該會出現一個帶有 Cg 中預設著色器的文字編輯器。
以下著色器比預設著色器更有用。您可以將其複製並貼上到著色器檔案中
Shader "tintImageEffectShader"
{
Properties
{
_MainTex ("Source", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
}
SubShader
{
Cull Off
ZWrite Off
ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vertexShader
#pragma fragment fragmentShader
#include "UnityCG.cginc"
struct vertexInput
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct vertexOutput
{
float2 texcoord : TEXCOORD0;
float4 position : SV_POSITION;
};
vertexOutput vertexShader(vertexInput i)
{
vertexOutput o;
o.position = mul(UNITY_MATRIX_MVP, i.vertex);
o.texcoord = i.texcoord;
return o;
}
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
float4 fragmentShader(vertexOutput i) : COLOR
{
float4 color = tex2D(_MainTex,
UnityStereoScreenSpaceUVAdjust(
i.texcoord, _MainTex_ST));
return color * _Color;
}
ENDCG
}
}
Fallback Off
}
該著色器具有兩個屬性:_Color 是一個顏色,該著色器使用它來對所有畫素的顏色進行著色。_MainTex 是一個渲染紋理,其中包含由攝像機渲染的攝像機檢視,或者它是先前影像效果的輸出渲染紋理。渲染紋理物件可以像 2D 紋理一樣用於紋理化,但攝像機也可以像渲染幀緩衝區一樣渲染到其中。渲染紋理非常適合影像效果,因為攝像機(或先前的影像效果)可以將影像渲染到其中,然後可以將影像饋送到下一個影像效果,就像它是紋理一樣。
該著色器停用面剔除(使用 Cull Off)和深度測試(使用 ZTest Always),以確保處理整個影像。它還停用寫入深度緩衝區(使用 ZWrite Off),以避免更改深度緩衝區。
頂點著色器 vertexShader() 將標準變換應用於頂點位置,並傳遞紋理座標。對於影像效果,頂點通常是攝像機視口的角點。紋理座標指定這些角點在紋理座標空間中的位置(從 0 到 1)。
然後,對輸出影像的每個畫素呼叫片段著色器 fragmentShader()。它可以使用插值的紋理座標訪問輸入影像 _MainTex 中的畫素。對於某些目標平臺(尤其是虛擬現實中的立體渲染),需要對紋理座標進行額外的變換;這些由 Unity 的 UnityStereoScreenSpaceUVAdjust() 函式處理。
此片段著色器只是讀取輸入影像中對應畫素的顏色,並將其乘以 _Color 來對其進行著色。更高階的影像效果的片段著色器會讀取輸入影像中不同位置的多個畫素的顏色,並將它們以複雜的方式組合起來計算每個輸出畫素的顏色。
為該著色器建立材質的一種方法是,在 **專案視窗** 中單擊 **建立 > 材質**。然後,您可以將著色器拖放到新材質上。當您選擇新材質時,**檢查器視窗** 中的預覽應該顯示著色器的效果;如果它沒有顯示,您可能需要單擊檢查器視窗底部的灰色欄使其出現。如果沒有預覽或球體是亮粉紅色,則應該在 Unity 視窗底部和 **控制檯** 視窗中顯示錯誤訊息(您可以從主選單使用 **視窗 > 常規 > 控制檯** 開啟它)。在這種情況下,您需要修復著色器檔案中的錯誤。
最後一步是將材質及其著色器應用於攝像機檢視的影像。這需要一個小的指令碼,該指令碼必須附加到攝像機;以下是在 C# 中的示例,應將其儲存為“tintImageEffectScript.cs”
using System;
using UnityEngine;
[RequireComponent(typeof(Camera))]
[ExecuteInEditMode]
public class tintImageEffectScript : MonoBehaviour {
public Material material;
void Start()
{
if (null == material || null == material.shader ||
!material.shader.isSupported)
{
enabled = false;
return;
}
}
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Graphics.Blit(source, destination, material);
}
}
此指令碼有一個公共變數 material,必須將其設定為建立的材質。Start() 函式只是檢查一切是否正常;如果沒有,它會停用指令碼,從而停用影像效果。實際工作是在 OnRenderImage() 函式中完成的。Unity 會為每個影像效果呼叫此函式,其中輸入影像(作為渲染紋理)位於 source 變數中,並期望輸出影像位於 destination 變數中(這也是一個渲染紋理)。將材質及其著色器應用於 source 中的輸入影像的標準方法是呼叫 Graphics.Blit(source, destination, material),它使用 material 中的著色器,以 source 作為輸入紋理 _MainTex,對 destination 渲染紋理中的所有畫素進行光柵化。
許多影像效果的指令碼要複雜得多,因為它們計算多箇中間影像(有時具有不同的尺寸),而不是僅僅使用一次呼叫 Graphics.Blit()。在這些情況下,單個影像效果使用多個著色器。這通常使用具有多個通道的單個著色器檔案來實現。然後,特定通道的著色器將與類似 Graphics.Blit(source, destination, material, pass) 的呼叫一起使用,其中 pass 是使用的通道的索引。
恭喜,您已經瞭解了 Unity 中影像效果的基礎知識。您已經看到的一些內容是
- 如何為影像效果建立著色器。
- 如何為這樣的著色器建立材質。
- 如何為影像效果建立 C# 指令碼。
如果您還想了解更多資訊
- 關於 Unity 中的標準影像效果,請參閱 Unity 的後期處理包 的描述。
- 關於將更強大的計算著色器用於影像效果,請參閱 “計算影像效果”一節。