[toc]

# 综述

常常听说 URP 里最多只能执行一个 Pass,但这其实是不对的,为啥呢?
就是因为 RenderFeature,URP 把实际上 Pass 执行的顺序抽象到了 CommandList 中了,即可以手动的在 C# 端控制 Pass 的执行顺序,而不需要硬写在代码里了。
并且一个 RenderFeature 里可以执行哪些 Pass 也都是自己决定的
接下来以毛玻璃后处理效果来记录 RenderFeature 用法

# RenderPass

URP14.0 后必须在 Pass 的 CameraSetup 阶段中获取 cameraColorTargetHandle , 禁止在 Feature 中获取

而且必须用 RTHandle 代替 RTTargetHandle

1
2
3
var renderer = renderingData.cameraData.renderer;
//获取屏幕的RT和材质,传递到Pass中
RenderTargetIdentifier currentRT = renderer.cameraColorTargetHandle;
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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class SFXDistortedPass : ScriptableRenderPass
{
//接收Feature脚本传入的数据
private RenderTargetIdentifier sourceRT;
private Material material;

private static string m_RTName = "SFXDistorted";

private static int rtTagId = Shader.PropertyToID(m_RTName);
//临时RT
private RTHandle tempRT = RTHandles.Alloc(rtTagId,name : m_RTName);

public void Setup(Material mat)
{
this.material = mat;
}

public override void Execute(ScriptableRenderContext context, ref RenderingData data)
{
CommandBuffer cmd = CommandBufferPool.Get("SFXDistorted22");

//向cmd中写入所有的渲染命令
Render(cmd, ref data);
//执行命令
context.ExecuteCommandBuffer(cmd);
//执行完毕后释放该命令
CommandBufferPool.Release(cmd);
}

void Render(CommandBuffer cmd, ref RenderingData data)
{
//创建一个临时的RenderTexture,参数和相机的一致
RenderTextureDescriptor opaqueDesc = data.cameraData.cameraTargetDescriptor;
opaqueDesc.depthBufferBits = 0;
cmd.GetTemporaryRT(rtTagId,opaqueDesc);

//将源RY通过Material写入到tempRT
cmd.Blit(sourceRT,rtTagId,material);

//再从tempRT写回到sourceRT
cmd.Blit(rtTagId,sourceRT);
}

public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
base.OnCameraSetup(cmd,ref renderingData);
var renderer = renderingData.cameraData.renderer;
//获取屏幕的RT和材质,传递到Pass中
RenderTargetIdentifier currentRT = renderer.cameraColorTargetHandle;
sourceRT = currentRT;
}

public override void OnCameraCleanup(CommandBuffer cmd)
{
base.OnCameraCleanup(cmd);
tempRT.Release();
}
}

# RenderFeature

实际的 Pass 执行都放到 context 的 EnqueuePass 中去了

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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class SFXDistortedFeature : ScriptableRendererFeature
{
[Serializable]
public class DistortedSetting
{
public RenderPassEvent Event = RenderPassEvent.AfterRenderingOpaques;
public Shader shader;
[Tooltip("扭曲强度")]
public float distortedInt;
[Tooltip("扭曲图")]
public Texture2D distortedTex;
}

public DistortedSetting _settings = new DistortedSetting();
private Material matInstance;
private SFXDistortedPass pass;
private static readonly int distortedInt = Shader.PropertyToID("_distortedInt");
private static readonly int distortedTex = Shader.PropertyToID("_distortedTex");

public override void Create()
{
pass = new SFXDistortedPass();
pass.renderPassEvent = _settings.Event;
}

public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData data)
{
//检查Shader是否为空,为空则创建一个默认的
if (_settings.shader == null) return;
if (matInstance == null)
{
matInstance = CoreUtils.CreateEngineMaterial(_settings.shader);
}
matInstance.SetTexture(distortedTex,_settings.distortedTex);
matInstance.SetFloat(distortedInt,_settings.distortedInt);
pass.Setup(matInstance);
//Pass进入队列
renderer.EnqueuePass(pass);
}
}

# 设置

# Shader

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
Shader "URP/distortedPM"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BaseColor("Test Color",Color) = (1,1,1,1)
}
SubShader
{
Pass
{
Cull Off
ZTest Always
ZWrite Off
HLSLPROGRAM

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"

#pragma vertex Vertex
#pragma fragment fragment

Texture2D _MainTex,_distortedTex;
SamplerState sampler_MainText,sampler_distortedTex;
TEXTURE2D(_CameraColorTexture);
SAMPLER(sampler_CameraColorTexture);
half4 _BaseColor;
half _distortedInt;

struct a2v
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f{
float4 pos: SV_POSITION;
float2 uv: TEXCOORD0;
};

v2f Vertex(a2v v)
{
v2f o;
o.pos = TransformObjectToHClip(v.vertex);
o.uv = v.uv;
return o;
}

half4 fragment(v2f i): SV_TARGET{
half distortedTex = _distortedTex.Sample(sampler_distortedTex, i.uv);
//half4 col = _MainTex.Sample(sampler_MainTex, lerp(i.uv,distortedTex,_distortedInt));
float2 uv = distortedTex*_distortedInt + i.uv;
return SAMPLE_TEXTURE2D(_CameraColorTexture, sampler_CameraColorTexture, uv);
}

ENDHLSL
}
}
}