Unity : 2019.4.30f1
URP : 7.7.1
吐个槽,UniversalRP 没有官方提供实现自定义效果的 URP Integrated 的 PP 接口,总之有点不靠谱啊
不过后来我瞄了一下 URP 部分提供的源码,发现不提供URP的PP扩展实例有几个原因:
该类是负责管理 Volume
的 MonoBehavior
组件中的 Add Overrides
中的 Volume
列表中的选项基类,但是需要 Attribute
来标记,这样 unity 黑箱会遍历添加到列表项中
该类是负责管理 Volume
的 MonoBehavior
组件中的 Add Overrides
中的 Volume
列表
开类所在路径:
下,其中 x.x.x 就是 URP 的版本号
查看器源代码,可以发现,该类是单例模式,并且在构造函数就加载了 baseComponentTypes
,这 baseComponentTypes
就是扩展与 VolumeComponent
的所有类型
你可以发现,使用了一个 API:
// Grab all the component types we can find
baseComponentTypes = CoreUtils.GetAllTypesDerivedFrom<VolumeComponent>()
.Where(t => !t.IsAbstract);
其中 CoreUtils.GetAllTypesDerivedFrom
是 cpp inject 到 csharp 层的接口,这里就不多说明了,作用就是获取当前程序集当中的所有继承自 VolumeComponent
的类,注意还使用了 Linq
: Where(t => !t.IsAbstract);
非 abstract
的
然后在创建 VolumeComponent
的 Stack CreateStack
,如下:
就是调用 VolumeStack
中的 Reload
函数,如下,就是遍历类型,逐个 CreateInstance
是 URP Renderer 中添加额外 Renderer Feature 功能的基类
实现该类后,可以在:URP Renderer 中的 Add RendererFeature
中看到此选项
在 URP 中实现的 PostProcess 中,你可以参考 URP 中的源码是如何实现的,源码文件所在位置:
下,其中 x.x.x 就是 URP 的版本号
这么多的 Pass,你可以临摹其中的写法
要扩展 URP 的 PP,那么可以总结下面几步即可(留意之前说的我的环境是,unity 2019.4.30f1, URP 7.7.1 的版本,如果以后在其他版本实现扩展,发现 API 过时、不存在、或是没效果,那么要确保是否环境一致):
VolumeComponent
扩展,为了在 Volume
的 MonoBehavior
组件中的 Add Overrides
可以有对应选项,且可以设置 VolumeComponent
相关的属性,在渲染时,可以传递给 ScriptableRendererFeature
, ScriptableRenderPass
ScriptableRendererFeature
扩展,是对应 URP Pipeline Renderer 的一个渲染 Feature 的扩展,我们可以自定义一些类,继承自 ScriptableRendererFeature
,然后在该 Feature 中调用后续我括扩展的 ScriptableRenderPass
Create
方法,在此方法中 new XXXRenderPass
这 XXXRenderPass
就是你自己扩展的自定义 pass,然后设置 Pass 的 renderPassEvent
AddRenderPasses
方法,在此方法中,给 上面你创建出来的 XXXRenderPass
设置渲染前需要的数据,如:指定 Material.shader
、设置Material.SetXXX
、最后一定要调用 renderer.EnqueuePass(pass);
,这样该 Pass的 Execute
方法才会被执行到ScriptableRenderPass
扩展,这里就是我们的自定义的 PP 执行的绘制 API 调用的核心封装的地方
RenderingData.cameraData.cameraTargetDescriptor
作为 类似 Built-in RP 中 void OnRenderImage(RenderTexture src, RenderTexture dest)
中的 RenderTexture src
Blit
来绘制到另一个 TempRT
(这个RT你自己的 pass 维护,可复用的)TempRT
再复制回 RenderingData.cameraData.cameraTargetDescriptor
,这样就可以给下一个 Pass 后续接着处理了下面是的所有源代码,注意 我在代码中写下的 注释
矩形马赛克的后效,其实我之前在 Built-in RP 中有实现过一般,可以参考文章:Unity Shader PostProcessing - 5 - PixelSyle 像素化风格
其中的 MinIntParamter
类型的参数,其实可以有很多中,具体有多少中,你可以搜索一下,可以参考下面的 查看有多少种现成的 VolueComponent 的参数类型
// jave.lin 2021/10/21
// Box Mosaic Volume Component
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// VolumeComponentMenu 的 Attribute 会自动被 unity 阴影分析到 Volume 的 VolumeComponent 的菜单列表项中
[Serializable, VolumeComponentMenu("Custom-Post-processing/BoxMosaic")]
public class BoxMosaicVolumeComponent : VolumeComponent, IPostProcessComponent
{
public static readonly int _PixelSize = Shader.PropertyToID("_PixelSize");
[Header("马赛克大小")]
[Tooltip("马赛克大小")]
public MinIntParameter pixelSize = new MinIntParameter(1, 1, false);
public bool IsActive() => pixelSize.value > 1;
public bool IsTileCompatible() => false;
// 将 volume 中的属性设置到对应的 mat 中,可以封装到基类处理
public void SetToMat(Material mat)
{
Debug.Log($"{nameof(BoxMosaicVolumeComponent)}.SetToMat");
mat.SetFloat(_PixelSize, (float)pixelSize.value);
}
}
// jave.lin 2021/10/25
// 用于扩展 URP PP 的自定义 RenderPass
// 我们定位 ScriptableRenderPass.Execute 方法的作用类似于 Built-in RP 中的
// MonoBehavior 的 void OnRenderImage(RenderTexture src, RenderTexture dest)
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class ExtendURPCustomPPRenderPass : ScriptableRenderPass
{
// command buffer 名字
private const string CMD_BUF_NAME = "ExtendURPCustomPPRenderPass";
// 使用的材质
public Material mat;
// volume component
public BoxMosaicVolumeComponent volumeComp;
// src render target
public RenderTargetIdentifier srcTarget;
// 用于可重复使用的 render target handle, RenderTargetHandle.identity() 是对应的 RenderTargetIdentity
private RenderTargetHandle tempTargetHandle;
public ExtendURPCustomPPRenderPass()
{
Debug.Log($"{nameof(ExtendURPCustomPPRenderPass)}.ctor");
// jave.lin : 相当于 this.tempTargetHandle.id = Shader.PropertyToID("_TempRT")
this.tempTargetHandle.Init("_TempRT");
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
Debug.Log($"{nameof(ExtendURPCustomPPRenderPass)}.Execute");
// 没材质,不处理
if (mat == null) return;
// 如果是在 Scene 视图下的相机,那么不处理
if (renderingData.cameraData.isSceneViewCamera) return;
// VolumeManager 单例管理器中拿到 VolumeStack 对象
var stack = VolumeManager.instance.stack;
volumeComp = stack.GetComponent<BoxMosaicVolumeComponent>();
if (volumeComp == null) return;
if (!volumeComp.IsActive()) return;
// Render
var cmd = CommandBufferPool.Get(CMD_BUF_NAME);
Render(cmd, ref renderingData);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
public void Setup(RenderTargetIdentifier srcTarget, Material mat)
{
Debug.Log($"{nameof(ExtendURPCustomPPRenderPass)}.Setup");
this.srcTarget = srcTarget;
this.mat = mat;
}
private void Render(CommandBuffer cmd, ref RenderingData renderingData)
{
Debug.Log($"{nameof(ExtendURPCustomPPRenderPass)}.Render");
// jave.lin : 这里有一些细节
// 先拿到 struct CameraData 的引用
// 减少 stack frame 上的 instance, copy, release
ref var camData = ref renderingData.cameraData;
// 将 volume component 的属性设置到 material 中
volumeComp.SetToMat(mat);
// 获取临时 rt des
// jave.lin : copy rt des
RenderTextureDescriptor tempRTDes = camData.cameraTargetDescriptor;
// jave.lin : override remove depth buffer
tempRTDes.depthBufferBits = 0;
// jave.lin : 将 tempRTDes 的内容与 tempTargetHandle.id 也就是 Shader.PropertyToID("_TempRT") 的id 对应绑定
cmd.GetTemporaryRT(tempTargetHandle.id, tempRTDes);
// jave.lin : 这里将是我们 box mosaic 的核心执行的地方
cmd.Blit(srcTarget, tempTargetHandle.Identifier(), mat);
// jave.lin : 将后效处理后的 rt 内容复制会 src
// 便于一下的后效接着已有的内容基础上继续处理
// 如果两个 rt 尺寸不同,也可以使用 blit 来处理
cmd.CopyTexture(tempTargetHandle.Identifier(), srcTarget);
// jave.lin : release 刚刚上面申请的 rt
cmd.ReleaseTemporaryRT(tempTargetHandle.id);
}
}
// jave.lin 2021/10/25
// 用于扩展 URP PP 的自定义 RendererFeature
// 我们定位 ScriptableRenderPass.Execute 方法的作用类似于 Built-in RP 中的
// MonoBehavior 的 void OnRenderImage(RenderTexture src, RenderTexture dest)
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class ExtendURPCustomPPRendererFeature : ScriptableRendererFeature
{
// 用于后效的 shader
public Shader shader;
// 用于后效的 pass
private ExtendURPCustomPPRenderPass pass;
// 用于后效的 mat
private Material mat;
public override void Create()
{
var srcPassObj = pass;
// jave.lin : 如果这里不判断是否创建过,那么会被创建多次而浪费掉
if (srcPassObj == null)
{
// 创建 pass
pass = new ExtendURPCustomPPRenderPass();
// 设置 pass 执行的 event 阶段,可以理解为:inject point
// 这里我们设置到 urp PP 前渲染
pass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
}
// 在该 ScriptableRendererFeature.OnEnabled, OnValidated 被创建时调用
Debug.Log($"{nameof(ExtendURPCustomPPRendererFeature)}.Create, srcPassObj : {srcPassObj}, nowPassObj : {pass}");
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
Debug.Log($"{nameof(ExtendURPCustomPPRendererFeature)}.AddRenderPasses");
// 没设置 shader
if (shader == null) return;
// 根据 shader 构建 material
if (mat == null)
{
mat = CoreUtils.CreateEngineMaterial(shader);
}
// 将相机渲染的目录 rt 作用 pass 后效的 src target
var passSrcTarget = renderer.cameraColorTarget;
// 设置 pass 内容:srcTarget 和 material
pass.Setup(passSrcTarget, mat);
// 添加 pass 到渲染队列中
// 这样 pass 才会被执行 Execute
renderer.EnqueuePass(pass);
}
public void OnDestroy()
{
var srcMat = mat;
if (mat != null)
{
CoreUtils.Destroy(mat);
mat = null;
}
Debug.Log($"{nameof(ExtendURPCustomPPRendererFeature)}.OnDestroy, srcMat : {mat}, nowMat : {mat}");
}
}
// jave.lin 2020.10.25
// URP 版本的 像素化风格的另一种参数控制方式
// Built-in URP 版本的可以参考 blog : https://blog.csdn.net/linjf520/article/details/104842999
Shader "URP/Custom-PostProcess/BoxMosaicPP"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {} // 便于材质中查看该 RT 内容
_PixelSize ("PixelSize", Range(1, 100)) = 1
}
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = TransformObjectToHClip(v.vertex.xyz);
o.uv = v.uv;
return o;
}
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
// jave.lin : 如该 shader 会出现在单帧中重复绘制调用的情况下
// 那么建议将下面的 uniform 都提取到 CBUFFER 中,否则不用提取
float4 _MainTex_TexelSize;
half _PixelSize;
half4 frag(v2f i) : SV_Target
{
float2 interval = _PixelSize * _MainTex_TexelSize.xy;
float2 th = i.uv * rcp(interval); // 按interval划分中,属于第几个像素
float2 th_int = th - frac(th); // 去小数,让采样的第几个纹素整数化,这就是失真UV关键
th_int *= interval; // 再重新按第几个像素的uv坐标定位
return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, th_int);
}
ENDHLSL
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDHLSL
}
}
}
可以看到:Pixel Size == 1 时,SetPass Call 为 4,大于 1 才会 + 1 = 5
其实上面的写法当中,单单查扩展的一个 URP PP 没什么问题
但是如果你想要扩展多个 URP PP 时,你会发现你需要写重复的代码一堆一堆,这时候就是需要你的 软件基本功:OOP 设置一下,封装一下就好了,虽然一致决定 OOP 的设计有很多 诟病,但是对于这么小的一个功能够用,诟病也不会因此放大
TestingURPCustomPP_BoxMosaicPP 提取码: 82cr
官方
第三方的资料
国外
Searching 377 files for "(?!=\[Serializable, DebuggerDisplay\(k_DebuggerDisplay\)\]\n).*?class \w+?Parameter\b" (regex)
H:\WorkFiles\TestingSRPStartProj\TestingSRPStartProj\Library\PackageCache\com.unity.render-pipelines.core@7.7.1\Editor\Volume\SerializedDataParameter.cs:
12: public sealed class SerializedDataParameter
H:\WorkFiles\TestingSRPStartProj\TestingSRPStartProj\Library\PackageCache\com.unity.render-pipelines.core@7.7.1\Runtime\Utilities\TextureCurve.cs:
244: public class TextureCurveParameter : VolumeParameter<TextureCurve>
H:\WorkFiles\TestingSRPStartProj\TestingSRPStartProj\Library\PackageCache\com.unity.render-pipelines.core@7.7.1\Runtime\Volume\VolumeParameter.cs:
18: public abstract class VolumeParameter
131: /// public sealed class MyFloatParameter : VolumeParameter<float>
145: public class VolumeParameter<T> : VolumeParameter, IEquatable<VolumeParameter<T>>
316: // public sealed class MyEnumParameter : VolumeParameter { }
324: public class BoolParameter : VolumeParameter<bool>
339: public class LayerMaskParameter : VolumeParameter<LayerMask>
361: public class IntParameter : VolumeParameter<int>
396: public class NoInterpIntParameter : VolumeParameter<int>
419: public class MinIntParameter : IntParameter
463: public class NoInterpMinIntParameter : VolumeParameter<int>
507: public class MaxIntParameter : IntParameter
551: public class NoInterpMaxIntParameter : VolumeParameter<int>
595: public class ClampedIntParameter : IntParameter
646: public class NoInterpClampedIntParameter : VolumeParameter<int>
698: public class FloatParameter : VolumeParameter<float>
733: public class NoInterpFloatParameter : VolumeParameter<float>
757: public class MinFloatParameter : FloatParameter
803: public class NoInterpMinFloatParameter : VolumeParameter<float>
848: public class MaxFloatParameter : FloatParameter
894: public class NoInterpMaxFloatParameter : VolumeParameter<float>
940: public class ClampedFloatParameter : FloatParameter
993: public class NoInterpClampedFloatParameter : VolumeParameter<float>
1046: public class FloatRangeParameter : VolumeParameter<Vector2>
1115: public class NoInterpFloatRangeParameter : VolumeParameter<Vector2>
1163: public class ColorParameter : VolumeParameter<Color>
1232: public class NoInterpColorParameter : VolumeParameter<Color>
1280: public class Vector2Parameter : VolumeParameter<Vector2>
1308: public class NoInterpVector2Parameter : VolumeParameter<Vector2>
1324: public class Vector3Parameter : VolumeParameter<Vector3>
1353: public class NoInterpVector3Parameter : VolumeParameter<Vector3>
1369: public class Vector4Parameter : VolumeParameter<Vector4>
1399: public class NoInterpVector4Parameter : VolumeParameter<Vector4>
1414: public class TextureParameter : VolumeParameter<Texture>
1431: public class NoInterpTextureParameter : VolumeParameter<Texture>
1446: public class RenderTextureParameter : VolumeParameter<RenderTexture>
1463: public class NoInterpRenderTextureParameter : VolumeParameter<RenderTexture>
1478: public class CubemapParameter : VolumeParameter<Cubemap>
1495: public class NoInterpCubemapParameter : VolumeParameter<Cubemap>
1513: public class ObjectParameter<T> : VolumeParameter<T>
1588: public class AnimationCurveParameter : VolumeParameter<AnimationCurve>
H:\WorkFiles\TestingSRPStartProj\TestingSRPStartProj\Library\PackageCache\com.unity.render-pipelines.universal@7.7.1\Editor\SavedParameter.cs:
6: class SavedParameter<T>
H:\WorkFiles\TestingSRPStartProj\TestingSRPStartProj\Library\PackageCache\com.unity.render-pipelines.universal@7.7.1\Runtime\Overrides\DepthOfField.cs:
60: public sealed class DepthOfFieldModeParameter : VolumeParameter<DepthOfFieldMode> { public DepthOfFieldModeParameter(DepthOfFieldMode value, bool overrideState = false) : base(value, overrideState) { } }
H:\WorkFiles\TestingSRPStartProj\TestingSRPStartProj\Library\PackageCache\com.unity.render-pipelines.universal@7.7.1\Runtime\Overrides\FilmGrain.cs:
41: public sealed class FilmGrainLookupParameter : VolumeParameter<FilmGrainLookup> { public FilmGrainLookupParameter(FilmGrainLookup value, bool overrideState = false) : base(value, overrideState) { } }
H:\WorkFiles\TestingSRPStartProj\TestingSRPStartProj\Library\PackageCache\com.unity.render-pipelines.universal@7.7.1\Runtime\Overrides\MotionBlur.cs:
39: public sealed class MotionBlurModeParameter : VolumeParameter<MotionBlurMode> { public MotionBlurModeParameter(MotionBlurMode value, bool overrideState = false) : base(value, overrideState) { } }
42: public sealed class MotionBlurQualityParameter : VolumeParameter<MotionBlurQuality> { public MotionBlurQualityParameter(MotionBlurQuality value, bool overrideState = false) : base(value, overrideState) { } }
H:\WorkFiles\TestingSRPStartProj\TestingSRPStartProj\Library\PackageCache\com.unity.render-pipelines.universal@7.7.1\Runtime\Overrides\Tonemapping.cs:
24: public sealed class TonemappingModeParameter : VolumeParameter<TonemappingMode> { public TonemappingModeParameter(TonemappingMode value, bool overrideState = false) : base(value, overrideState) { } }
48 matches across 8 files