文章內使用Unity 2019 LTS
一個場景有著各種形狀的物體,假如今天我們不只是要對單一物體進行特效處理,而是要對整個場景所看到「視角」進行處理,例如:模糊效果、傳送門...,勢必不可能在特定時間一個一個對照射到的物體進行處理,所以,有幾種方法可以把整個看到的視角,不論3D或2D,存在紋理貼圖(Texture)裡面,再進行加工。
利用相機把照射到的視角,儲存在Render Texture裡面,首先,先從Create -> Render Texture新增出一張Render Texture,之後,在要捕捉的相機上找到Target Texture
的屬性,把剛剛新增Render Texture附加上去,這樣這張貼圖就可以獲得相機的視角。
我們就可以像利用一般的紋理貼圖一樣,對它進行處理,會發現貼圖的結果是上下顛倒的,需要把紋理座標做一下修正。
Shader "Learning/RenderTexture" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(appdata_base v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
// 反轉紋理座標
o.uv.x = 1 - o.uv.x;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
ENDCG
}
}
FallBack Off
}
有一點很大的不同是,Render Texture是直接吃相機的視角的,如果視角內繪製的物體有變化,貼圖也會跟著變化,可以把它延伸做出影片撥放的效果。
GrabPass則是直接利用Shader的語法來直接獲取螢幕影像,用法為:
Shader "Learning/GrabPassTest"
{
Properties{
}
SubShader{
Tags{"Queue"="Transparent" "RenderType"="Opaque"}
// GrabPass!!
GrabPass{"_GrabPassTexture"}
pass{
CGPROGRAM
#pragma vertex vertex
#pragma fragment frag
#include "UnityCG.cginc"
// 記得要像使用變數一樣,再一次宣告他們
sampler2D _GrabPassTexture;
float4 _GrabPassTexture_TexelSize;
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 frag(v2f i):SV_TARGET{
return tex2D(_GrabPassTexture,i.uv);
}
ENDCG
}
}
}
直接在大括號中填上的字串就是產生出來的「螢幕紋理貼圖」的變數名稱,在變數後面加上_TexelSize
可以獲得GrabPass的紋理大小
OnRenderImage
則是一個在MonoBehaviour
底下的一個函數,常用在「螢幕後製處理(screen post-processing)」上。
以下就拿一個調節螢幕亮度的功能當作範例:
Shader "Learning/Brightness" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Brightness ("Brightness", Float) = 1
}
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
half _Brightness;
struct v2f {
float4 pos : SV_POSITION;
half2 uv: TEXCOORD0;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed4 texColor = tex2D(_MainTex, i.uv);
return fixed4(texColor.rgb * _Brightness, texColor.a);
}
ENDCG
}
}
}
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class PostBrightness : MonoBehaviour
{
public Shader BrightnessShader;
private Material mat;
[Range(0.0f, 3.0f)]
public float Brightness = 1.0f;
private void Start() {
if (BrightnessShader == null) {
return;
}
if (!BrightnessShader.isSupported) {
return;
}
else {
mat = new Material(BrightnessShader);
mat.hideFlags = HideFlags.DontSave;
}
}
void OnRenderImage(RenderTexture src, RenderTexture dest) {
if (mat != null) {
mat.SetFloat("_Brightness", Brightness);
Graphics.Blit(src, dest, mat);
} else {
Graphics.Blit(src, dest);
}
}
}