原文链接
版权声明:本文为CSDN博主「莫之」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
正文
一、理论基础
Unity 只提供了很有限的几种后处理效果,这显然是不能满足策划的脑洞和开发者的需求的;而 Unity 的后处理框架允许开发者扩展自己的后处理效果,并将其放入到后处理的堆栈中;参考官方提供的自定义效果,官方给出了一个 灰度效果 比较简单,给出一个稍微复杂点的 高斯模糊 的效果;如下,是一个 grascale+高斯模糊 的混合效果图;
二、着色器编写
后处理部分的 Shader 和渲染 3D 所用的 Shader 稍有不同,而 Unity 框架使用的 Shader 是 HLSL 而不是 CG,但是 HLSL 交叉编译后也可以跨平台所以不用担心这一点;所以,之前的 CG 在这里不能再用,包括之前常用的 UnityCG 文件里包含的各种内置类型,这里也不能用,也不需要再用,而且不再有 fixed
类型;
顶点着色器的不同
这里参考框架自带的 vertexDefault,
- 顶点坐标的计算并不需要 mvp 矩阵变换,
- 纹理坐标的计算也是根据顶点做一个基本变换;
struct AttributesDefault
{
float3 vertex : POSITION;
};
VaryingsDefault VertUVTransform(AttributesDefault v)
{
VaryingsDefault o;
#if STEREO_DOUBLEWIDE_TARGET
o.vertex = float4(v.vertex.xy * _PosScaleOffset.xy + _PosScaleOffset.zw, 0.0, 1.0);
#else
o.vertex = float4(v.vertex.xy, 0.0, 1.0);
#endif
o.texcoord = TransformTriangleVertexToUV(v.vertex.xy) * _UVTransform.xy + _UVTransform.zw;
o.texcoordStereo = TransformStereoScreenSpaceTex(o.texcoord, 1.0);
#if STEREO_INSTANCING_ENABLED
o.stereoTargetEyeIndex = (uint)_DepthSlice;
#endif
return o;
}
渲染状态的设置
设置如下即可
Cull Off ZWrite Off ZTest Always
效果示例
基本区别就在这里,下面是一个用于后处理的高斯模糊的 shader;
Shader "Hidden/Custom/Gaussian"
{
HLSLINCLUDE
#include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl"
// #include "UnityCG.cginc"
// TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex);
sampler2D _MainTex;
half4 _MainTex_TexelSize;
float _BlurSize;
struct a2v {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
half2 uv[5]: TEXCOORD0;
};
v2f vertBlurVertical(a2v v) {
v2f o;
o.pos = float4(v.vertex.xy, 0.0, 1.0);
float2 uv = TransformTriangleVertexToUV(v.vertex.xy);
#if UNITY_UV_STARTS_AT_TOP
uv = uv * float2(1.0, -1.0) + float2(0.0, 1.0);
#endif
o.uv[0] = uv;
o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
return o;
}
v2f vertBlurHorizontal(a2v v) {
v2f o;
o.pos = float4(v.vertex.xy, 0.0, 1.0);
float2 uv = TransformTriangleVertexToUV(v.vertex.xy);
#if UNITY_UV_STARTS_AT_TOP
uv = uv * float2(1.0, -1.0) + float2(0.0, 1.0);
#endif
o.uv[0] = uv;
o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
return o;
}
float4 fragBlur(v2f i) : SV_Target {
float weight[3] = {0.4026, 0.2442, 0.0545};
float3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
for (int it = 1; it < 3; it++) {
sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
}
return float4(sum, 1.0);
}
ENDHLSL
SubShader
{
Cull Off ZWrite Off ZTest Always
Pass
{
HLSLPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDHLSL
}
Pass
{
HLSLPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDHLSL
}
}
}
三、C# 的渲染逻辑
Settings
首先要有一个类来存储高斯模糊的 参数设置,这里定义了一个 可序列化 的类,并需要继承自PostProcessEffectSettings
,
[Serializable]
[PostProcess(typeof(GaussianBlurRenderer), PostProcessEvent.AfterStack, "Custom/GaussianBlur")]
public sealed class GaussianBlurSettings : PostProcessEffectSettings
{
public FloatParameter blurSize = new FloatParameter { value = 0.5f };
}
Renderer
重点 在下面这个渲染类,它继承自 PostProcessEffectRenderer<T>
,并且需要重写 Render
函数;
因为一个高斯模糊需要 两个 pass,需要做 两次 blit 操作,所以我们需要一张 RT 来 暂存 第一个 pass 的处理结果;
public sealed class GaussianBlurRenderer : PostProcessEffectRenderer<GaussianBlurSettings> {
int _tempRTID;
public override void Init() {
_tempRTID = Shader.PropertyToID("_TempRT");
}
public override void Render(PostProcessRenderContext context)
{
var sheet = context.propertySheets.Get(Shader.Find("Hidden/Custom/Gaussian"));
context.command.GetTemporaryRT(_tempRTID, context.width, context.height, 0, FilterMode.Bilinear, context.sourceFormat);
sheet.properties.SetFloat("_BlurSize", settings.blurSize);
context.command.BlitFullscreenTriangle(context.source, _tempRTID, sheet, 0);
context.command.BlitFullscreenTriangle(_tempRTID, context.destination, sheet, 1);
context.command.ReleaseTemporaryRT(_tempRTID);
}
}
四、实现逐渐模糊的动态效果
参考官方文档中的案例,这里将高斯模糊随着时间逐渐变得模糊;
using UnityEngine;
using UnityEngine.Rendering.PostProcessing;
public class GaussianCtrl : MonoBehaviour
{
PostProcessVolume m_Volume;
GaussianBlurSettings m_Gaussian;
// Start is called before the first frame update
void Start()
{
m_Gaussian = ScriptableObject.CreateInstance<GaussianBlurSettings>();
m_Gaussian.enabled.Override(true);
m_Gaussian.blurSize.Override(1.0f);
m_Volume = PostProcessManager.instance.QuickVolume(gameObject.layer, 100f, m_Gaussian);
}
// Update is called once per frame
void Update()
{
m_Gaussian.blurSize.value = Mathf.PingPong(Time.time, 3);
}
}
Unity学习之 PostProcessing 的使用
具体步骤
第一步
新建一个 Post-processing Profile(配置文件)。
第二步
在主相机下加一个组件 PostProcess Layer 。
注意:需要将相机的
Layer
层设置与 Post Process Layer 下的 Volume blending (体积混合)下的Layer
设置一致,也就是设置成 PostProcessing,还要将相机拖进Trigger
里面,我的理解是作为一个触发用的。将相机设置到这个位置,也就是相机可以时刻触发这个插件的设置环境,就是我们可以看到用这个插件做出来的效果。(相同Layer
层)
第三步
新建一个空物体,添加 Postprocess Volume 组件。要勾选 Is Global
(是否全部),才能渲染场景。Weight
是比重的意思,就是渲染的程度。
记得将一开始新建的 Post-processing Profile 拖拽到这里。
其他参考
Unity Post Processing(后处理效果)添加方法及注意事项-最全最新 该文不仅介绍了通用管线下后处理的安装与设置,还涵盖了 URP 管线下的后处理设置。