1.定義:
屏幕後處理通常指在渲染完整個場景得到屏幕圖像後,再對這個圖像進行各種操作,實現各種屏幕特效。使用屏幕後處理技術,可以實現景深(Depth of Field),運動模糊(Motion Blur)等。
2.實現步驟
2.1 先抓取屏幕圖像,調用方法如下:
monoBehaviour.OnRenderImage(RenderTexture src,RenderTexture dest)
Unity會把當前渲染完場景得到的屏幕紋理 或 上一步處理後得到的渲染紋理存儲到 src 中。然後再經過一系列操作後再把 dest渲染到屏幕上。
2.2OnRenderImage函數中利用Graphics.Blit函數對 src 紋理進行操作。
public static void Blit(Texture source, RenderTexture dest);
public static void Blit(Texture source, RenderTexture dest, Material mat, int pass = -1);
public static void Blit(Texture source, Material mat, int pass = -1);
public static void Blit(Texture source, RenderTexture dest, Vector2 scale, Vector2 offset);
- 參數source是屏幕紋理 或 上一步處理後得到的渲染紋理
- 參數dest是目標渲染紋理,如果dest = null,會直接渲染到屏幕上。
- 參數mat是使用的材質,source會被傳遞到Shader中的_MainTex的紋理屬性。
- 參數pass默認= - 1,標識一次調用Shader中的所有Pass,否則只會調用給定索引的Pass
默認情況下 OnRenderImage函數在所有的不透明和透明的的Pass執行完畢後被調用,以便對場景中所有物體都產生影響,但有時希望在不透明的Pass執行完畢後被調用,這樣不對透明物體產生影響。此時可以在OnRenderImage添加ImageEffectOpaque函數來實現目的。
https://docs.unity3d.com/2018.3/Documentation/ScriptReference/Graphics.Blit.html
實踐:
前期準備,在傳入Shader參數時候,初始化一個Material,用於屏幕後處理。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class PostEffectsBase_Chan : MonoBehaviour {
protected void CheckResources()
{
bool isSurrorted = SystemInfo.supportsImageEffects;
if (!isSurrorted) {
NotSupported ();
}
}
protected void NotSupported()
{
this.enabled = false;
}
protected void Start()
{
CheckResources ();
}
//指定一個Shader,並創建一個材質使用這個Shader
protected Material CheckShaderAndCreateMaterial(Shader _shader,Material _material)
{
if (_shader == null) {
return null;
}
if (_shader.isSupported && _material && _material.shader == _shader) {
return _material;
}
if (!_shader.isSupported) {
return null;
} else {
_material = new Material (_shader);
_material.hideFlags = HideFlags.DontSave;
if (_material) {
return _material;
}
return null;
}
}
}
1.調整屏幕圖像的亮度,飽和度和對比度
1.1 寫一個可調節圖像亮度,飽和度,對比度的Shader。
Shader "Chan/Brightness Saturation And Contrast" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Brightness ("Brightness", Float) = 1
_Saturation("Saturation", Float) = 1
_Contrast("Contrast", Float) = 1
}
SubShader {
Pass {
ZTest Always Cull Off
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
half _Brightness;
half _Saturation;
half _Contrast;
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 renderTex = tex2D(_MainTex, i.uv);
// Apply brightness
fixed3 finalColor = renderTex.rgb * _Brightness;
// Apply saturation
fixed luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;
fixed3 luminanceColor = fixed3(luminance, luminance, luminance);
finalColor = lerp(luminanceColor, finalColor, _Saturation);
// Apply contrast
fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
finalColor = lerp(avgColor, finalColor, _Contrast);
return fixed4(finalColor, renderTex.a);
}
ENDCG
}
}
Fallback Off
}
1.2 腳本BrightnessSaturationAndContrast_Chan 繼承自 PostEffectsBase_Chan,並掛到Camera上。在腳本中調節Shader中的亮度,飽和度,對比度 三個變量的值。
using UnityEngine;
using System.Collections;
public class BrightnessSaturationAndContrast_Chan : PostEffectsBase_Chan {
public Shader briSatConShader;
private Material briSatConMaterial;
public Material material {
get {
briSatConMaterial = CheckShaderAndCreateMaterial(briSatConShader, briSatConMaterial);
return briSatConMaterial;
}
}
[Range(0.0f, 3.0f)]
public float brightness = 1.0f;
[Range(0.0f, 3.0f)]
public float saturation = 1.0f;
[Range(0.0f, 3.0f)]
public float contrast = 1.0f;
void OnRenderImage(RenderTexture src, RenderTexture dest) {
if (material != null) {
material.SetFloat("_Brightness", brightness);
material.SetFloat("_Saturation", saturation);
material.SetFloat("_Contrast", contrast);
Graphics.Blit(src, dest, material);
} else {
Graphics.Blit(src, dest);
}
}
}
參數source是屏幕紋理 或 上一步處理後得到的渲染紋理。會傳遞給Shader中名字叫_MainTex的紋理,所以Shader中一定要定義名字爲_MainTex的紋理。
由於屏幕後處理,其實是繪製了一個與屏幕同寬高的四邊形面片,因此需要關閉四邊形面片的深度寫入,爲了防止擋住場景中的物體。
2.邊緣檢測
邊緣檢測的原理是利用卷積覈對圖像上所有像素進行卷積操作,操作時候把卷積核中心放到該像素上。
將原始像素點"1",使用中間卷積核,進行卷積操作得到卷積結果"8"。
常用卷積算子的優缺點:
Shader "Chan/Chapter 12/Edge Detection" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_EdgeOnly ("Edge Only", Float) = 1.0
//邊緣顏色
_EdgeColor ("Edge Color", Color) = (0, 0, 0, 1)
//背景顏色
_BackgroundColor ("Background Color", Color) = (1, 1, 1, 1)
}
SubShader {
Pass {
ZTest Always Cull Off
ZWrite Off
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment fragSobel
sampler2D _MainTex;
uniform half4 _MainTex_TexelSize;
fixed _EdgeOnly;
fixed4 _EdgeColor;
fixed4 _BackgroundColor;
struct v2f {
float4 pos : SV_POSITION;
half2 uv[9] : TEXCOORD0;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
//由於卷積操作需要採樣相鄰區域的紋理,因此先取得相鄰區域的uv座標
//採樣紋理座標放到頂點着色器中,提高性能
o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1);
o.uv[1] = uv + _MainTex_TexelSize.xy * half2(0, -1);
o.uv[2] = uv + _MainTex_TexelSize.xy * half2(1, -1);
o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 0);
o.uv[4] = uv + _MainTex_TexelSize.xy * half2(0, 0);
o.uv[5] = uv + _MainTex_TexelSize.xy * half2(1, 0);
o.uv[6] = uv + _MainTex_TexelSize.xy * half2(-1, 1);
o.uv[7] = uv + _MainTex_TexelSize.xy * half2(0, 1);
o.uv[8] = uv + _MainTex_TexelSize.xy * half2(1, 1);
return o;
}
//將顏色轉換爲亮度(灰度)
fixed luminance(fixed4 color) {
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
//卷子算子Sobel 進行邊緣檢測 得到當前像素的梯度值
half Sobel(v2f i) {
//水平方向的卷積核Gx
const half Gx[9] = {-1, 0, 1,
-2, 0, 2,
-1, 0, 1};
//垂直方向的卷積核Gy
const half Gy[9] = {-1, -2, -1,
0, 0, 0,
1, 2, 1};
half texColor;
half edgeX = 0;
half edgeY = 0;
for (int it = 0; it < 9; it++) {
texColor = luminance(tex2D(_MainTex, i.uv[it]));
edgeX += texColor * Gx[it];
edgeY += texColor * Gy[it];
}
//1. 本來是開跟操作,處於性能考慮 使用絕對值代替
//2. 1 - (橫豎方向梯度) = 值越小,越有可能是邊緣點
half edge = 1 - abs(edgeX) - abs(edgeY);
return edge;
}
fixed4 fragSobel(v2f i) : SV_Target {
half edge = Sobel(i);
//使用edge 梯度值計算原圖下的顏色值
fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[4]), edge);
//使用edge 梯度值計算純色背景下的顏色值
fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);
//使用_EdgeOnly 融合邊緣顏色和原圖顏色值
return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
}
ENDCG
}
}
FallBack Off
}
https://blog.csdn.net/zouxy09/article/details/49080029
https://blog.csdn.net/tigerda/article/details/61192943
3.高斯模糊
利用高斯卷積核進行卷積計算,高斯核是正方形大小的濾波核。每個元素計算都基於二維高斯方程。
高斯函數二維正態分佈:
模擬了距離越近的元素,對中心點的影響越大。
出於性能考慮,將二維高斯核改爲兩個一維高速核。同時去掉一維高斯核中重複的權重。
Shader "Chan/Chapter 12/Gaussian Blur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurSize ("Blur Size", Float) = 1.0
}
SubShader {
//CGINCLUDE ENDCG 類似C++頭文件
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
float _BlurSize;
struct v2f {
float4 pos : SV_POSITION;
half2 uv[5]: TEXCOORD0;
};
// 由於使用的是5x5的高斯核進行模糊計算,因此取中心點相鄰的4個垂直uv區域,用於採樣紋理
v2f vertBlurVertical(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;//當前操作的紋理的uv座標
o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
return o;
}
// 由於使用的是5x5的高斯核進行模糊計算,因此取中心點相鄰的4個水平uv區域,用於採樣紋理
v2f vertBlurHorizontal(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
return o;
}
fixed4 fragBlur(v2f i) : SV_Target {
float weight[3] = {0.4026, 0.2442, 0.0545};
fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
for (int it = 1; it < 3; it++) {
sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
}
return fixed4(sum, 1.0);
}
ENDCG
ZTest Always Cull Off
ZWrite Off
Pass {
//使用Name語義,其他Shader 通過Name後的名字就可直接調用這個Pass
NAME "GAUSSIAN_BLUR_VERTICAL"
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
Pass {
NAME "GAUSSIAN_BLUR_HORIZONTAL"
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDCG
}
}
FallBack Off
}
註解:
- 本例中使用 5x5 的二維高斯核進行模糊計算,但是被分爲垂直方向和水平方向的兩個一維數組,因此Shader中 vertBlurVertical vertBlurHorizontal 分別取下圖中當前計算的UV區域(塗黑區域)相鄰的上下和左右2個UV區域。
- 由於“距離”計算中心點(塗黑區域)大小一樣的區域,具有相同的權重。橫豎兩個一維5個元素的數組,定義3個權重即可。
- Shader 中fragBlur根據UV採樣到計算點(塗黑區域)相鄰的紋理,然後乘以區域相對應的權重。
配合使用的C#腳本:
using UnityEngine;
using System.Collections;
public class GaussianBlur : PostEffectsBase {
public Shader gaussianBlurShader;
private Material gaussianBlurMaterial = null;
public Material material {
get {
gaussianBlurMaterial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMaterial);
return gaussianBlurMaterial;
}
}
//高斯模糊迭代次數
[Range(0, 4)]
public int iterations = 3;
//模糊範圍
[Range(0.2f, 3.0f)]
public float blurSpread = 0.6f;
//縮放係數
[Range(1, 8)]
public int downSample = 2;
//
void OnRenderImage(RenderTexture src, RenderTexture dest) {
if (material != null) {
int rtW = src.width;
int rtH = src.height;
// 使用RenderTexture.GetTemporary創建一個與屏幕大小相當的緩存
RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
// 將Shader中第一個Pass對豎直方向進行高斯核卷積操作計算得到的圖像放到緩存中
Graphics.Blit(src, buffer, material, 0);
// 使用Shader中的第二個Pass對buffer進行水平方向的高斯核卷積操作
Graphics.Blit(buffer, dest, material, 1);
// 釋放創建的緩存
RenderTexture.ReleaseTemporary(buffer);
} else {
Graphics.Blit(src, dest);
}
}
//圖片降採樣處理
// void OnRenderImage (RenderTexture src, RenderTexture dest) {
// if (material != null) {
// int rtW = src.width/downSample;
// int rtH = src.height/downSample;
// // 截取的屏幕紋理縮小,並將紋理濾波模式設置爲雙線性。因此處理的像素數量變低
// RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
// buffer.filterMode = FilterMode.Bilinear;
// // 將Shader中第一個Pass對豎直方向進行高斯核卷積操作計算得到的圖像放到緩存中
// Graphics.Blit(src, buffer, material, 0);
// // 使用Shader中的第二個Pass對buffer進行水平方向的高斯核卷積操作
// Graphics.Blit(buffer, dest, material, 1);
// // 釋放創建的緩存
// RenderTexture.ReleaseTemporary(buffer);
// } else {
// Graphics.Blit(src, dest);
// }
// }
// 圖片降採樣處理 + 增加高斯模糊的迭代次數
// void OnRenderImage (RenderTexture src, RenderTexture dest) {
// if (material != null) {
// int rtW = src.width/downSample;
// int rtH = src.height/downSample;
// RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
// buffer0.filterMode = FilterMode.Bilinear;
// Graphics.Blit(src, buffer0);
// for (int i = 0; i < iterations; i++) {
// material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
// //將Shader中第一個Pass對豎直方向進行高斯核卷積操作計算得到的圖像放到緩存中
// RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
// Graphics.Blit(buffer0, buffer1, material, 0);
// RenderTexture.ReleaseTemporary(buffer0);
// buffer0 = buffer1;
// //使用Shader中的第二個Pass對buffer進行水平方向的高斯核卷積操作
// buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
// Graphics.Blit(buffer0, buffer1, material, 1);
// RenderTexture.ReleaseTemporary(buffer0);
// buffer0 = buffer1;
// }
// Graphics.Blit(buffer0, dest);
// RenderTexture.ReleaseTemporary(buffer0);
// } else {
// Graphics.Blit(src, dest);
// }
// }
}
4.Bloom效果
原理:
- 根據一個閾值提取圖像紅較亮的區域,把它們存儲在一張渲染紋理中。
- 然後再利用高斯模糊對渲染紋理進行模糊處理,模擬管線擴散效果。
- 最後和原圖像進行混合,得到最終效果。
Shader "Chan/Chapter 12/Bloom" {
Properties
{
_MainTex("Base Tex",2D) = "white"{}
//存儲高斯模糊後的較亮區域
_Bloom("Bloom",2D) = "black"{}
//提取較亮區域使用的閾值
_LuminanceThreshold("Luminance Threshold",Float) = 0.5
//控制不同迭代之間高斯模糊的模糊區域範圍
_BlurSize("Blur Size",Float) = 1.0
}
SubShader
{
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _Bloom;
float _LuminanceThreshold;
float _BlurSize;
struct v2f
{
float4 pos:SV_POSITION;
half2 uv:TEXCOORD0;
};
v2f vertExtractBright(appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
//將顏色轉化爲亮度(灰度)
fixed luminance(fixed4 color)
{
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
//提取得到的亮度值和原紋理顏色值相乘,得到亮部區域
fixed4 fragExtractBright(v2f i):SV_Target
{
float4 c = tex2D(_MainTex,i.uv);
fixed val = clamp(luminance(c) - _LuminanceThreshold,0.0,1.0);
return c * val;
}
struct v2fBloom
{
float4 pos:SV_POSITION;
half4 uv:TEXCOORD0;
};
v2fBloom vertBloom(appdata_img v)
{
v2fBloom o;
o.pos = UnityObjectToClipPos(v.vertex);
//獲得原紋理uv座標
o.uv.xy = v.texcoord;
//獲得bloom後的紋理uv座標
o.uv.zw = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0.0)
o.uv.w = 1.0 - o.uv.w;
#endif
return o;
}
//原紋理和高斯模糊後的亮部區域紋理疊加
fixed4 fragBloom(v2fBloom i):SV_Target
{
return tex2D(_MainTex,i.uv.xy) + tex2D(_Bloom,i.uv.zw);
}
ENDCG
ZTest Always Cull Off ZWrite Off
//根據閾值提取得到亮部紋理
Pass
{
CGPROGRAM
#pragma vertex vertExtractBright
#pragma fragment fragExtractBright
ENDCG
}
//垂直方向 & 水平方向 利用高斯核進行模糊計算
UsePass "Chan/Chapter12-GaussianBlur_Chan/GAUSSIAN_BLUR_VERTICAL"
UsePass "Chan/Chapter12-GaussianBlur_Chan/GAUSSIAN_BLUR_HORIZONTAL"
//原紋理和高斯模糊紋理疊加,得到最終輸出顯示紋理
Pass
{
CGPROGRAM
#pragma vertex vertBloom
#pragma fragment fragBloom
ENDCG
}
}
FallBack Off
}
配合使用的C#:
using UnityEngine;
using System.Collections;
public class Bloom : PostEffectsBase {
public Shader bloomShader;
private Material bloomMaterial = null;
public Material material {
get {
bloomMaterial = CheckShaderAndCreateMaterial(bloomShader, bloomMaterial);
return bloomMaterial;
}
}
// Blur iterations - larger number means more blur.
[Range(0, 4)]
public int iterations = 3;
// Blur spread for each iteration - larger value means more blur
[Range(0.2f, 3.0f)]
public float blurSpread = 0.6f;
[Range(1, 8)]
public int downSample = 2;
//設置一個閾值,提取紋理亮部
[Range(0.0f, 4.0f)]
public float luminanceThreshold = 0.6f;
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
material.SetFloat("_LuminanceThreshold", luminanceThreshold);
int rtW = src.width/downSample;
int rtH = src.height/downSample;
RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
buffer0.filterMode = FilterMode.Bilinear;
//創建一個紋理,調用Shader 中的 Bloom的第一個Pass,計算高亮部分
Graphics.Blit(src, buffer0, material, 0);
for (int i = 0; i < iterations; i++) {
material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
//material 中shader的 第二pass進行垂直方向的莫斯計算
//對應Shader Bloom的 UsePass "Chan/Chapter12-GaussianBlur_Chan/GAUSSIAN_BLUR_VERTICAL"
Graphics.Blit(buffer0, buffer1, material, 1);
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
//UsePass "Chan/Chapter12-GaussianBlur_Chan/GAUSSIAN_BLUR_HORIZONTAL"
Graphics.Blit(buffer0, buffer1, material, 2);
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
}
//計算好的帶亮度的高斯模糊的紋理,傳入Shader中的_Bloom,調用第四個Pass,和原紋理疊加
material.SetTexture ("_Bloom", buffer0);
Graphics.Blit (src, dest, material, 3);
RenderTexture.ReleaseTemporary(buffer0);
} else {
Graphics.Blit(src, dest);
}
}
}
5.運動模糊
實現方法:
- 利用一塊累積緩存來混合多張連續的圖像,當物體快速移動產生多張圖像後,取它們之間的平均值作爲最後的運動模糊圖像。此方法,性能消耗很大。
- 利用速度緩存,緩存中存儲了當前各個像素當前的運動速度,然後利用該值決定模糊的方向和大小。
Shader "Chan/Chapter 12/Motion Blur" {
Properties
{
_MainTex("Base Tex",2D) = "white"{}
//模糊參數
_BlurAmount("Blur Amount",Float) = 1.0
}
SubShader
{
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed _BlurAmount;
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;
}
//用於更新渲染紋理的RGB通道
fixed4 fragRGB(v2f i):SV_Target
{
return fixed4(tex2D(_MainTex,i.uv).rgb,_BlurAmount);
}
//用於更新渲染紋理的A通道
half4 fragA(v2f i):SV_Target
{
return tex2D(_MainTex,i.uv);
}
ENDCG
ZTest Always Cull Off
ZWrite Off
Pass
{
//開啓混合,並設置混合模式
Blend SrcAlpha OneMinusSrcAlpha
//ColorMask 可以指定渲染結果的輸出通道,而不是通常的 RGBA 四個通道都被寫入。
ColorMask RGB
CGPROGRAM
#pragma vertex vert
#pragma fragment fragRGB
ENDCG
}
Pass
{
//開啓混合,並設置混合模式
Blend One Zero
//ColorMask 可以指定渲染結果的輸出通道,而不是通常的 RGBA 四個通道都被寫入。
ColorMask A
CGPROGRAM
//爲了保證顏色緩存的透明度不變,這樣保證後續屏幕後處理中有關透明度操作沒問題
#pragma vertex vert
#pragma fragment fragA
ENDCG
}
}
FallBack Off
}
註解:
分爲兩個Pass渲染,是爲了
//爲了保證顏色緩存的透明度不變,這樣保證後續屏幕後處理中有關透明度操作沒問題
配合使用的C#
using UnityEngine;
using System.Collections;
public class MotionBlur : PostEffectsBase {
public Shader motionBlurShader;
private Material motionBlurMaterial = null;
public Material material {
get {
motionBlurMaterial = CheckShaderAndCreateMaterial(motionBlurShader, motionBlurMaterial);
return motionBlurMaterial;
}
}
//模糊參數
[Range(0.0f, 0.9f)]
public float blurAmount = 0.5f;
private RenderTexture accumulationTexture;
void OnDisable() {
DestroyImmediate(accumulationTexture);
}
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
//創建一塊累積緩存,並用當前幀圖像初始化
if (accumulationTexture == null || accumulationTexture.width != src.width || accumulationTexture.height != src.height) {
DestroyImmediate(accumulationTexture);
accumulationTexture = new RenderTexture(src.width, src.height, 0);
accumulationTexture.hideFlags = HideFlags.HideAndDontSave;
Graphics.Blit(src, accumulationTexture);
}
//表明我們需要進行一個渲染紋理的操作,但是爲了疊加紋理,不清空accumulationTexture
//爲了不讓Unity發出警告誤以爲是忘記清空了。所以寫這麼一句
accumulationTexture.MarkRestoreExpected();
material.SetFloat("_BlurAmount", 1.0f - blurAmount);
//把當前屏幕圖像src疊加到 accumulationTexture中
Graphics.Blit (src, accumulationTexture, material);
//把混合結果顯示到屏幕中
Graphics.Blit (accumulationTexture, dest);
} else {
Graphics.Blit(src, dest);
}
}
}
註解:
//進行一個渲染紋理的操作,但是爲了疊加紋理,不清空accumulationTexture
//爲了不讓Unity發出警告誤以爲是忘記清空了。所以寫這麼一句
accumulationTexture.MarkRestoreExpected();
https://github.com/candycat1992/Unity_Shaders_Book/issues/51