URP管線,RenderFeature后處理模糊小記

參數(shù)界面

參考文章:Carle專欄? 算法取自毛老師? https://zhuanlan.zhihu.com/p/125744132
模糊算法這里用的是Kawase和DualKawase,最后整合到了一起~~
文末附代碼~~~~~
(才接觸RenderFeature,如有解釋不準(zhǔn)確的地方,希望斧正 感謝)
簡(jiǎn)單解釋一下Kawase思路:

一、算法解析

Kawase模糊精華在于算子是隨模糊迭代而變化的,圖中黑點(diǎn)為被渲染像素點(diǎn),紅點(diǎn)為采樣點(diǎn),由采樣點(diǎn)的值計(jì)算出被渲染像素點(diǎn)的值。注意,這里4個(gè)采樣點(diǎn)位于黑點(diǎn)像素的四個(gè)角??!四個(gè)角!意味著這里UV偏移就不能是意義上的One pixe,而是偏移半個(gè)像素點(diǎn)的距離,知道這里后咋們上蒜子圖~~

二、shader部分
回顧KawaseBlur圖,第一次采樣UV偏移為?0.5 * KawaseBlurKernel[m] * TexturePixeSize。第二次采樣UV偏移為1.5*XXX,第三次采樣為2.5*XXX~~~如此類推,所以說(shuō)算子是迭代變化的。上shader關(guān)鍵代碼:

三、RenderFeature部分
shader都是小菜,RenderFeature才是最復(fù)雜的,上菜!??!

這里我喜歡把Setting、Feature、Pass分別定義成單獨(dú)的CS文件。
1、Setting解析
這里定義的參數(shù)是顯示到面板的接口,


看紅框部分!??!
藥點(diǎn)
①[System.Serializable]是“序列化參數(shù)列表”,沒(méi)懂什么意思,大概看來(lái)就是配合后面在Feature中定義好Public該類,就能暴露出參數(shù),可通過(guò)面板直接修改。
②這里shader可直接Feature中指定路徑,也可以暴露出來(lái)手動(dòng)指定
③downsamplescale是將處理源圖像給縮小的倍數(shù),passloop是迭代次數(shù),BlurRadius是將要傳入到Pass中的定義的Offset值
2、Feature解析

藥點(diǎn)
①Create函數(shù)當(dāng)Setting變化時(shí)執(zhí)行,所以這里我們將函數(shù)外聲明好的m_pass放到這里面進(jìn)行實(shí)例化,他的有參構(gòu)造函數(shù)在Pass的Class中,簡(jiǎn)單的賦值并指定渲染時(shí)機(jī)。

②AddRenderPasses每幀執(zhí)行,將Pass放入到緩沖池里。這里可以做一個(gè)判空,有需要再Debug一下也行。
③Setupsource函數(shù)是我們定義的將相機(jī)圖像傳入到RT的一個(gè)函數(shù)。
3、Pass解析,重中之重來(lái)咯

這里定義了Pass類中需要的變量,以及enum類型的BlurPassIndex。另外臨時(shí)RT的申請(qǐng)流程分三步走,
先用?Shader.PropertyToID("XXXX")定義int類型的ID,
再用cmd.GetTemporaryRT(ID)申請(qǐng)RT,
最后實(shí)例化RT(將RT與ID綁定的操作)RenderTargetIdentifier RTNAME = new?RenderTargetIdentifier(ID)。

這里因?yàn)楹竺嫔婕暗絉T在循環(huán)中申請(qǐng)和釋放,所以我只是先聲明了RT并未將其實(shí)例化。
Pass的大體結(jié)構(gòu)可分為三部分
第一部分:

OnCameraSetup函數(shù)是初始化函數(shù),可以將申請(qǐng)RT、配置RT、申請(qǐng)變量等初始化操作放在這里面執(zhí)行。(這里的操作也可以放到第二部分Execute中做,看個(gè)人選擇和功能需求)
第二部分:

Render函數(shù)如圖

Execute函數(shù)就是具體Pass的執(zhí)行函數(shù)了,也是最重要的部分。
藥點(diǎn)
①因?yàn)镋xecute函數(shù)中沒(méi)有自定義commandBuffer,所以我們會(huì)在函數(shù)體內(nèi)用CommandBufferPool.Get(“PassNameXXX”)命令申請(qǐng)一個(gè)CommandBuffer類型的緩沖區(qū)命令,最后由context.ExecuteCommandBuffer(cmd);注入到緩沖池中
②用了個(gè)Switch控制Feature執(zhí)行哪個(gè)函數(shù),因?yàn)槲覀儗awase和DualKawase寫到了一起
③申請(qǐng)的緩沖區(qū)命令和RT一樣,最終都需要手動(dòng)去釋放以免內(nèi)存泄漏。CommandBufferPool.Release(cmd);
④看Render函數(shù),先用cmd.Blit命令將Source圖像復(fù)制給RT1,再結(jié)合for函數(shù)和一個(gè)bool值和語(yǔ)法糖,乒乓執(zhí)行Pass

稍等一下,插句嘴



看到這里細(xì)心的朋友應(yīng)該注意到了,我們用RT1 RT2都是先申請(qǐng)后使用,為什么這個(gè)this.Source只是聲明了都沒(méi)申請(qǐng)就能用?。。。?/span>
而且最后一個(gè)Blit我們將結(jié)果直接復(fù)制到Source中就收工大吉了,
在FreamDebug中卻顯示我們最后的Blit是將結(jié)果Copy給了cameraColorTarget
這是為什么呢???有的同學(xué)肯定就說(shuō)了Class類直接用等號(hào)賦值相當(dāng)于引用,可以理解為指針。但是我發(fā)現(xiàn)RT、cameraColorTarget、Source的類型RenderTargetIdentifier為結(jié)構(gòu)體??!不是Class!??!結(jié)構(gòu)體等號(hào)賦值實(shí)際上是新定義了一個(gè)變量,這里把我困擾到了,所以我覺(jué)得最合理的應(yīng)該把這里的Source全部替換為cameraColorTarget,省去AddRenderPasses里的那步,我懶了 試過(guò)可行,這兒就把重新截圖了。Unity肯定哪步將二者的地址給指定了一下,所以我最后也不用再釋放Source這個(gè)變量了......
有知道的朋友能告知一下原因嗎!?。。。。?!感謝了

繼續(xù)藥點(diǎn)
⑤這里這個(gè)語(yǔ)法糖我覺(jué)得非常巧妙,避免了我們?nèi)ピ俣x一個(gè)邏輯或者RT來(lái)充當(dāng)temp打工人。
⑥最后釋放兩張RT,這步其實(shí)可以放到接下來(lái)要截圖的第三部分中,但是我還是就寫在第二步的Execute中吧。
三、第三部分

就是可以用來(lái)最終注銷釋放RT等操作,沒(méi)什么可說(shuō)的。
DualKawase就先鴿一鴿吧,碼字太累了...
上源碼:
BlurSetting.cs

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace BlurSettingnamespace
{
? ? [System.Serializable]
? ? public class BlurSetting
? ? {
? ? ? ? public Shader shader = null;
? ? ? ? public BlurType blurType = BlurType.Kawase;
? ? ? ? public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingSkybox;
? ? ? ? public enum BlurType
? ? ? ? {
? ? ? ? ? ? Kawase ,
? ? ? ? ? ? DualKawase,
? ? ? ? ? ? Gaussian
? ? ? ? }? ? ? ?
? ? }
? ? [System.Serializable]
? ? public class KawaseBlurSetting
? ? {
? ? ? ? [Range(1, 10)]
? ? ? ? public int downsamplescale = 1;
? ? ? ? [Range(0, 10)]
? ? ? ? public int passloop = 5;
? ? ? ? [Range(0.0f, 5.0f)]
? ? ? ? public float BlurRadius = 1.0f;
? ? }
? ? [System.Serializable]
? ? public class DualKawaseBlurSetting
? ? {
? ? ? ? [Range(0, 10)]
? ? ? ? public int iteration = 5;
? ? ? ? [Range(0.0f, 5.0f)]
? ? ? ? public float BlurRadius = 1.0f;
? ? }
}

KawaseBlurRenderPass.cs

using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using BlurSettingnamespace;
namespace KawaseBlurRenderPassnamespace
{
? ? class KawaseBlurRenderPass : ScriptableRenderPass
? ? {
? ? ? ? private BlurSetting m_setting;
? ? ? ? private KawaseBlurSetting kawaseBlurSetting;
? ? ? ? private DualKawaseBlurSetting dualKawaseBlurSetting;
? ? ? ??
? ? ? ? enum BlurPassIndex
? ? ? ? {
? ? ? ? ? ? KawasePass = 0,
? ? ? ? ? ? DualKawaseUpPass = 1,
? ? ? ? ? ? DualKawaseDownPass = 2
? ? ? ? }
? ? ? ? public KawaseBlurRenderPass(BlurSetting blurSetting, KawaseBlurSetting kawaseBlurSetting , DualKawaseBlurSetting dualKawaseBlurSetting)
? ? ? ? {
? ? ? ? ? ? this.m_setting = blurSetting;
? ? ? ? ? ? this.kawaseBlurSetting = kawaseBlurSetting;
? ? ? ? ? ? this.dualKawaseBlurSetting = dualKawaseBlurSetting;
? ? ? ? ? ? this.renderPassEvent = blurSetting.passEvent;? ? ? ? ? ??
? ? ? ? }
? ? ? ? //定義源RT
? ? ? ? private RenderTargetIdentifier Source { get; set; }
? ? ? ? //聲明兩個(gè)臨時(shí)RT(未實(shí)例化)? 定義其ID
? ? ? ? private readonly int TempRTID1 = Shader.PropertyToID("TempRTID1Name");
? ? ? ? private readonly int TempRTID2 = Shader.PropertyToID("TempRTID2Name");? ? ? ??
? ? ? ? RenderTargetIdentifier TempRT1;
? ? ? ? RenderTargetIdentifier TempRT2;
? ? ? ? //定義一個(gè)shader參數(shù)ID
? ? ? ? private readonly int _OffsetID = Shader.PropertyToID("_Offset");
? ? ? ? Material passmat;
? ? ? ? public void Setupsource(RenderTargetIdentifier source)
? ? ? ? {
? ? ? ? ? ? this.Source = source;
? ? ? ? }
? ? ? ? // This method is called before executing the render pass.
? ? ? ? // It can be used to configure render targets and their clear state. Also to create temporary render target textures.
? ? ? ? // When empty this render pass will render to the active camera render target.
? ? ? ? // You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
? ? ? ? // The render pipeline will ensure target setup and clearing happens in a performant manner.
? ? ? ? public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
? ? ? ? {
? ? ? ? ? ? if(m_setting.blurType == BlurSetting.BlurType.Kawase)
? ? ? ? ? ? ? ? Initialize(cmd, renderingData);
? ? ? ? ? ? if (m_setting.shader == null)
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? else? ? ? ? ? ??
? ? ? ? ? ? ? ? passmat = new Material(m_setting.shader);?
? ? ? ? ? ??
? ? ? ? }
? ? ? ? // Here you can implement the rendering logic.
? ? ? ? // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
? ? ? ? // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
? ? ? ? // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
? ? ? ? public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
? ? ? ? {
? ? ? ? ? ? CommandBuffer cmd = CommandBufferPool.Get("KawaseBlurPass");
? ? ? ? ? ? //重要操作
? ? ? ? ? ? switch(m_setting.blurType)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? case BlurSetting.BlurType.Kawase:
? ? ? ? ? ? ? ? ? ? Render(cmd , renderingData);
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case BlurSetting.BlurType.DualKawase:
? ? ? ? ? ? ? ? ? ? DualRender(cmd, renderingData);
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? context.ExecuteCommandBuffer(cmd);
? ? ? ? ? ? CommandBufferPool.Release(cmd);? ? ? ? ? ??
? ? ? ? }
? ? ? ? // Cleanup any allocated resources that were created during the execution of this render pass.
? ? ? ? public override void OnCameraCleanup(CommandBuffer cmd)
? ? ? ? {
? ? ? ? }
? ? ? ? void Initialize(CommandBuffer cmd , RenderingData renderingData)
? ? ? ? {
? ? ? ? ? ? RenderTextureDescriptor renderTextureDescriptor = renderingData.cameraData.cameraTargetDescriptor;
? ? ? ? ? ? int width = Mathf.Max(renderTextureDescriptor.width / kawaseBlurSetting.downsamplescale, 1);
? ? ? ? ? ? int height = Mathf.Max(renderTextureDescriptor.height / kawaseBlurSetting.downsamplescale, 1);
? ? ? ? ? ? //申請(qǐng)兩個(gè)臨時(shí)RT
? ? ? ? ? ? cmd.GetTemporaryRT(TempRTID1, width, height, 0, FilterMode.Trilinear, renderTextureDescriptor.colorFormat);
? ? ? ? ? ? cmd.GetTemporaryRT(TempRTID2, width, height, 0, FilterMode.Trilinear, renderTextureDescriptor.colorFormat);
? ? ? ? ? ? //指定RT? 實(shí)例化的過(guò)程
? ? ? ? ? ? TempRT1 = new RenderTargetIdentifier(TempRTID1);
? ? ? ? ? ? TempRT2 = new RenderTargetIdentifier(TempRTID2);
? ? ? ? }
? ? ? ? void DualInitialize(CommandBuffer cmd , RenderingData renderingData)
? ? ? ? {
? ? ? ? ? ? //RenderTextureDescriptor renderTextureDescriptor = renderingData.cameraData.cameraTargetDescriptor;
? ? ? ? ? ??
? ? ? ? }
? ? ? ? void Render(CommandBuffer cmd ,RenderingData renderingData)
? ? ? ? {
? ? ? ? ? ? cmd.Blit(this.Source, TempRTID1);
? ? ? ? ? ? bool NeedSwith = true;
? ? ? ? ? ? for(int i = 1;i < (kawaseBlurSetting.passloop + 1); i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //cmd.SetGlobalFloat(_OffsetID , i / kawaseBlurSetting.downsamplescale + kawaseBlurSetting.BlurRadius);
? ? ? ? ? ? ? ? cmd.SetGlobalFloat(_OffsetID , i + kawaseBlurSetting.BlurRadius);
? ? ? ? ? ? ? ? cmd.Blit(NeedSwith ? TempRT1 : TempRT2, NeedSwith ? TempRT2 : TempRT1, passmat, (int)BlurPassIndex.KawasePass);
? ? ? ? ? ? ? ? NeedSwith = !NeedSwith;
? ? ? ? ? ? }
? ? ? ? ? ? cmd.Blit(NeedSwith ? TempRT1 : TempRT2, this.Source);
? ? ? ? ? ? //釋放RT
? ? ? ? ? ? cmd.ReleaseTemporaryRT(TempRTID1);
? ? ? ? ? ? cmd.ReleaseTemporaryRT(TempRTID2);
? ? ? ? }
? ? ? ? void DualRender(CommandBuffer cmd , RenderingData renderingData)
? ? ? ? {
? ? ? ? ? ? RenderTextureDescriptor renderTextureDescriptor = renderingData.cameraData.cameraTargetDescriptor;
? ? ? ? ? ? int Width = renderTextureDescriptor.width;
? ? ? ? ? ? int Height = renderTextureDescriptor.height;
? ? ? ? ? ? bool IsChange = true;
? ? ? ? ? ? //初始化RT1 RT2
? ? ? ? ? ? SetupRT(cmd, TempRT1, TempRTID1, Width, Height);
? ? ? ? ? ? Width /= 2;
? ? ? ? ? ? Height /= 2;
? ? ? ? ? ? SetupRT(cmd, TempRT2, TempRTID2, Width , Height);
? ? ? ? ? ? //將Source傳入RT1中
? ? ? ? ? ? cmd.Blit(this.Source, TempRTID1);
? ? ? ? ? ? //傳遞參數(shù)給shader
? ? ? ? ? ? cmd.SetGlobalFloat(_OffsetID, dualKawaseBlurSetting.BlurRadius);
? ? ? ? ? ? //DownSample
? ? ? ? ? ? for (int i = 1;i < dualKawaseBlurSetting.iteration + 1; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Width /= 2;
? ? ? ? ? ? ? ? Height /= 2;
? ? ? ? ? ? ? ? cmd.Blit(IsChange ? TempRTID1 : TempRTID2,
? ? ? ? ? ? ? ? ? ? ? ? IsChange ? TempRTID2 : TempRTID1, passmat, (int)BlurPassIndex.DualKawaseDownPass);
? ? ? ? ? ? ? ? IsChange = !IsChange;
? ? ? ? ? ? ? ? //釋放RT 準(zhǔn)備參加下次循環(huán)計(jì)算
? ? ? ? ? ? ? ? cmd.ReleaseTemporaryRT(IsChange ? TempRTID2 : TempRTID1);
? ? ? ? ? ? ? ? //重新申請(qǐng)RT 準(zhǔn)備參加下次循環(huán)計(jì)算
? ? ? ? ? ? ? ? SetupRT(cmd, IsChange ? TempRT2 : TempRT1, IsChange ? TempRTID2 : TempRTID1, Width, Height);? ??
? ? ? ? ? ? }
? ? ? ? ? ? //找到最后參與Pass計(jì)算的RT 此時(shí)兩張RT都已申請(qǐng),未釋放
? ? ? ? ? ? //釋放最后申請(qǐng)的RT
? ? ? ? ? ? cmd.ReleaseTemporaryRT(IsChange ? TempRTID2 : TempRTID1);
? ? ? ? ? ? //重新申請(qǐng)正常尺寸的RT
? ? ? ? ? ? Width *= 4;
? ? ? ? ? ? Height *= 4;
? ? ? ? ? ? SetupRT(cmd, IsChange ? TempRT2 : TempRT1, IsChange ? TempRTID2 : TempRTID1, Width, Height);
? ? ? ? ? ? //升采樣循環(huán)
? ? ? ? ? ? for (int i = 1; i < dualKawaseBlurSetting.iteration + 1; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Width *= 2;
? ? ? ? ? ? ? ? Height *= 2;
? ? ? ? ? ? ? ? cmd.Blit(IsChange ? TempRTID1 : TempRTID2,
? ? ? ? ? ? ? ? ? ? ? ? IsChange ? TempRTID2 : TempRTID1, passmat, (int)BlurPassIndex.DualKawaseUpPass);
? ? ? ? ? ? ? ? IsChange = !IsChange;
? ? ? ? ? ? ? ? //釋放RT
? ? ? ? ? ? ? ? cmd.ReleaseTemporaryRT(IsChange ? TempRTID2 : TempRTID1);
? ? ? ? ? ? ? ? //重新申請(qǐng)RT
? ? ? ? ? ? ? ? SetupRT(cmd, IsChange ? TempRT2 : TempRT1, IsChange ? TempRTID2 : TempRTID1, Width, Height);
? ? ? ? ? ? }
? ? ? ? ? ? //最后將最后參與計(jì)算的RT傳遞回源RT中,再將兩個(gè)RT都釋放
? ? ? ? ? ? cmd.Blit(IsChange ? TempRTID1 : TempRTID2, this.Source);
? ? ? ? ? ? cmd.ReleaseTemporaryRT(TempRTID1);
? ? ? ? ? ? cmd.ReleaseTemporaryRT(TempRTID2);
? ? ? ? }
? ? ? ? void SetupRT(CommandBuffer cmd, RenderTargetIdentifier renderTargetIdentifier, int ID , int width , int heigh)
? ? ? ? {
? ? ? ? ? ? //申請(qǐng)臨時(shí)RT
? ? ? ? ? ? cmd.GetTemporaryRT(ID, width, heigh, 0, FilterMode.Trilinear, RenderTextureFormat.Default);
? ? ? ? ? ? //實(shí)例化RT
? ? ? ? ? ? renderTargetIdentifier = new RenderTargetIdentifier(ID);
? ? ? ? }
? ? }
}

KawaseBlurRenderfeature.cs

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using KawaseBlurRenderPassnamespace;
using BlurSettingnamespace;
public class KawaseBlurRenderfeature : ScriptableRendererFeature
{? ??
? ? public BlurSetting blurSetting = new();
? ? public KawaseBlurSetting kawaseBlurSetting = new();
? ? public DualKawaseBlurSetting dualKawaseBlurSetting = new();
? ? //public DualKawaseBlurSetting dualKawaseBlurSetting;
? ? KawaseBlurRenderPass m_pass;
? ??
? ? public override void Create()
? ? {
? ? ? ? m_pass = new(blurSetting, kawaseBlurSetting, dualKawaseBlurSetting);? ?
? ? }
? ? // Here you can inject one or multiple render passes in the renderer.
? ? // This method is called when setting up the renderer once per-camera.
? ? public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
? ? {
? ? ? ? m_pass.Setupsource(renderer.cameraColorTarget);
? ? ? ? if(blurSetting.shader != null && kawaseBlurSetting.passloop != 0 && dualKawaseBlurSetting.iteration != 0)
? ? ? ? ? ? renderer.EnqueuePass(m_pass);
? ? }
}

shader源碼(本來(lái)我寫成了三個(gè)文件,但是路徑難免會(huì)不同,我就隨便整合成一個(gè)了,湊合用吧)

Shader "Post/KawaseBlur"
{
? ? Properties
? ? {
? ? ? ? [HideInInspector] _MainTex ("Texture", 2D) = "white" { }
? ? }
? ? HLSLINCLUDE
? ? #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
? ? //#include "../../RenderFeature/PostShaderSource/postprocessing.hlsl"
? ? //#include "../../RenderFeature/PostShaderSource/KawaseBlurKernel.hlsl"
? ? /////////////////////////////////////////////////////////////////////////////////////////
? ? //kawaseBlur算子和雙重向下采樣算子
? ? static float2 KawaseBlurKernel[4] = {
? ? ? ? float2(-1, -1), //左下
? ? ? ? float2(-1, 1), //左上
? ? ? ? float2(1, 1), //右上
? ? ? ? float2(1, -1) ? ? ?//右下
? ? };
? ? //雙重向上采樣算子
? ? static float2 KawaseBlurKernel_Up[8] = {
? ? ? ? float2(0, 1),
? ? ? ? float2(0.5, 0.5),
? ? ? ? float2(1, 0),
? ? ? ? float2(0.5, -0.5),
? ? ? ? float2(0, -1),
? ? ? ? float2(-0.5, -0.5),
? ? ? ? float2(-1, 0),
? ? ? ? float2(-0.5, 0.5),
? ? };
? ? float4 KawaseBlur(TEXTURE2D(Tex), SAMPLER(samplertex), float2 uv, float2 TexSize, float PixeOffset)
? ? {
? ? ? ? float4 col = 0;
? ? ? ? int loopnum = 4;
? ? ? ? for (int i = 0; i < loopnum; i++)
? ? ? ? col += SAMPLE_TEXTURE2D(Tex, samplertex, uv + (float2) (PixeOffset +0.5f) * KawaseBlurKernel[i] * TexSize);
? ? ? ? return col / loopnum;
? ? }
? ? float4 DualKawaseBlur_Up(TEXTURE2D(Tex), SAMPLER(samplertex), float2 uv, float2 TexSize, float PixeOffset)
? ? {
? ? ? ? float4 col = 0;
? ? ? ? for (int i = 4; i < 0; i++)
? ? ? ? {
? ? ? ? ? ? col += SAMPLE_TEXTURE2D(Tex, samplertex, uv + KawaseBlurKernel_Up[i] * (1.0 + PixeOffset) * TexSize);
? ? ? ? ? ? col += SAMPLE_TEXTURE2D(Tex, samplertex, uv + KawaseBlurKernel_Up[i + 1] * (1.0 + PixeOffset) * TexSize) * 2;
? ? ? ? }
? ? ? ? return col * 0.0833;
? ? }
? ? float4 DualKawaseBlur_Down(TEXTURE2D(Tex), SAMPLER(samplertex), float2 uv, float2 TexSize, float PixeOffset)
? ? {
? ? ? ? float4 col = SAMPLE_TEXTURE2D(Tex, samplertex, uv) * 0.5;
? ? ? ? for (int i = 0; i < 4; i++)
? ? ? ? col += SAMPLE_TEXTURE2D(Tex, samplertex, uv + KawaseBlurKernel[i] * (float2)PixeOffset * TexSize) * 0.125;
? ? ? ? return col;
? ? }
? ? struct appdata
? ? {
? ? ? ? float4 vertex : POSITION;
? ? ? ? float2 uv : TEXCOORD0;
? ? };
? ? struct v2f
? ? {
? ? ? ? float2 uv : TEXCOORD0;
? ? ? ? float4 vertex : SV_POSITION;
? ? };
? ?
? ? TEXTURE2D(_MainTex);
? ? SAMPLER(sampler_MainTex);
? ? CBUFFER_START(UnityPerMaterial)
? ? ? ? float4 _MainTex_TexelSize;
? ? CBUFFER_END
? ?
? ? v2f VertDefault(appdata v)
? ? {
? ? ? ? v2f o;
? ? ? ? o.vertex = TransformObjectToHClip(v.vertex.xyz);
? ? ? ? o.uv = v.uv;
? ? ? ? return o;
? ? }
? ? //////////////////////////////////////////////////////////////////////////////////////////////
? ? float _Offset;
? ? float4 frag(v2f i) : SV_Target
? ? {
? ? ? ? // sample the texture
? ? ? ? float4 col = KawaseBlur(_MainTex, sampler_MainTex, i.uv, _MainTex_TexelSize.xy, _Offset);
? ? ? ? return col;
? ? }
? ? float4 frag_Up(v2f i) : SV_Target
? ? {
? ? ? ? // sample the texture
? ? ? ? float4 col = KawaseBlur(_MainTex, sampler_MainTex, i.uv, _MainTex_TexelSize.xy, _Offset);
? ? ? ? return col;
? ? }
? ? float4 frag_Down(v2f i) : SV_Target
? ? {
? ? ? ? // sample the texture
? ? ? ? float4 col = KawaseBlur(_MainTex, sampler_MainTex, i.uv, _MainTex_TexelSize.xy, _Offset);
? ? ? ? return col;
? ? }
? ? ENDHLSL
? ? SubShader
? ? {
? ? ? ? Tags { "RenderType" = "Opaque" }
? ? ? ? Cull Off
? ? ? ? ZWrite Off
? ? ? ? ZTest Always
? ? ? ?
? ? ? ? Pass
? ? ? ? {
? ? ? ? ? ? Name "KawaseBlurPass"
? ? ? ? ? ? HLSLPROGRAM
? ? ? ? ? ? #pragma vertex VertDefault
? ? ? ? ? ? #pragma fragment frag
? ? ? ? ? ? ENDHLSL
? ? ? ? }
? ? ? ? Pass
? ? ? ? {
? ? ? ? ? ? Name "DualKawaseBlurPass_UpSampling"
? ? ? ? ? ? HLSLPROGRAM
? ? ? ? ? ? #pragma vertex VertDefault
? ? ? ? ? ? #pragma fragment frag_Up
? ? ? ? ? ? ENDHLSL
? ? ? ? }
? ? ? ? Pass
? ? ? ? {
? ? ? ? ? ? Name "DualKawaseBlurPass_DownSampling"
? ? ? ? ? ? HLSLPROGRAM
? ? ? ? ? ? #pragma vertex VertDefault
? ? ? ? ? ? #pragma fragment frag_Down
? ? ? ? ? ? ENDHLSL
? ? ? ? }
? ? }
}