渲染紋理
現(xiàn)代的GPU允許把整個(gè)三維場(chǎng)景渲染到一個(gè)中間緩沖中,即渲染目標(biāo)紋理(Render Target Texture RTT),而不是傳統(tǒng)的幀緩沖或是后備緩沖(backbuffer)。與之相關(guān)的是多重渲染目標(biāo)(Multiple Render Target, MRT),這種技術(shù)指的是GPU允許我們把場(chǎng)景同時(shí)渲染到多個(gè)渲染目標(biāo)紋理中,而不再需要為每個(gè)渲染目標(biāo)紋理單獨(dú)渲染完整的場(chǎng)景。延遲渲染就是使用多重渲染目標(biāo)的一個(gè)應(yīng)用。
Unity為渲染目標(biāo)紋理定義了一種專(zhuān)門(mén)的紋理類(lèi)型——渲染紋理(Render Texture)。Unity渲染紋理有兩種使用方法:
1、在Project下創(chuàng)建一個(gè)渲染紋理,然后將其賦給某個(gè)攝像機(jī),創(chuàng)建的渲染紋理可以調(diào)整分標(biāo)率,濾波模式。這樣攝像機(jī)渲染的結(jié)果會(huì)實(shí)時(shí)更新到這張紋理下。(這種方式也可以通過(guò)寫(xiě)代碼的方式)
2、在屏幕后處理時(shí)使用GrabPass,命令,或是OnRenderImage函數(shù)獲取當(dāng)前屏幕圖像。Unity會(huì)把這個(gè)屏幕圖像放到一張和屏幕分辨率等同的渲染紋理中,然后就可以在Pass中把它當(dāng)成普通的紋理使用就可以,用來(lái)實(shí)現(xiàn)各種屏幕特效。
鏡子效果
其實(shí)鏡子效果的實(shí)現(xiàn)就是在“鏡子”的后面放置一個(gè)攝像機(jī),然后把這個(gè)攝像機(jī)看到的東西畫(huà)在“鏡子”上(就是講這個(gè)攝像機(jī)的RenderTexure當(dāng)成正常的紋理畫(huà)在某個(gè)面上),需要注意的是“鏡子”與主相機(jī)看到的實(shí)際是鏡面的,也就是反過(guò)來(lái)的,所以UV坐標(biāo)需要在X方向上翻轉(zhuǎn)一下。

Shader "Unlit/Mirror"
{
Properties
{
_MainTex ("Render Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v
{
float4 vertex : POSITION;
float3 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
sampler2D _MainTex;
v2f vert (a2v 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
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
}
}
玻璃效果
當(dāng)我們?cè)赟hader中定義一個(gè)GrabPass之后,Unity會(huì)把當(dāng)前屏幕的圖像繪制在一張紋理中,我們可以再后續(xù)的Pass中訪問(wèn)這張紋理。通常會(huì)使用GrabPass來(lái)實(shí)現(xiàn)諸如玻璃等透明材質(zhì)的模擬,使用GrabPass可以對(duì)該物體后面的圖像進(jìn)行更復(fù)雜的操作,如使用法線模擬折射效果。
在使用GrabPass時(shí)需要注意物體的渲染隊(duì)列設(shè)置,GrabPass通常用于渲染透明物體,盡管代碼里面并不包含混合指令,但仍需要把渲染隊(duì)列設(shè)置成透明隊(duì)列:
Tags{"RenderType"="Opaque" "Queue"="Transparent"}
這樣能保證在畫(huà)這個(gè)物體時(shí)其他不透明的物體已經(jīng)繪制完了。

Shader "Unlit/GlassRefraction"
{
Properties{
_MainTex("Main Tex",2D)="white"{}
_BumpTex("Bump Tex",2D)="bump"{}
_Cubemap("Envirenment Tex",Cube)="_SkyBox"{}
_Distortion("Distortion",Range(0,100))=50
_RefractionAmount("Refraction Amount",Range(0,1))=0.5
}
SubShader{
Tags{"RenderType"="Opaque" "Queue"="Transparent"}
GrabPass{"_RefractionTex"}
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpTex;
float4 _BumpTex_ST;
samplerCUBE _Cubemap;
float _Distortion;
fixed _RefractionAmount;
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 scrPos : TEXCOORD0;
float4 uv : TEXCOORD1;
float4 TtoW0 : TEXCOORD2;
float4 TtoW1 : TEXCOORD3;
float4 TtoW2 : TEXCOORD4;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.scrPos = ComputeGrabScreenPos(o.pos);
o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpTex);
float3 worldPos = mul(unity_ObjectToWorld,v.vertex);
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float3 worldTangent = UnityObjectToWorldDir(v.tangent);
float3 worldBinormal = cross(normalize(worldTangent),normalize(worldNormal))*v.tangent.w;
o.TtoW0 = float4(worldTangent.x,worldBinormal.x,worldNormal.x,worldPos.x);
o.TtoW1 = float4(worldTangent.y,worldBinormal.y,worldNormal.y,worldPos.y);
o.TtoW2 = float4(worldTangent.z,worldBinormal.z,worldNormal.z,worldPos.z);
return o;
}
fixed4 frag(v2f i):SV_TARGET
{
float3 worldPos = float3(i.TtoW0.z,i.TtoW1.z,i.TtoW2.z);
float3 worldViewDir = UnityWorldSpaceViewDir(worldPos);
//切線空間
fixed3 bump = UnpackNormal(tex2D(_BumpTex,i.uv.zw));
//在切線空間對(duì)法線進(jìn)行偏移,得出偏移的像素?cái)?shù)量,_RefractionTex_TexelSize代表素紋大小
//如256X512的紋理,素紋大小就是(1/256,1/512)
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
//然后對(duì)屏幕空間XY值進(jìn)行偏移,很疑惑為什么還要乘一次Z值,感覺(jué)不乘也可以
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
fixed3 refractColor = tex2D(_RefractionTex,i.scrPos.xy/i.scrPos.w).rgb;
fixed3 diffuseColor = tex2D(_MainTex,i.uv.xy).rgb;
fixed3 worldBump = normalize(half3(dot(bump,i.TtoW0.xyz),dot(bump,i.TtoW1.xyz),dot(bump,i.TtoW2.xyz)));
fixed3 reflectDir = reflect(-worldViewDir,worldBump);
fixed3 reflectColor = texCUBE(_Cubemap,reflectDir).rgb * diffuseColor;
fixed3 finalColor = reflectColor * (1-_RefractionAmount) + refractColor * _RefractionAmount;
return fixed4(finalColor,1.0);
}
ENDCG
}
}
}