也可以看知乎 https://zhuanlan.zhihu.com/p/425214124
坑
https://www.sardinefish.com/blog/458
URP Post Processing
https://mp.weixin.qq.com/s/JSsAWv3s-jBJHZb5JV5tyw
后处理相关学习 http://blog.coolcoding.cn/?p=3472
outline https://alexanderameye.github.io/outlineshader
https://samdriver.xyz/article/scriptable-render
官方后处理Demo https://github.com/Unity-Technologies/UniversalRenderingExamples
一堆 https://github.com/YuSiangLai/URPCustomPostProcessing
outline https://github.com/yahiaetman/URPCustomPostProcessingStack
遮挡显示及描边效果 https://zhuanlan.zhihu.com/p/170241589
某大佬网站,当前页面是renderfeature(基础) https://samdriver.xyz/article/scriptable-render
深度描边 https://alexanderameye.github.io/notes/edge-detection-outlines/
黑白闪 https://www.bilibili.com/video/BV1Cr4y127Jz?spm_id_from=333.999.0.0
commandbuffer.Drawxxxxx
【百人斩】凶恶的真实(745806106) 20:51:51
如果是用了Scriptable render pass的话
【百人斩】凶恶的真实(745806106) 20:52:08
它里面有一个RenderTargetIdentifier[] m_ColorAttachments
ConfigureTarget用来暂存pass里用到的RenderTargetIdentifier
创建Render Feature
Create-Rendering-Universal Render Pipeline-Render Feature
模板
- 学习用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52using 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
[// 让编辑器在不运行状态下运行 ]
public class mybiltxf : ScriptableRendererFeature
{
[// renderfeature 的面板 ]
public class mysetting//定义一个设置的类
{
public string PassName = "调色";
public Material mymat;
[public float Brightness = 1; ]
[public float Saturate = 1; ]
[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
1 | Shader "WX/URP/Post/post" |
- Kawase
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103using 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
1 | Shader "WX/URP/Post/Kawaseblur" |
- 径向模糊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// 让编辑器在不运行状态下运行
[ ]
public class radialBlur : ScriptableRendererFeature
{
// 让这些设置可以显示在面板上
[ ]
public class setting
{
public string PassName = "径向模糊";
public Material RadialBlurMat = null;
[public float x = 0.5f; ]
[public float y = 0.5f; ]
[public int loop = 5; ]
[public float blur = 3; ]
[public int downsample = 2; ]
[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
1 | Shader"WX/URP/Post/radialBrul" |
- real模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
[// 让编辑器在不运行状态下运行 ]
public class FullScreen : ScriptableRendererFeature
{
[// 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
1 | Shader "Neilyodog/URPUnlitShaderBasic" |
源文件
- 径向模糊
radialBlur.cs
- HSV
HSV.cs
- Kawase
Kawase.cs
分析
开头贴标签
第一部分
在继承 ScriptableRendererFeature 的类里面
设置renderer面板
第二部分
在继承 ScriptableRenderPass 的类里面
- 声明变量
- 从相机上copy图像
- #核心#重写 Execute 函数,来控制如何渲染(记得清理缓存)
- 给renderer面板传递参数
- 添加passshader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// 让编辑器在不运行状态下运行
[ ]
public class radialBlur : ScriptableRendererFeature
{
// 让这些设置可以显示在面板上
[ ]
public class setting
{
public string PassName = "径向模糊";
public Material RadialBlurMat = null;
[public float x = 0.5f; ]
[public float y = 0.5f; ]
[public int loop = 5; ]
[public float blur = 3; ]
[public int downsample = 2; ]
[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("径向模糊材质球丢失!");
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67Shader"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的起点是否为左上
没搞懂的
- 官方自带的会算cameraMatrix
官方的 RenderObjectsPass.cs 会在Execute部分算一遍相机的MVP矩阵,没明白为啥,感觉是应对相机偏移矩阵发生变化的情况
主要变化一个是 ProjectionMatrix 的 z 值也就是深度范围 [0,1] or [-1,1]
另一个就是 ViewMatrix 第四列的平移操作
- enableRandomWrite
没懂为啥需要随机写入
官方后处理笔记
2019.4.30
这个就是GUI,没细看