设置一个被剔除的图层
编写一个 CustomPass
在 AggregateCullingParameters 中,额外包含了目标图层,以进行接下来的渲染。
在 Execute() 中,这个 Pass 主要做了以下事情:
- 对目标图层进行完整渲染。(cameraColorBuffer)
- 对目标图层写入Motion Vector。(cameraMotionVectorsBuffer)
using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using System.Collections.Generic;
using System;
/// <summary>
/// DrawRenderers Custom Pass
/// </summary>
[System.Serializable]
public class ViewingObjectPass : CustomPass
{
/// <summary>
/// HDRP Shader passes
/// </summary>
public enum ShaderPass
{
// Ordered by frame time in HDRP
///<summary>Object Depth pre-pass, only the depth of the object will be rendered.</summary>
DepthPrepass = 1,
///<summary>Forward pass, render the object color.</summary>
Forward = 0,
}
// Used only for the UI to keep track of the toggle state
[SerializeField] internal bool filterFoldout;
[SerializeField] internal bool rendererFoldout;
//Filter settings
/// <summary>
/// Render Queue filter to select which kind of object to render.
/// </summary>
public RenderQueueType renderQueueType = RenderQueueType.AllOpaque;
/// <summary>
/// Layer Mask filter, select which layer to render.
/// </summary>
public LayerMask layerMask = 1; // Layer mask Default enabled
/// <summary>
/// Sorting flags of the objects to render.
/// </summary>
public SortingCriteria sortingCriteria = SortingCriteria.CommonOpaque;
/// <summary>
/// When true, overrides the depth state of the objects.
/// </summary>
public bool overrideDepthState = false;
/// <summary>
/// Overrides the Depth comparison function, only used when overrideDepthState is true.
/// </summary>
public CompareFunction depthCompareFunction = CompareFunction.LessEqual;
/// <summary>
/// Overrides the Depth write, only used when overrideDepthState is true.
/// </summary>
public bool depthWrite = true;
/// <summary>
/// Set the shader pass to use when the override material is null
/// </summary>
public ShaderPass shaderPass = ShaderPass.Forward;
int fadeValueId;
static ShaderTagId[] forwardShaderTags;
static ShaderTagId[] depthShaderTags;
// Cache the shaderTagIds so we don't allocate a new array each frame
ShaderTagId[] cachedShaderTagIDs;
[SerializeField, HideInInspector]
Shader motionVectorShader;
Material motionVector;
/// <summary>
/// Called before the first execution of the pass occurs.
/// Allow you to allocate custom buffers.
/// </summary>
/// <param name="renderContext">The render context</param>
/// <param name="cmd">Current command buffer of the frame</param>
protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
{
fadeValueId = Shader.PropertyToID("_FadeValue");
// In case there was a pass index assigned, we retrieve the name of this pass
//if (String.IsNullOrEmpty(overrideMaterialPassName) && overrideMaterial != null)
// overrideMaterialPassName = overrideMaterial.GetPassName(overrideMaterialPassIndex);
forwardShaderTags = new ShaderTagId[]
{
HDShaderPassNames.s_ForwardName, // HD Lit shader
HDShaderPassNames.s_ForwardOnlyName, // HD Unlit shader
HDShaderPassNames.s_SRPDefaultUnlitName, // Cross SRP Unlit shader
HDShaderPassNames.s_EmptyName, // Add an empty slot for the override material
};
depthShaderTags = new ShaderTagId[]
{
HDShaderPassNames.s_DepthForwardOnlyName,
HDShaderPassNames.s_DepthOnlyName,
HDShaderPassNames.s_EmptyName, // Add an empty slot for the override material
};
motionVectorShader = Shader.Find("Hidden/HDRP/MotionVectors");
motionVector = CoreUtils.CreateEngineMaterial(motionVectorShader);
}
/// <summary>
/// Use this method if you want to draw objects that are not visible in the camera.
/// For example if you disable a layer in the camera and add it in the culling parameters, then the culling result will contains your layer.
/// </summary>
/// <param name="cullingParameters">Aggregate the parameters in this property (use |= for masks fields, etc.)</param>
/// <param name="hdCamera">The camera where the culling is being done</param>
protected override void AggregateCullingParameters(ref ScriptableCullingParameters cullingParameters, HDCamera hdCamera)
{
cullingParameters.cullingMask |= (uint)(int)layerMask;
}
ShaderTagId[] GetShaderTagIds()
{
if (shaderPass == ShaderPass.DepthPrepass)
return depthShaderTags;
else
return forwardShaderTags;
}
/// <summary>
/// Execute the DrawRenderers with parameters setup from the editor
/// </summary>
/// <param name="ctx">The context of the custom pass. Contains command buffer, render context, buffer, etc.</param>
protected override void Execute(CustomPassContext ctx)
{
var shaderPasses = GetShaderTagIds();
//if (overrideMaterial != null)
//{
// shaderPasses[shaderPasses.Length - 1] = new ShaderTagId(overrideMaterialPassName);
// overrideMaterial.SetFloat(fadeValueId, fadeValue);
//}
if (shaderPasses.Length == 0)
{
Debug.LogWarning("Attempt to call DrawRenderers with an empty shader passes. Skipping the call to avoid errors");
return;
}
var mask = overrideDepthState ? RenderStateMask.Depth : 0;
mask |= overrideDepthState && !depthWrite ? RenderStateMask.Stencil : 0;
var stateBlock = new RenderStateBlock(mask)
{
depthState = new DepthState(depthWrite, depthCompareFunction),
// We disable the stencil when the depth is overwritten but we don't write to it, to prevent writing to the stencil.
stencilState = new StencilState(false),
};
PerObjectData renderConfig = ctx.hdCamera.frameSettings.IsEnabled(FrameSettingsField.Shadowmask)
? HDUtils.GetBakedLightingWithShadowMaskRenderConfig()
: HDUtils.GetBakedLightingRenderConfig();
var result = new UnityEngine.Rendering.RendererUtils.RendererListDesc(shaderPasses, ctx.cullingResults, ctx.hdCamera.camera)
{
rendererConfiguration = renderConfig,
renderQueueRange = GetRenderQueueRange(renderQueueType),
sortingCriteria = sortingCriteria,
excludeObjectMotionVectors = false,
stateBlock = stateBlock,
layerMask = layerMask,
};
var renderCtx = ctx.renderContext;
CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ctx.cameraDepthBuffer, ClearFlag.None);
CoreUtils.DrawRendererList(ctx.renderContext, ctx.cmd, renderCtx.CreateRendererList(result));
result = new UnityEngine.Rendering.RendererUtils.RendererListDesc(HDShaderPassNames.s_MotionVectorsName, ctx.cullingResults, ctx.hdCamera.camera)
{
rendererConfiguration = PerObjectData.MotionVectors,
renderQueueRange = GetRenderQueueRange(renderQueueType),
sortingCriteria = sortingCriteria,
excludeObjectMotionVectors = false,
stateBlock = stateBlock,
layerMask = layerMask,
};
CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraMotionVectorsBuffer, ctx.cameraDepthBuffer, ClearFlag.None);
CoreUtils.DrawRendererList(ctx.renderContext, ctx.cmd, renderCtx.CreateRendererList(result));
}
protected override void Cleanup()
{
CoreUtils.Destroy(motionVector);
}
}
把 Pass 挂起来,如图进行配置
效果
理论上不出bug的话跟正常渲染没啥区别(
可能会有奇怪的地方出BUG,比如SSAO(这个我直接禁用了),比如运动模糊和TAA(已经修复,即代码中的写入Motion Vector),欢迎留言讨论。
抛砖引玉,恳请批评指正!
参考文献:
Unity SRP 中渲染 Motion Vector 的正确姿势 - SardineFish - https://www.sardinefish.com/blog/458