通用的Feature

4.2k words

来源

实战项目观察–URP应用与优化记录 - limb的文章 - 知乎
Learn to Write Unity Compute Shaders

思路

通用的RenderFeature,可用挂接简单独立的Pass,实现一个RenderFeature,大家可以往这个RenderFeature来注册各种Event下的Command,而后由这个RenderFeature提交给管线去渲染;各种比较简单和独立的Pass都可以往这个地方挂,当时看到之后感觉还蛮方便的,值得借鉴。

应用

BlurHighLight
RingHighLight

代码

Code
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

public class PostProcessPass : ScriptableRenderPass
{
private static readonly int ID_Blit_Temp1 = Shader.PropertyToID("_PostProcessing_Blit_Temp1");
private static readonly int ID_Blit_Temp2 = Shader.PropertyToID("_PostProcessing_Blit_Temp2");
private static readonly RenderTargetIdentifier m_BlitTemp1 = new RenderTargetIdentifier(ID_Blit_Temp1);
private static readonly RenderTargetIdentifier m_BlitTemp2 = new RenderTargetIdentifier(ID_Blit_Temp2);
private List<IPostProcessBehaviour> m_Effects;

public PostProcessPass Setup(List<IPostProcessBehaviour> effects)
{
m_Effects = effects;
m_Effects.Sort((a, b) => a.Event - b.Event);
return this;
}

public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
base.Configure(cmd, cameraTextureDescriptor);
foreach(var effect in m_Effects)
{
effect.Configure(cmd, cameraTextureDescriptor);
}
ConfigureTarget(colorAttachmentHandle, depthAttachmentHandle);
}

public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get("Component Based Post Process");
var descriptor = renderingData.cameraData.cameraTargetDescriptor;
descriptor.msaaSamples = 1;
cmd.GetTemporaryRT(ID_Blit_Temp1, descriptor);
cmd.GetTemporaryRT(ID_Blit_Temp2, descriptor);

var renderer = renderingData.cameraData.renderer;
var lastIndex = m_Effects.Count - 1;
var blitIndex = 0;
var blitSwap = true;

foreach(var effect in m_Effects)
{
var src = blitSwap ? m_BlitTemp1 : m_BlitTemp2;
var dst = blitSwap ? m_BlitTemp2 : m_BlitTemp1;
if (blitIndex == 0)
{
src = renderer.cameraColorTargetHandle;
}
else if (blitIndex == lastIndex)
{
dst = renderer.cameraColorTargetHandle;
}
blitSwap = !blitSwap;

var name = effect.m_Name;
cmd.BeginSample(name);
effect.Execute(cmd, src, dst, descriptor, context, ref renderingData);
cmd.EndSample(name);

blitIndex++;
}
if (lastIndex == 0)
{
cmd.Blit(m_BlitTemp2, renderer.cameraColorTargetHandle);
}

cmd.ReleaseTemporaryRT(ID_Blit_Temp1);
cmd.ReleaseTemporaryRT(ID_Blit_Temp2);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}

public override void FrameCleanup(CommandBuffer cmd)
{
base.FrameCleanup(cmd);
foreach(var effect in m_Effects)
{
effect.FrameCleanUp(cmd);
}
}

}

public class PostProcessFeature : AScriptableRendererFeature
{
public RenderResources resources;
private PostProcessPass m_ScreenPostProcess;
private readonly List<IPostProcessBehaviour> m_PostprocessQueue = new List<IPostProcessBehaviour>();
private readonly List<IPostProcessBehaviour> m_ScreenProcessing = new List<IPostProcessBehaviour>();

public override void Create()
{
m_ScreenPostProcess = new PostProcessPass()
{
renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing + 1
};
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
m_ScreenPostProcess.Dispose();
}

protected override void EnqueuePass(ScriptableRenderer renderer, ref RenderingData renderingData)
{
m_PostprocessQueue.Clear();
if (renderingData is { postProcessingEnabled: true, cameraData: { postProcessEnabled: true } })
{
m_PostprocessQueue.AddRange(renderingData.cameraData.camera.GetComponentsInChildren<IPostProcessBehaviour>());

if (m_PostprocessQueue.Count <= 0)
{
return;
}

m_ScreenProcessing.Clear();

var postProcessCount = m_PostprocessQueue.Count;
for(int k = 0; k < postProcessCount; k++)
{
var postProcess = m_PostprocessQueue[k];
postProcess.ValidateParameters();
if (!postProcess.m_Enable)
{
continue;
}
if (!postProcess.m_OpaqueProcess)
{
m_ScreenProcessing.Add(postProcess);
}
}

if (m_ScreenProcessing.Count > 0)
{
renderer.EnqueuePass(m_ScreenPostProcess.Setup(m_ScreenProcessing));
}
}
}
}