目录
前言
写一个使用RenderFeature添加PostProcessPass的框架
P1
P2
包含
- VC设置后处理参数
- RenderFeature添加Pass
- 纹理上下采样
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// 通用渲染管线程序集
namespace UnityEngine.Rendering.Universal
{
//添加到Volume组件菜单中
[Serializable, VolumeComponentMenu("CustomVC/PostProcessVC")]
public class PostProcessVC : VolumeComponent
{
/*
ClampedXXXParameter(default, min, max)
ColorParameter(Color.white)
BoolParameter(false)
*/
public IntParameter IntParame2 = new ClampedIntParameter(4, 1, 8);
public FloatParameter FloatParame = new ClampedFloatParameter(0.5f, 0.0f, 1.0f);
public ColorParameter ColorParame = new ColorParameter(Color.white);
public BoolParameter BoolParame = new BoolParameter(false);
}
}
public class BloomSRF : ScriptableRendererFeature
{
[System.Serializable]
public class BloomSettings
{
public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
public Shader shader; // 设置后处理Shader
}
public BloomSettings settings = new BloomSettings();
PostProcessRenderPass postProcessScriptablePass;
public override void Create()
{
this.name = "postProcess"; // 外部显示名字
postProcessScriptablePass =
new PostProcessRenderPass(RenderPassEvent.BeforeRenderingPostProcessing, settings.shader); // 初始化Pass
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
postProcessScriptablePass.Setup(renderingData.cameraData.renderer.cameraColorTarget); // 初始化Pass里的属性
renderer.EnqueuePass(postProcessScriptablePass);
}
}
public class PostProcessRenderPass : ScriptableRenderPass
{
static readonly string k_RenderTag = "PostProcessRender"; // 设置渲染 Tags
static readonly int TempTargetId = Shader.PropertyToID("_存储阈值的临时贴图"); // 设置储存图像信息
PostProcessVC postProcessVC; // 传递到volume
Material postProcessMaterial; // 后处理使用材质
RenderTargetIdentifier cameraColorTexture; // 设置当前渲染目标
Level[] m_Pyramid;
const int k_MaxPyramidSize = 16;
struct Level
{
internal int down;
internal int up;
}
static class ShaderIDs
{
internal static readonly int ShaderID1 = Shader.PropertyToID("ShaderID1");
internal static readonly int ShaderID3 = Shader.PropertyToID("ShaderID2");
internal static readonly int ShaderID2 = Shader.PropertyToID("ShaderID3");
}
public PostProcessRenderPass(RenderPassEvent evt, Shader postProcessShader)
{
renderPassEvent = evt; // 设置渲染事件的位置
var shader = postProcessShader; // 输入Shader信息
// 判断如果不存在Shader
if (shader = null) // Shader如果为空提示
{
Debug.LogError("PostProcessPass没有指定Shader");
return;
}
//如果存在新建材质
postProcessMaterial = CoreUtils.CreateEngineMaterial(postProcessShader);
m_Pyramid = new Level[k_MaxPyramidSize];
for (int i = 0; i < k_MaxPyramidSize; i++)
{
m_Pyramid[i] = new Level
{
down = Shader.PropertyToID("_BlurMipDown" + i),
up = Shader.PropertyToID("_BlurMipUp" + i)
};
}
}
//提交渲染信息
public void Setup(in RenderTargetIdentifier currentTarget)
{
this.cameraColorTexture = currentTarget;
}
//后处理的逻辑和渲染核心函数,基本相当于内置管线的OnRenderImage函数
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// 判断材质是否为空
if (postProcessMaterial == null)
{
Debug.LogError("材质初始化失败");
return;
}
// 判断是否开启后处理
if (!renderingData.cameraData.postProcessEnabled)
{
return;
}
// 渲染设置
var stack = VolumeManager.instance.stack; // 传入volume
postProcessVC = stack.GetComponent<PostProcessVC>(); // 拿到我们的volume
if (postProcessVC == null)
{
Debug.LogError(" Volume组件获取失败 ");
return;
}
var cmd = CommandBufferPool.Get(k_RenderTag); // 设置渲染标签
Render(cmd, ref renderingData); // 设置渲染函数
context.ExecuteCommandBuffer(cmd); // 执行函数
CommandBufferPool.Release(cmd); // 释放
}
void Render(CommandBuffer cmd, ref RenderingData renderingData)
{
ref var cameraData = ref renderingData.cameraData; // 获取摄像机属性
var camera = cameraData.camera; // 传入摄像机
var source = cameraColorTexture; // 获取渲染图片
int buffer0 = TempTargetId; // 渲染结果图片
int tw = (int)(camera.scaledPixelWidth / postProcessVC.降采样.value);
int th = (int)(camera.scaledPixelHeight / postProcessVC.降采样.value);
Vector4 vec4 = new Vector4(postProcessVC.模糊范围.value / (float)Screen.width,
postProcessVC.模糊范围.value / (float)Screen.height, 0, 0);
postProcessMaterial.SetVector(ShaderIDs.vec4, vec4);
postProcessMaterial.SetFloat(ShaderIDs.ShaderID1, postProcessVC.FloatParame.value);
postProcessMaterial.SetFloat(ShaderIDs.ShaderID2, postProcessVC.IntParame2.value);
postProcessMaterial.SetFloat(ShaderIDs.ShaderID3, postProcessVC.BoolParame.value);
//取阈值
cmd.GetTemporaryRT(buffer0,
camera.scaledPixelWidth, camera.scaledPixelHeight, 0,
FilterMode.Trilinear, RenderTextureFormat.Default);
//混合计算
cmd.Blit(source, buffer0);
cmd.Blit(buffer0, source, postProcessMaterial, 0);
// 降采样
RenderTargetIdentifier lastDown = source; //备份
for (int i = 0; i < postProcessVC.迭代次数.value; i++)
{
int mipDown = m_Pyramid[i].down;
int mipUp = m_Pyramid[i].up;
cmd.GetTemporaryRT(mipDown, tw, th, 0, FilterMode.Bilinear);
cmd.GetTemporaryRT(mipUp, tw, th, 0, FilterMode.Bilinear);
cmd.Blit(lastDown, mipDown, postProcessMaterial, 1);
lastDown = mipDown;
tw = Mathf.Max(tw / 2, 1);
th = Mathf.Max(th / 2, 1);
}
// 升采样
int lastUp = m_Pyramid[postProcessVC.迭代次数.value - 1].down;
for (int i = postProcessVC.迭代次数.value - 2; i >= 0; i--)
{
int mipUp = m_Pyramid[i].up;
cmd.Blit(lastUp, mipUp,
postProcessMaterial, 2);
lastUp = mipUp;
}
//合并
if (postProcessVC.Debug.value)
{
cmd.Blit(lastUp, source, postProcessMaterial, 4);
}
else
{
cmd.SetGlobalTexture("_SourceTex", buffer0);
cmd.Blit(lastUp, source, postProcessMaterial, 3);
}
// Cleanup
for (int i = 0; i < postProcessVC.迭代次数.value; i++)
{
if (m_Pyramid[i].down != lastUp)
cmd.ReleaseTemporaryRT(m_Pyramid[i].down);
if (m_Pyramid[i].up != lastUp)
cmd.ReleaseTemporaryRT(m_Pyramid[i].up);
}
}
}