URP把后处理分成了两个pass,finalPass只在相机抗锯齿模式为FastApproximateAntialiasing时调用。
开启条件,在ForwardRenderer判断
- lastCameraInTheSatck。
- 这名字有误导性,取的值是RenderingData的resolveFinalTarget,而resolveFinalTarget是在InitializeRenderingData函数设置的,取的值是requiresBlitToBackbuffer,InitializeRenderingData被RenderSingleCamera调用。requiresBlitToBackbuffer会在scene相机渲染,以及stack的最后一个相机设为true。
- 这个参数的真正意义,我的理解是表示这个相机是否要渲染到屏幕上,如果要渲染到屏幕,再去判断相机是否开启后处理。
- 后处理已经把画面渲染到屏幕上了,所以finalBlitPass不需要执行。
bool applyFinalPostProcessing = anyPostProcessing && lastCameraInTheStack &&renderingData.cameraData.antialiasing == AntialiasingMode.FastApproximateAntialiasing;
- applyFinalPostProcessing:
- 为true的条件,相机开启后处理,并且lastCameraInTheStack为true、抗锯齿模式为FastApproximateAntialiasing。
- applyPostProcessing:
- 只要相机开启后处理就为true。
- 如果lastCameraInTheStack为true,会设置渲染目标rt,并设置是否需要对sRGB做转换。
PostProcessPass,驱动各个后处理shader
数据
- MaterialLibrary:定义实现各个效果的material。
- PostProcessData:ScriptableObject,配置shader和贴图。在editor可以创建ScriptableObject的实例,但是并不能编辑shader,可能是个未开发完的功能。
- ShaderConstants:缓存shader名字对应的id。
RenderFinalPass,渲染final pass
var material = m_Materials.finalPass;
获取material,用于后续渲染。
if (cameraData.antialiasing == AntialiasingMode.FastApproximateAntialiasing)
material.EnableKeyword(ShaderKeywordStrings.Fxaa);
逻辑上来说判断抗锯齿模式是多余的,只有fxaa才会用这个分支渲染。
SetupGrain(cameraData, material);
SetupDithering(cameraData, material);
如果对应效果开启,设置shader关键字或贴图,shader用的是finalPass。这俩并不对应具体的shader。
Render函数
Render函数里有个有趣的写法,int GetSource() => source;类似这种,在函数内定义函数。
这个函数按顺序渲染各个后处理效果,简单看下流程。
cmd.Blit(GetSource(), BlitDstDiscardContent(cmd, GetDestination()), m_Materials.stopNaN);
- 最先渲染的是一个特殊的NaNshader,可选的,作用是将不合法的颜色值用自定义的颜色输出,应该是调试用的,判断颜色是否合法的代码在CoreRP的Common.hlsl文件。
DoSubpixelMorphologicalAntialiasing(ref cameraData, cmd, GetSource(), GetDestination());
- 处理SubpixelMorphological模式的抗锯齿,消耗较大,移动平台使用要看情况。
DoDepthOfField(cameraData.camera, cmd, GetSource(), GetDestination(), cameraData.pixelRect);
- 对scene camera不开启。Gaussian和Bokeh两种模式
DoMotionBlur(cameraData.camera, cmd, GetSource(), GetDestination());
- 对scene camera不开启。是相机的运动模糊,物体运动是没有效果的。
DoPaniniProjection(cameraData.camera, cmd, GetSource(), GetDestination());
- 对scene camera不开启。帕尼尼投影,一种圆柱形投影,效果是扭曲两侧,突出中间,在渲染大视角的时候提供更好的效果。
后面的都和UberPost shader相关
void SetupBloom(CommandBuffer cmd, int source, Material uberMaterial)
- 先按分辨率大小计算模糊次数,每次水平竖直分别模糊,没吃降低一倍分辨率,然后在执行Upsample,完成bloom。
- 设置bloom相关参数和贴图到UberPost shader。设置lens dirtiness相关参数到uber。
SetupLensDistortion(m_Materials.uber, cameraData.isSceneViewCamera);
- 镜头扭曲效果,设置uber shader的参数。
SetupChromaticAberration(m_Materials.uber);
- 比较高性能的色差效果。
SetupVignette(m_Materials.uber);
- 图像边缘的色调变暗,突出中间部分。
SetupColorGrading(cmd, ref renderingData, m_Materials.uber);
- 改变或矫正最终图像的颜色和亮度,处理lut、hdr、tonemapping
SetupGrain(cameraData, m_Materials.uber);
SetupDithering(cameraData, m_Materials.uber);
- 在不是final pass的情况下执行。
cmd.SetGlobalTexture("_BlitTex", GetSource());
- rt设置给_BlitTex。
cmd.SetRenderTarget(cameraTarget, colorLoadAction, RenderBufferStoreAction.Store, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.DontCare);
- 设置rt为相机,完成渲染。
这些常见的后处理效果,都有比较成熟的算法,具体逻辑就不细看了。