创建Render Feature
Create-Rendering-Universal Render Pipeline-Render Feature
模板
- 学习用
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// 首先由两个类组成
// 继承自 ScriptableRendererFeature 的 CustomRenderPassFeature
// 继承自 ScriptableRenderPass 的 CustomRenderPass
// CustomRenderPassFeature 负责把 Render Pass添加到 Renderer里面。
// Render Feature 在 renderpipline的任意阶段
public class CustomRenderPassFeature : ScriptableRendererFeature
{
// 室实际的渲染工作
// 在 ScriptableRenderPass 里,可以在狗仔甘薯里面去指定了 Event 在什么时间点执行(默认是不透明之后画)
class CustomRenderPass : ScriptableRenderPass
{
// Configure() 在执行渲染过程之前,Renderer 将调用次方法。如果需要配置渲染目标及其清除状态,并创建临时渲染目标纹理,那就要重写这个方法。
// 如果渲染过程未重写这个方法,则该渲染过程将渲染到激活状态下 Camera 的渲染目标。
// 在每次 Execute 调用的前一帧调用,用来设置传递需要的东西
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
}
// https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
// Execute() 是这个类的核心方法,定义我们的执行规则;包含渲染逻辑,设置渲染状态,绘制渲染器或绘制程序网络,调度计算等。
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
}
// 可用于释放通过此过程创建的分配资源。完成渲染相机后调用。就可以使用此回调释放此渲染过程创建的所有资源。
public override void FrameCleanup(CommandBuffer cmd)
{
}
}
CustomRenderPass m_ScriptablePass; // 实例化
// Create() 用来初始化这个 Feature 的资源。这里初始化完了之后要给 AddRenderPasses 函数来在渲染中插入改pass
public override void Create()
{
m_ScriptablePass = new CustomRenderPass();
// 通过 Event 控制在render pipline的何时插入 Pass 以及顺序
m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
}
// AddRenderPasses() 在 Renderer 中插入一个或多个 ScriptableRenderPass,对这个Renderer 每个摄像机都设置一次
// 每个相机,没帧调用一次
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(m_ScriptablePass);
}
}
- 屏幕HSV
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
[ExecuteInEditMode] // 让编辑器在不运行状态下运行
public class mybiltxf : ScriptableRendererFeature
{
[System.Serializable] // renderfeature 的面板
public class mysetting//定义一个设置的类
{
public string PassName = "调色";
public Material mymat;
[Range(0, 1)] public float Brightness = 1;
[Range(0, 1)] public float Saturate = 1;
[Range(-1, 2)] public float Constrast = 1;
public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents; // 增加一个可控渲染事件来决定渲染位置
}
public mysetting setting = new mysetting(); // 实例化
class CustomRenderPass : ScriptableRenderPass//自定义pass
{
public Material passMat = null;
public float Brightness;
public float Saturate;
public float Constrast;
public string name;
public FilterMode passfiltermode { get; set; } //图像的模式,这里设置方便整体调整,这里相当于拿相机rt的设置
private RenderTargetIdentifier passSource { get; set; } //源图像,目标图像
public RenderTargetIdentifier Temp1;
string passTag; // 给cmd对象池用来调用的
public void setup(RenderTargetIdentifier sour) // 把相机的图像copy过来
{
this.passSource = sour;
}
//类似OnRenderimagePass,图像处理部分
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
int TempID1 = Shader.PropertyToID("Temp1");
int brightness = Shader.PropertyToID("_brightness");
int saturate = Shader.PropertyToID("_saturate");
int contranst = Shader.PropertyToID("_contranst");
CommandBuffer cmd = CommandBufferPool.Get(passTag); // 类似于cbuffer,把整个渲染命令圈起来
RenderTextureDescriptor opaquedesc = renderingData.cameraData.cameraTargetDescriptor; // 拿到相机数据
cmd.GetTemporaryRT(TempID1, opaquedesc); //申请一个临时图像
Temp1 = new RenderTargetIdentifier(TempID1);
cmd.SetGlobalFloat(brightness, Brightness); // 往shader里传面板上的值
cmd.SetGlobalFloat(saturate, Saturate);
cmd.SetGlobalFloat(contranst, Constrast);
cmd.Blit(passSource, Temp1, passMat,0); //把源贴图输入到材质对应的pass里处理,并把处理结果的图像存储到临时图像;
cmd.Blit(Temp1, passSource); //然后把临时图像又存到源图像里
context.ExecuteCommandBuffer(cmd); //执行命令缓冲区的该命令
CommandBufferPool.Release(cmd); //释放该命令
}
}
CustomRenderPass mypass;
public override void Create()//进行初始化,这里最先开始
{
mypass = new CustomRenderPass();
mypass.renderPassEvent = setting.passEvent; // 渲染位置
mypass.Brightness = setting.Brightness; // 参数
mypass.Saturate = setting.Saturate;
mypass.Constrast = setting.Constrast;
mypass.passMat = setting.mymat; // 读取材质球
mypass.name = setting.PassName; // frameDebug 里的渲染名字
}
//传值到pass里
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
mypass.setup(renderer.cameraColorTarget);
renderer.EnqueuePass(mypass); // 设置到渲染流程中
}
}
shader
Shader "WX/URP/Post/post"
{
Properties
{
[HideInInspector]_MainTex ("MainTex", 2D) = "white" {}
// _brightness ("Brightness", Range(0, 1)) = 1
// _saturate ("Saturate", Range(0, 1)) = 1
// _contranst ("Constrast", Range(-1, 2)) = 1
}
SubShader
{
Tags {"RenderPipeline" = "UniversalRenderPipeline"}
Cull Off ZWrite Off ZTest Always
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
float _brightness;
float _saturate;
float _contranst;
CBUFFER_END
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
struct a2v
{
float4 positionOS: POSITION;
float2 texcoord: TEXCOORD;
};
struct v2f
{
float4 positionCS: SV_POSITION;
float2 texcoord: TEXCOORD;
};
ENDHLSL
pass
{
HLSLPROGRAM
#pragma vertex VERT
#pragma fragment FRAG
v2f VERT(a2v i)
{
v2f o;
o.positionCS = TransformObjectToHClip(i.positionOS.xyz);
o.texcoord = i.texcoord;
return o;
}
half4 FRAG(v2f i): SV_TARGET
{
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord);
//灰度图,即计算明度。用的是Luminance公式,在sRGB空间下
float gray = 0.21 * tex.x + 0.72 * tex.y + 0.072 * tex.z;
tex.xyz *= _brightness;//计算亮度
tex.xyz = lerp(float3(gray, gray, gray), tex.xyz, _saturate); //饱和度。这里相当于在去色后和当前的图中lerp了
tex.xyz = lerp(float3(0.5, 0.5, 0.5), tex.xyz, _contranst); //对比度
return tex;
}
ENDHLSL
}
}
}
- Kawase
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
[ExecuteInEditMode] // 让编辑器在不运行状态下运行
public class Kawase : ScriptableRendererFeature
{
[System.Serializable] // renderfeature 的面板
public class featureSetting
{
public string passRenderName = "KawaseBlur";
public Material passMat;
[Range(1, 10)] public int downsample = 1;
[Range(1, 10)] public int loop = 2;
[Range(0.5f, 5)] public float blur = 0.5f;
[Range(0, 1)] public float lerp = 1; // 对比process前后区别
public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents; // copy具体哪个阶段的图像
}
public featureSetting setting = new featureSetting(); // 实例化
class CustomRenderPass : ScriptableRenderPass
{
public Material passMat = null;
public string passName;
public int passdownsample = 1;
public int passloop = 2;
public float passblur = 4;
public float lerp = 1;
string passTag; // 名字,方便从frameDebug里看到
// public RenderTargetIdentifier Scoure;
private RenderTargetIdentifier passSource { get; set; } // 源图像
public void setup(RenderTargetIdentifier sour) // 把相机的图像copy过来,具体哪张取决于renderEvent
{
this.passSource = sour;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// 类似shaderGUI里的传递,这里就不是setfloat了,就是拿shaderProperty的ID了,因为下面要用id创建临时图像
int TempID1 = Shader.PropertyToID("Temp1"); // 临时图像,也可以用 Handle,ID不乱就可以
int TempID2 = Shader.PropertyToID("Temp2");
int Lerp = Shader.PropertyToID("_Lerp");
int ScoureID = Shader.PropertyToID("_SourceTex"); // 为了方便对比prosses前后的区别
CommandBuffer cmd = CommandBufferPool.Get(passTag); // 类似于cbuffer,把整个渲染命令圈起来
RenderTextureDescriptor getCameraData = renderingData.cameraData.cameraTargetDescriptor; // 拿到相机数据,方便创建共享屬性的rt
int width = getCameraData.width / passdownsample; // downSample
int height = getCameraData.height / passdownsample;
// 获取临时渲染纹理 注意hdr的支持
cmd.GetTemporaryRT(TempID1, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.DefaultHDR); // 申请一个临时图像,并设置相机rt的参数进去
cmd.GetTemporaryRT(TempID2, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.DefaultHDR);
cmd.GetTemporaryRT(ScoureID, getCameraData);
RenderTargetIdentifier Temp1 = TempID1;
RenderTargetIdentifier Temp2 = TempID2;
RenderTargetIdentifier Scoure = ScoureID;
// Scoure = new RenderTargetIdentifier(ScoureID); // 感觉上下两种效果上区别不大
// 设置参数
cmd.SetGlobalFloat("_Blur", 1f);
cmd.SetGlobalFloat(Lerp, lerp);
// 拷贝
cmd.Blit(passSource, Scoure); // 把原始图像存起来做lerp操作,对比前后变化
cmd.ReleaseTemporaryRT(ScoureID); // 释放 RT
cmd.Blit(passSource, Temp1, passMat); //把源贴图输入到材质对应的pass里处理,并把处理结果的图像存储到临时图像;
for (int t = 1; t < passloop; t++) // 每次循环相当于把已经模糊的图片放进来进行模糊运算
{
cmd.SetGlobalFloat("_Blur", t * passblur); // 1.5
cmd.Blit(Temp1, Temp2, passMat);
var temRT = Temp1;
Temp1 = Temp2;
Temp2 = temRT;
}
// cmd.SetGlobalFloat("_Blur", passloop * passblur); // 这里相当于在loop外又模糊一次,还不如直接调 passloop 的参数
// cmd.Blit(Temp1, passSource, passMat);
cmd.Blit(Temp1, passSource);
// 释放
cmd.ReleaseTemporaryRT(TempID1); // 看起来好像如果不释放的话就会一直运行,会影响 passSource 的 Tex 图像
cmd.ReleaseTemporaryRT(TempID2);
context.ExecuteCommandBuffer(cmd); //执行命令缓冲区的该命令
CommandBufferPool.Release(cmd); //释放该命令
}
}
CustomRenderPass m_ScriptablePass;
public override void Create()
{
m_ScriptablePass = new CustomRenderPass();
m_ScriptablePass.renderPassEvent = setting.passEvent; // 渲染位置
m_ScriptablePass.passMat = setting.passMat; // 材质
m_ScriptablePass.passName = setting.passRenderName; // 渲染名称
m_ScriptablePass.passblur = setting.blur; // 变量
m_ScriptablePass.passloop = setting.loop;
m_ScriptablePass.passdownsample = setting.downsample;
m_ScriptablePass.lerp = setting.lerp;
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
m_ScriptablePass.setup(renderer.cameraColorTarget); // 通过setup函数设置不同的渲染阶段的渲染结果进 passSource 里面
renderer.EnqueuePass(m_ScriptablePass); // 执行
}
}
shader
Shader "WX/URP/Post/Kawaseblur"
{
Properties
{
_MainTex("MainTex",2D) = "white"{}
//_Blur("Blur",float)=2
// _Lerp("对比前后区别",range(0,1)) = 1
}
SubShader
{
Tags{"RenderPipeline" = "UniversalRenderPipeline"}
Cull Off ZWrite Off ZTest Always
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
float _Blur,_Lerp;
float4 _MainTex_TexelSize;
CBUFFER_END
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
TEXTURE2D(_SourceTex); // 原图
SAMPLER(sampler_SourceTex);
struct a2v
{
float4 positionOS:POSITION;
float2 texcoord:TEXCOORD;
};
struct v2f
{
float4 positionCS:SV_POSITION;
float2 texcoord:TEXCOORD;
};
v2f VERT(a2v i)//水平方向的采样
{
v2f o;
o.positionCS = TransformObjectToHClip(i.positionOS.xyz);
o.texcoord = i.texcoord;
return o;
}
half4 FRAG(v2f i) :SV_TARGET
{
float4 col;
half4 tex = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord);
// 其实感觉这里的意思是 UV + 方向 * 贴图大小的像素距离 * 偏移量
tex += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + float2(-1,-1) * _MainTex_TexelSize.xy * _Blur);
tex += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + float2(1,-1) * _MainTex_TexelSize.xy * _Blur);
tex += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + float2(-1,1) * _MainTex_TexelSize.xy * _Blur);
tex += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + float2(1,1) * _MainTex_TexelSize.xy * _Blur);
tex /= 5;
float3 Source = SAMPLE_TEXTURE2D(_SourceTex,sampler_SourceTex,i.texcoord).rgb;
tex.rgb = lerp(Source,tex.rgb,_Lerp);
// return half4(Source,1); // debug
return tex;
}
ENDHLSL
pass
{
HLSLPROGRAM
#pragma vertex VERT
#pragma fragment FRAG
ENDHLSL
}
}
}
- 径向模糊
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// 让编辑器在不运行状态下运行
[ExecuteInEditMode]
public class radialBlur : ScriptableRendererFeature
{
// 让这些设置可以显示在面板上
[System.Serializable]
public class setting
{
public string PassName = "径向模糊";
public Material RadialBlurMat = null;
[Range(0, 1)] public float x = 0.5f;
[Range(0, 1)] public float y = 0.5f;
[Range(1, 24)] public int loop = 5;
[Range(1, 16)] public float blur = 3;
[Range(1, 4)] public int downsample = 2;
[Range(0, 1)] public float instensity = 1;
public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
}
public setting mysetitng = new setting(); // 实例化
class CustomRenderPass : ScriptableRenderPass
{
public Material mymat;
public string name;
public float x;
public float y;
public int loop;
public float instensity;
public float blur;
public int downsample;
public RenderTargetIdentifier Source { get; set; }
//public RenderTargetIdentifier BlurTex;
public RenderTargetIdentifier Temp1;
public RenderTargetIdentifier Temp2;
int ssW;
int ssH;
public void setupmypass(RenderTargetIdentifier source)
{
this.Source = source;
}
// 执行
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// int BlurTexID = Shader.PropertyToID("_BlurTex"); // 模糊前的,shader 里没有这张图
int TempID1 = Shader.PropertyToID("Temp1"); // 空的临时图像
int TempID2 = Shader.PropertyToID("_SourceTex"); //
int loopID = Shader.PropertyToID("_Loop");
int Xid = Shader.PropertyToID("_X");
int Yid = Shader.PropertyToID("_Y");
int BlurID = Shader.PropertyToID("_Blur");
int instenID = Shader.PropertyToID("_Instensity"); // 其实就是pass2 lerp用的
RenderTextureDescriptor SSdesc = renderingData.cameraData.cameraTargetDescriptor; // 拿到相机数据
ssH = SSdesc.height / downsample;
ssW = SSdesc.width / downsample;
CommandBuffer cmd = CommandBufferPool.Get(name); // 通过对象池来调用,大量数据处理的时候特别有用
cmd.GetTemporaryRT(TempID1, ssW, ssH, 0, FilterMode.Bilinear, RenderTextureFormat.RGB111110Float);//用来存降采样的
//cmd.GetTemporaryRT(BlurTexID, SSdesc); // 模糊图前的 ,pass2 要用
cmd.GetTemporaryRT(TempID2, SSdesc); // 这边不降采样的图就直接拿相机的渲染设置了
// 实例化
Temp1 = new RenderTargetIdentifier(TempID1);
Temp2 = new RenderTargetIdentifier(TempID2);
cmd.SetGlobalFloat(loopID, loop);
cmd.SetGlobalFloat(Xid, x);
cmd.SetGlobalFloat(Yid, y);
cmd.SetGlobalFloat(BlurID, blur);
cmd.SetGlobalFloat(instenID, instensity);
// 一个Blit就是FrameDebug里一次结果
cmd.Blit(Source, Temp1); // 存储降采样的源图,用于pass0计算模糊图
cmd.Blit(Source, Temp2); // 存储源图,用来lerp前后效果的(正常不应该这么用,在shader的for循环里做手脚)
cmd.Blit(Temp1, Source, mymat, 0);//pass0的模糊计算
// 清理
//cmd.ReleaseTemporaryRT(BlurTexID);
cmd.ReleaseTemporaryRT(TempID1);
cmd.ReleaseTemporaryRT(TempID2);
context.ExecuteCommandBuffer(cmd); // 调用
CommandBufferPool.Release(cmd); // 清理
}
}
CustomRenderPass m_ScriptablePass;
public override void Create()
{
m_ScriptablePass = new CustomRenderPass();
m_ScriptablePass.renderPassEvent = mysetitng.passEvent;
m_ScriptablePass.blur = mysetitng.blur;
m_ScriptablePass.x = mysetitng.x;
m_ScriptablePass.y = mysetitng.y;
m_ScriptablePass.instensity = mysetitng.instensity;
m_ScriptablePass.loop = mysetitng.loop;
m_ScriptablePass.mymat = mysetitng.RadialBlurMat;
m_ScriptablePass.name = mysetitng.PassName;
m_ScriptablePass.downsample = mysetitng.downsample;
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (mysetitng.RadialBlurMat != null)
{
m_ScriptablePass.setupmypass(renderer.cameraColorTarget);
// 把括号里的pass根据渲染设置(是有Create设置的)丢到渲染流程中
renderer.EnqueuePass(m_ScriptablePass);
}
else
{
Debug.LogError("径向模糊材质球丢失!");
}
}
}
shader
Shader"WX/URP/Post/radialBrul"
{
Properties
{
_MainTex("tex",2D) = "white"{}
}
SubShader
{
Tags{"RenderPipeline" = "UniversalRenderPipeline"}
Cull Off ZWrite Off ZTest Always
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
float4 _MainTex_ST;
float _Loop;
float _Blur;
float _Y;
float _X;
float _Instensity;
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
TEXTURE2D(_SourceTex); // 原图
SAMPLER(sampler_SourceTex);
struct a2v
{
float4 positionOS:POSITION;
float2 texcoord:TEXCOORD;
};
struct v2f
{
float4 positionCS:SV_POSITION;
float2 texcoord:TEXCOORD;
};
ENDHLSL
pass
{
HLSLPROGRAM
#pragma vertex VERT
#pragma fragment FRAG
v2f VERT(a2v i)
{
v2f o;
o.positionCS = TransformObjectToHClip(i.positionOS.xyz);
o.texcoord = i.texcoord;
return o;
}
half4 FRAG(v2f i) :SV_TARGET
{
float4 col = 0;
float2 dir = (i.texcoord - float2(_X,_Y)) * _Blur * 0.01;
for (int t = 0; t < _Loop; t++)
{
col += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + dir * t) / _Loop;
}
float4 Source = SAMPLE_TEXTURE2D(_SourceTex,sampler_SourceTex,i.texcoord);//得到屏幕原始图
return lerp(Source,col,_Instensity);
}
ENDHLSL
}
}
}
- real模板
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
[ExecuteInEditMode] // 让编辑器在不运行状态下运行
public class FullScreen : ScriptableRendererFeature
{
[System.Serializable] // renderfeature 的面板
public class featureSetting
{
public string passRenderName = "渲染pass的名字";
public Material passMat;
public Color MainColor;
public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
}
public featureSetting setting = new featureSetting(); // 实例化
class CustomRenderPass : ScriptableRenderPass
{
public Material passMat = null;
public string passName;
public Color MainColor;
string passTag; // 给cmd对象池用来调用的
private RenderTargetIdentifier passSource { get; set; } //源图像,这里一般是cameraColorTex
public void setup(RenderTargetIdentifier sour) // 把相机的图像copy过来
{
this.passSource = sour;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
int TempID1 = Shader.PropertyToID("_MaskTex");
int MColor = Shader.PropertyToID("_MainColor");
CommandBuffer cmd = CommandBufferPool.Get(passTag); // 类似于cbuffer,把整个渲染命令圈起来
RenderTextureDescriptor getCameraData = renderingData.cameraData.cameraTargetDescriptor; // 拿到相机数据,方便创建共享屬性的rt
cmd.GetTemporaryRT(TempID1, getCameraData); //申请一个临时图像,并设置相机rt的参数进去
RenderTargetIdentifier Temp1 = TempID1;
// 设置参数
cmd.SetGlobalColor(MColor, MainColor);
// 拷贝
cmd.Blit(passSource, Temp1, passMat, -1); //把源贴图输入到材质对应的pass里处理,并把处理结果的图像存储到临时图像;
cmd.Blit(Temp1, passSource); //然后把临时图像又存到源图像里
cmd.ReleaseTemporaryRT(TempID1);
context.ExecuteCommandBuffer(cmd); //执行命令缓冲区的该命令
CommandBufferPool.Release(cmd); //释放该命令
}
}
CustomRenderPass m_ScriptablePass;
public override void Create()
{
m_ScriptablePass = new CustomRenderPass();
m_ScriptablePass.renderPassEvent = setting.passEvent; // 渲染位置
m_ScriptablePass.passMat = setting.passMat; // 材质
m_ScriptablePass.passName = setting.passRenderName; // 渲染名称
m_ScriptablePass.MainColor = setting.MainColor; // 变量
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
m_ScriptablePass.setup(renderer.cameraColorTarget); // 通过setup函数设置不同的渲染阶段的渲染结果进 passSource 里面
renderer.EnqueuePass(m_ScriptablePass); // 执行
}
}
shader
Shader "Neilyodog/URPUnlitShaderBasic"
{
Properties
{
_MainTex("主贴图",2D) = "white"{}
// _MainColor("主颜色",color) = (1,1,1,1)
_MaskTex("mask",2D) = "white"{}
}
SubShader
{
Tags {"Queue"="Geometry" "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
float2 uv : TEXCOORD0;
float fogCoord : TEXCOORD1;
};
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
float4 _MainColor;
CBUFFER_END
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
TEXTURE2D(_MaskTex);
SAMPLER(sampler_MaskTex);
Varyings vert(Attributes v)
{
Varyings o;
o.positionHCS = TransformObjectToHClip(v.positionOS.xyz);
o.uv = TRANSFORM_TEX(v.uv,_MainTex);
o.fogCoord = ComputeFogFactor(o.positionHCS.z);
return o;
}
half4 frag(Varyings i) : SV_Target
{
float2 screenUV = i.positionHCS.xy / _ScreenParams.xy;
half4 c = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.uv);
half mask = SAMPLE_TEXTURE2D(_MaskTex,sampler_MaskTex,screenUV).r;
//return half4(mask.xxx,1);
c *= _MainColor * mask;
c.rgb = MixFog(c.rgb,i.fogCoord);
return c;
}
ENDHLSL
}
}
}
源文件
- 径向模糊
- HSV
- Kawase
分析

开头贴标签
第一部分
在继承 ScriptableRendererFeature 的类里面
设置renderer面板
第二部分
在继承 ScriptableRenderPass 的类里面
-
声明变量
-
从相机上copy图像
-
#核心#重写 Execute 函数,来控制如何渲染(记得清理缓存)
-
给renderer面板传递参数
-
添加pass
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// 让编辑器在不运行状态下运行
[ExecuteInEditMode]
public class radialBlur : ScriptableRendererFeature
{
// 让这些设置可以显示在面板上
[System.Serializable]
public class setting
{
public string PassName = "径向模糊";
public Material RadialBlurMat = null;
[Range(0, 1)] public float x = 0.5f;
[Range(0, 1)] public float y = 0.5f;
[Range(1, 24)] public int loop = 5;
[Range(1, 16)] public float blur = 3;
[Range(1, 4)] public int downsample = 2;
[Range(0, 1)] public float instensity = 0.5f;
public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
}
public setting mysetitng = new setting(); // 实例化
class CustomRenderPass : ScriptableRenderPass
{
public Material mymat;
public string name;
public float x;
public float y;
public int loop;
public float instensity;
public float blur;
public int downsample;
public RenderTargetIdentifier Source { get; set; }
//public RenderTargetIdentifier BlurTex;
public RenderTargetIdentifier Temp1;
public RenderTargetIdentifier Temp2;
int ssW;
int ssH;
public void setupmypass(RenderTargetIdentifier source)
{
this.Source = source;
}
// 执行
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// int BlurTexID = Shader.PropertyToID("_BlurTex"); // 模糊前的,shader 里没有这张图
int TempID1 = Shader.PropertyToID("Temp1");
int TempID2 = Shader.PropertyToID("_SourceTex"); //
int loopID = Shader.PropertyToID("_Loop");
int Xid = Shader.PropertyToID("_X");
int Yid = Shader.PropertyToID("_Y");
int BlurID = Shader.PropertyToID("_Blur");
int instenID = Shader.PropertyToID("_Instensity"); // 其实就是pass2 lerp用的
RenderTextureDescriptor SSdesc = renderingData.cameraData.cameraTargetDescriptor; // 拿到相机数据
ssH = SSdesc.height / downsample;
ssW = SSdesc.width / downsample;
CommandBuffer cmd = CommandBufferPool.Get(name); // 通过对象池来调用,大量数据处理的时候特别有用
cmd.GetTemporaryRT(TempID1, ssW, ssH, 0, FilterMode.Bilinear, RenderTextureFormat.RGB111110Float);//用来存降采样的
//cmd.GetTemporaryRT(BlurTexID, SSdesc); // 模糊图前的 ,pass2 要用
cmd.GetTemporaryRT(TempID2, SSdesc); // 这边不降采样的图就直接拿相机的渲染设置了
// 实例化
Temp1 = new RenderTargetIdentifier(TempID1);
Temp2 = new RenderTargetIdentifier(TempID2);
cmd.SetGlobalFloat(loopID, loop);
cmd.SetGlobalFloat(Xid, x);
cmd.SetGlobalFloat(Yid, y);
cmd.SetGlobalFloat(BlurID, blur);
cmd.SetGlobalFloat(instenID, instensity);
// 一个Blit就是FrameDebug里一次结果
cmd.Blit(Source, Temp1); // 存储降采样的源图,用于pass0计算模糊图
cmd.Blit(Source, Temp2); // 存储源图,用来lerp前后效果的(正常不应该这么用,在shader的for循环里做手脚)
cmd.Blit(Temp1, Source, mymat, 0);//pass0的模糊计算
// 清理
//cmd.ReleaseTemporaryRT(BlurTexID);
cmd.ReleaseTemporaryRT(TempID1);
cmd.ReleaseTemporaryRT(TempID2);
context.ExecuteCommandBuffer(cmd); // 调用
CommandBufferPool.Release(cmd); // 清理
}
}
CustomRenderPass m_ScriptablePass;
public override void Create()
{
m_ScriptablePass = new CustomRenderPass();
m_ScriptablePass.renderPassEvent = mysetitng.passEvent;
m_ScriptablePass.blur = mysetitng.blur;
m_ScriptablePass.x = mysetitng.x;
m_ScriptablePass.y = mysetitng.y;
m_ScriptablePass.instensity = mysetitng.instensity;
m_ScriptablePass.loop = mysetitng.loop;
m_ScriptablePass.mymat = mysetitng.RadialBlurMat;
m_ScriptablePass.name = mysetitng.PassName;
m_ScriptablePass.downsample = mysetitng.downsample;
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (mysetitng.RadialBlurMat != null)
{
m_ScriptablePass.setupmypass(renderer.cameraColorTarget);
// 把括号里的pass根据渲染设置(是有Create设置的)丢到渲染流程中
renderer.EnqueuePass(m_ScriptablePass);
}
else
{
Debug.LogError("径向模糊材质球丢失!");
}
}
}
shader
Shader"WX/URP/Post/radialBrul"
{
Properties
{
_MainTex("tex",2D) = "Wwhite"{}
}
SubShader
{
Tags{"RenderPipeline" = "UniversalRenderPipeline"}
Cull Off ZWrite Off ZTest Always
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
float4 _MainTex_ST;
float _Loop;
float _Blur;
float _Y;
float _X;
float _Instensity;
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
TEXTURE2D(_SourceTex); // 原图
SAMPLER(sampler_SourceTex);
struct a2v
{
float4 positionOS:POSITION;
float2 texcoord:TEXCOORD;
};
struct v2f
{
float4 positionCS:SV_POSITION;
float2 texcoord:TEXCOORD;
};
ENDHLSL
pass
{
HLSLPROGRAM
#pragma vertex VERT
#pragma fragment FRAG
v2f VERT(a2v i)
{
v2f o;
o.positionCS = TransformObjectToHClip(i.positionOS.xyz);
o.texcoord = i.texcoord;
return o;
}
half4 FRAG(v2f i) :SV_TARGET
{
float4 col = 0;
float2 dir = (i.texcoord - float2(_X,_Y)) * _Blur * 0.01;
for (int t = 0; t < _Loop; t++)
{
col += SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord + dir * t) / _Loop;
}
float4 Source = SAMPLE_TEXTURE2D(_SourceTex,sampler_SourceTex,i.texcoord);//得到屏幕原始图
return lerp(Source,col,_Instensity);
}
ENDHLSL
}
}
}
一些坑
- public面板
如果采用新建一个 setting 类的方式去创建面板的话,大概流程是:
-
class setting
-
public setting
-
ScriptableRenderPass public 一样的 setting
-
Execute 中 SetGlobalFloat
-
Create 中把外部面板中的数据拿到 ScriptableRenderPass 中去
同时,shader中注意 Properties 中不用新建数据
- 对于Blit 函数
cmd.Blit(a,b,mat,-1); 等同于
mat.SetTexture("_a",a);
cmd.Blit(null,b,mat,-1);
cmd.Blit和Blit等效果

- cameraColorTarget用 var格式
(当时还不懂var是啥意思)

- CommandBufferPool.Release(cmd) & cmd.clear()
CommandBufferPool.Release(cmd) : 把资源释放掉了,cmd就没了
cmd.clear() : 只是把这个buffer里面的命令清空
- 原神fullScreen三角形

- 对于 GetTemporaryRT 函数
相当于new RT,然后给RenderTargetIdentifier
添加“获取临时渲染纹理”命令。(一般是获取相机的RT数据)
开启HDR后需要把 Format 改成 DefaultHDR
跟着相机走的话可以这样写

这样写跟一个一个数据去getCameraData的最终呈现效果不一样
-
- 另外感觉一些数值,例如depth buffer如果不需要的话应该可以存更低的精度
-
对于 ReleaseTemporaryRT 函数
必须释放,不然会内存泄漏,只是释放的时机不同
旭辉老师说是如果cmd.GetTemporaryRT出来的话,释放cmd这个应该就会释放,但遇到过如果不释放就黑屏的问题
- CommandBufferPool.Get("name")
相当于给这个cmd起名,方便后面查找
- RenderTargetIdentifier & RenderTargetHandle 的区别
Handle是自己定义的

- 所有涉及到bilt计算的RT都需要给一个临时rt
因为最终material面板上这个tex的位置应该是一张rt
- 如果该pass没有贴图,blit会默认给MainTex
blit(RTID,cameraSource) 会自动把RTID给MainTex
- 给贴图赋值
只有两种Set可以,bilt并不行
- Filter
- cmd.GetTemporal & RenderTexture.GetTemporal
陈参说cmd只有一帧,返回的是handle(类似指针);而renderTexture是持续的。
- 对于 context.ExecuteCommandBuffer(cmd)
看官方自带的 RenderObjectsPass.cs 中,执行cmd的方法执行了两次
这是因为官方怕之前有其他的cmd啥的,先执行一次,实际并不影响
- Configure 和 Execute
Configure 是在 Execute 调用的前一帧调用
其实这俩一样,就是在Configure里官方会给你优化,两边用的api不太一样
- 对于Depth在后处理后,game和scene深度差距
copyDepth.hlsl 里有详细说明,对于GL之外的平台需要反转投影矩阵的y
CopyDepthPass.cs 里面是这样做的 IsCameraProjectionMatrixFlipped 函数用于判断uv的起点是否为左上


官方后处理笔记
2019.4.30
这个就是GUI,没细看