參考自《Unity Shader入門簡要》
原理梳理
- 存儲上一幀渲染效果的成像矩陣和當前幀成像逆矩陣
- 通過深度值獲得當前NDC坐標
- 通過NDC坐標和當前幀逆矩陣獲得頂點世界坐標
- 頂點世界坐標與上一幀成像矩陣獲取上一幀NDC坐標,求取同一頂點在兩幀內(nèi)NDC距離的差值
- 根據(jù)距離計算疊加上一幀uv和像素值
- 片元返回疊加后采樣效果,即該幀顯示兩幀效果,實現(xiàn)運動模糊
實現(xiàn)效果
C#代碼
void OnEnable()
{
//設(shè)置相機屬性,生成深度紋理
camera.Main.depthTextureMode = DepthTextureMode.Depth;
}
/*
OnRenderImage為MonoBehaviour內(nèi)模板函數(shù),類似與Awake等
通過該方法獲取屏幕成像前的RenderTexture
該方法時后期屏幕效果處理的基礎(chǔ)
*/
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
material.SetFloat("_BlurSize", blurSize);
material.SetMatrix("_PreviousViewProjectionMatrix", previousViewProjectionMatrix);
//通過透視投影矩陣*VP矩陣獲取成像矩陣,運用VP矩陣原因是因為在shader中我們將通過世界坐標運算
Matrix4x4 currentViewProjectionMatrix = camera_.projectionMatrix * camera_.worldToCameraMatrix;
//通過成像逆矩陣和深度獲取頂點世界坐標
Matrix4x4 currentViewProjectionInverseMatrix = currentViewProjectionMatrix.inverse;
material.SetMatrix("_CurrentViewProjectionInverseMatrix", currentViewProjectionInverseMatrix);
previousViewProjectionMatrix = currentViewProjectionMatrix;
//將material應(yīng)用到RenderTexture實現(xiàn)效果
Graphics.Blit(src, dest, material);
}
else
{
Graphics.Blit(src, dest);
}
}
Shader 代碼
Shader "Youcai/Chapter13/MotionBlurDepth"
{
Properties
{
_MainTex("Base (RGB)", 2D) = "white"{}
_BlurSize("BlurSize", float) = 0.5
}
SubShader
{
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
//獲取_MainTex貼圖紋素值
half4 _MainTex_TexelSize;
//深度紋理,設(shè)置對應(yīng)的相機屬性在Shader內(nèi)直接定義即可
sampler2D _CameraDepthTexture;
float4x4 _CurrentViewProjectionInverseMatrix;
float4x4 _PreviousViewProjectionMatrix;
half _BlurSize;
struct v2f
{
float4 pos: SV_POSITION;
half2 uv: TEXCOORD0;
half2 uv_depth: TEXCOORD1;
};
v2f vert(appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.uv_depth = v.texcoord;
//處理平臺差異存在的圖像翻轉(zhuǎn)問題
#if UNITY_UV_STARTS_AT_TOP
o.uv_depth.y = 1 - o.uv_depth.y;
#endif
return o;
}
fixed4 frag(v2f i) :SV_Target
{
//獲取深度值
float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);
//通過深度紋理獲取NDC坐標
float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
//通過NDC坐標和成像逆矩陣獲取世界坐標
float4 D = mul(_CurrentViewProjectionInverseMatrix, H);
float4 worldPos = D / D.w;
float4 currentPos = H;
//通過上一幀成像矩陣和世界坐標獲取上一幀NDC坐標
float4 previousPos = mul(_PreviousViewProjectionMatrix, worldPos);
previousPos /= previousPos.w;
//疊加兩幀uv和像素值
float2 velocity = (currentPos.xy - previousPos.xy);
float2 uv = i.uv;
float4 c = tex2D(_MainTex, uv);
uv += velocity * _BlurSize;
c += tex2D(_MainTex, uv);
//平分像素值
c /= 2;
return fixed4(c.rgb, 1.0);
}
ENDCG
Pass
{
ZTest Always Cull Off Zwrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
Fallback Off
}
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。