RenderPipeLine
渲染管线:
将对象显示到屏幕上所需要的一些流程及技术的总称。
例如:多通道渲染,单通道渲染,延迟渲染。
为什么需要可编程渲染管线:
Unity自带的渲染管道只有Forward和Deferred两种。各有优缺点。
不开源。
可编程渲染管线:
如何使用:需要定一个类,继承RenderPipeline, 并且重写其中的Render方法
渲染管线上下文:Render方法中参数之一,ScriptableRenderContext,可以编写一些缓冲区指令,并且用context去执行这些cmd.
CommandBuffer
CommandBuffer携带一系列的渲染命令,依赖相机,用来拓展渲染管线的渲染效果。
而且可以指定在相机渲染的某个点执行本身的拓展渲染。如上述Unity渲染管线图片,我们可以根据参数指定CommandBuffer在绿色的点上添加执行命令。
Command buffers也可以结合屏幕后期效果使用。 用于拓展Unity渲染管线。包含一系列渲染命令,比如设置渲染目标,绘制网格等,并可以设置为在摄像机期间的各个点执行渲染。
本文所用的Unity版本和LWRP的package版本分别是:
- Unity版本:2019.2.4f1
- LWRP的版本为:com.unity.render-pipelines.lightweight@6.9.1
主要讲解LWRP里面关于核心渲染步骤的源码:
可编程渲染管线如何使用:需要定一个类,继承RenderPipeline, 并且重写其中的Render方法
渲染管线上下文:Render方法中参数之一,ScriptableRenderContext,可以编写一些缓冲区指令,并且用context去执行这些cmd.
LightweightRenderPipeline
LightweightRenderPipeline继承了RenderPipeline,重写Render方法
Render方法
RenderSingleCamera 方法
ForwardRenderer
ForwardRenderer构造方法
Setup重载
LightweightRenderPipeline
Render方法
// 渲染入口点,渲染入口点为RenderPipeline类中的Render函数,该类重写了Render函数
// 参数为相机列表和渲染上下文,在这个函数的最后提交渲染上下文,完成渲染
protected override void Render(ScriptableRenderContext renderContext, Camera[] cameras)
{//ScriptableRenderContext定义在自定义渲染管道中使用的状态和绘图命令
BeginFrameRendering(renderContext, cameras);
//如果使用线性颜色空间 灯光强度使用线性空间下的强度
GraphicsSettings.lightsUseLinearIntensity = (QualitySettings.activeColorSpace == ColorSpace.Linear);
//是否开启SRP Batcher 通过piplineasset 配置
GraphicsSettings.useScriptableRenderPipelineBatching = asset.useSRPBatcher;
//设置shader的一些常亮
SetupPerFrameShaderConstants();
//对相机进行深度排序
SortCameras(cameras);
// 遍历相机
foreach (Camera camera in cameras)
{
BeginCameraRendering(renderContext, camera);
UnityEngine.Experimental.VFX.VFXManager.ProcessCamera(camera); //Visual Effect Graph is not yet a required package but calling this method when there isn't any VisualEffect component has no effect (but needed for Camera sorting in Visual Effect Graph context)
//正式开始渲染
RenderSingleCamera(renderContext, camera);
EndCameraRendering(renderContext, camera);
}
EndFrameRendering(renderContext, cameras);
}
整体结构非常简单,设置GraphicSetting参数,设置每帧Shader中的Global 变量,相机排序,相机遍历,每相机渲染。这里只有一个需要关心的方法RenderSingleCamera(renderContext, camera)。
RenderSingleCamera 方法
public static void RenderSingleCamera(ScriptableRenderContext context, Camera camera)
{
//获取相机剔除参数
if (!camera.TryGetCullingParameters(IsStereoEnabled(camera), out var cullingParameters))
return;
//设置管线资源
var settings = asset;
//获取camera附加数据
LWRPAdditionalCameraData additionalCameraData = null;
if (camera.cameraType == CameraType.Game || camera.cameraType == CameraType.VR)
#if UNITY_2019_2_OR_NEWER
camera.gameObject.TryGetComponent(out additionalCameraData);
#else
additionalCameraData = camera.gameObject.GetComponent<LWRPAdditionalCameraData>();
#endif
//初始化camera 数据
InitializeCameraData(settings, camera, additionalCameraData, out var cameraData);
//设置camera相关的shader常亮 相机宽高 投影矩阵等
SetupPerCameraShaderConstants(cameraData);
//获取渲染器
ScriptableRenderer renderer = (additionalCameraData != null) ? additionalCameraData.scriptableRenderer : settings.scriptableRenderer;
if (renderer == null)
{
Debug.LogWarning(string.Format("Trying to render {0} with an invalid renderer. Camera rendering will be skipped.", camera.name));
return;
}
#if UNITY_EDITOR
string tag = camera.name;
#else
string tag = k_RenderCameraTag;
#endif
//获取渲染用的commandbuffer
CommandBuffer cmd = CommandBufferPool.Get(tag);
using (new ProfilingSample(cmd, tag))
{
//清空渲染器 设置剔除参数
renderer.Clear();
renderer.SetupCullingParameters(ref cullingParameters, ref cameraData);
//渲染上下文执行commandbuffer
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
#if UNITY_EDITOR
// Emit scene view UI
if (cameraData.isSceneViewCamera)
ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
#endif
//根据前面的剔除参数得到剔除结果
var cullResults = context.Cull(ref cullingParameters);
//初始化渲染数据renderingdata 是否生成阴影 传入剔除结果和相机数据等供渲染器使用
InitializeRenderingData(settings, ref cameraData, ref cullResults, out var renderingData);
renderer.Setup(context, ref renderingData);
renderer.Execute(context, ref renderingData);
}
context.ExecuteCommandBuffer(cmd);
//清空cb
CommandBufferPool.Release(cmd);
//提交渲染执行
context.Submit();
}
这个方法的过程如下:
1.初始化剔除参数
2.获取UniversalAdditionalCameraData
3.初始化CameraData
4.设置PerCameraBuffer(每相机使用的Shader Global变量PerCameraBuffer)
5.获取ScriptableRenderer
6.使用ScriptableRenderer继续填充剔除参数和CameraData
7.开始性能采样(Profiler面板)
8.编辑器模式下Scene相机额外显示UI
9.剔除
10.根据管线设置、CameraData、剔除结果,初始化渲染数据RenderingData
11.使用ScriptableRenderer根据RenderingData,Setup并Excute渲染上下文
12.结束性能分析
13.提交渲染上下文
ForwardRenderer
渲染器的实例化在渲染器Data中 由渲染管线asset创建供渲染管线调用
ForwardRenderer构造方法
public ForwardRenderer(ForwardRendererData data) : base(data)
{
//根据4个shader创建材质 全屏拷贝 深度拷贝 采样 屏幕阴影
Material blitMaterial = CoreUtils.CreateEngineMaterial(data.shaders.blitPS);
Material copyDepthMaterial = CoreUtils.CreateEngineMaterial(data.shaders.copyDepthPS);
Material samplingMaterial = CoreUtils.CreateEngineMaterial(data.shaders.samplingPS);
Material screenspaceShadowsMaterial = CoreUtils.CreateEngineMaterial(data.shaders.screenSpaceShadowPS);
//获取forwarddata中的模板缓冲数据
StencilStateData stencilData = data.defaultStencilState;
m_DefaultStencilState = StencilState.defaultValue;
m_DefaultStencilState.enabled = stencilData.overrideStencilState;
m_DefaultStencilState.SetCompareFunction(stencilData.stencilCompareFunction);
m_DefaultStencilState.SetPassOperation(stencilData.passOperation);
m_DefaultStencilState.SetFailOperation(stencilData.failOperation);
m_DefaultStencilState.SetZFailOperation(stencilData.zFailOperation);
// Note: Since all custom render passes inject first and we have stable sort,
// we inject the builtin passes in the before events.
// 初始化各种pass 并且根据evt排序
m_MainLightShadowCasterPass = new MainLightShadowCasterPass(RenderPassEvent.BeforeRenderingShadows);
m_AdditionalLightsShadowCasterPass = new AdditionalLightsShadowCasterPass(RenderPassEvent.BeforeRenderingShadows);
m_DepthPrepass = new DepthOnlyPass(RenderPassEvent.BeforeRenderingPrepasses, RenderQueueRange.opaque, data.opaqueLayerMask);
m_ScreenSpaceShadowResolvePass = new ScreenSpaceShadowResolvePass(RenderPassEvent.BeforeRenderingPrepasses, screenspaceShadowsMaterial);
m_RenderOpaqueForwardPass = new DrawObjectsPass("Render Opaques", true, RenderPassEvent.BeforeRenderingOpaques, RenderQueueRange.opaque, data.opaqueLayerMask, m_DefaultStencilState, stencilData.stencilReference);
m_CopyDepthPass = new CopyDepthPass(RenderPassEvent.BeforeRenderingOpaques, copyDepthMaterial);
m_OpaquePostProcessPass = new PostProcessPass(RenderPassEvent.BeforeRenderingOpaques, true);
m_DrawSkyboxPass = new DrawSkyboxPass(RenderPassEvent.BeforeRenderingSkybox);
m_CopyColorPass = new CopyColorPass(RenderPassEvent.BeforeRenderingTransparents, samplingMaterial);
m_RenderTransparentForwardPass = new DrawObjectsPass("Render Transparents", false, RenderPassEvent.BeforeRenderingTransparents, RenderQueueRange.transparent, data.transparentLayerMask, m_DefaultStencilState, stencilData.stencilReference);
m_PostProcessPass = new PostProcessPass(RenderPassEvent.BeforeRenderingPostProcessing);
m_CapturePass = new CapturePass(RenderPassEvent.AfterRendering);
m_FinalBlitPass = new FinalBlitPass(RenderPassEvent.AfterRendering, blitMaterial);
#if UNITY_EDITOR
m_SceneViewDepthCopyPass = new SceneViewDepthCopyPass(RenderPassEvent.AfterRendering + 9, copyDepthMaterial);
#endif
// RenderTexture format depends on camera and pipeline (HDR, non HDR, etc)
// Samples (MSAA) depend on camera and pipeline
m_CameraColorAttachment.Init("_CameraColorTexture");
m_CameraDepthAttachment.Init("_CameraDepthAttachment");
m_DepthTexture.Init("_CameraDepthTexture");
m_OpaqueColor.Init("_CameraOpaqueTexture");
//初始化灯光
m_ForwardLights = new ForwardLights();
}
Setup重载
public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
{
Camera camera = renderingData.cameraData.camera;
//通过相机的msaa hdr等创建RT的描述
RenderTextureDescriptor cameraTargetDescriptor = renderingData.cameraData.cameraTargetDescriptor;
// Special path for depth only offscreen cameras. Only write opaques + transparents.
//如果是屏幕外的深度相机 只写入 opaques + transparents.
bool isOffscreenDepthTexture = camera.targetTexture != null && camera.targetTexture.format == RenderTextureFormat.Depth;
if (isOffscreenDepthTexture)
{
ConfigureCameraTarget(BuiltinRenderTextureType.CameraTarget, BuiltinRenderTextureType.CameraTarget);
for (int i = 0; i < rendererFeatures.Count; ++i)
rendererFeatures[i].AddRenderPasses(this, ref renderingData);
EnqueuePass(m_RenderOpaqueForwardPass);
EnqueuePass(m_DrawSkyboxPass);
EnqueuePass(m_RenderTransparentForwardPass);
return;
}
//设置阴影相关pass 是否支持阴影
bool mainLightShadows = m_MainLightShadowCasterPass.Setup(ref renderingData);
bool additionalLightShadows = m_AdditionalLightsShadowCasterPass.Setup(ref renderingData);
bool resolveShadowsInScreenSpace = mainLightShadows && renderingData.shadowData.requiresScreenSpaceShadowResolve;
// Depth prepass is generated in the following cases:
// - We resolve shadows in screen space
// - Scene view camera always requires a depth texture. We do a depth pre-pass to simplify it and it shouldn't matter much for editor.
// - If game or offscreen camera requires it we check if we can copy the depth from the rendering opaques pass and use that instead.
//是否生成深度prepass
bool requiresDepthPrepass = renderingData.cameraData.isSceneViewCamera ||
(renderingData.cameraData.requiresDepthTexture && (!CanCopyDepth(ref renderingData.cameraData)));
requiresDepthPrepass |= resolveShadowsInScreenSpace;
// TODO: There's an issue in multiview and depth copy pass. Atm forcing a depth prepass on XR until
// we have a proper fix.
if (renderingData.cameraData.isStereoEnabled && renderingData.cameraData.requiresDepthTexture)
requiresDepthPrepass = true;
bool createColorTexture = RequiresIntermediateColorTexture(ref renderingData, cameraTargetDescriptor)
|| rendererFeatures.Count != 0;
// If camera requires depth and there's no depth pre-pass we create a depth texture that can be read
// later by effect requiring it.
//没有深度prepass的情况下生成深度图
bool createDepthTexture = renderingData.cameraData.requiresDepthTexture && !requiresDepthPrepass;
//是否开启后处理
bool postProcessEnabled = renderingData.cameraData.postProcessEnabled;
bool hasOpaquePostProcess = postProcessEnabled &&
renderingData.cameraData.postProcessLayer.HasOpaqueOnlyEffects(RenderingUtils.postProcessRenderContext);
//设置颜色和深度纹理的rendertargethandle
m_ActiveCameraColorAttachment = (createColorTexture) ? m_CameraColorAttachment : RenderTargetHandle.CameraTarget;
m_ActiveCameraDepthAttachment = (createDepthTexture) ? m_CameraDepthAttachment : RenderTargetHandle.CameraTarget;
bool intermediateRenderTexture = createColorTexture || createDepthTexture;
//根据需求创建临时的RT
if (intermediateRenderTexture)
CreateCameraRenderTarget(context, ref renderingData.cameraData);
//记录两个RT ID
ConfigureCameraTarget(m_ActiveCameraColorAttachment.Identifier(), m_ActiveCameraDepthAttachment.Identifier());
// if rendering to intermediate render texture we don't have to create msaa backbuffer
//如果有颜色和深度纹理 使用中间纹理就不再创建MSAA backbuffer
int backbufferMsaaSamples = (intermediateRenderTexture) ? 1 : cameraTargetDescriptor.msaaSamples;
if (Camera.main == camera && camera.cameraType == CameraType.Game && camera.targetTexture == null)
SetupBackbufferFormat(backbufferMsaaSamples, renderingData.cameraData.isStereoEnabled);
//添加各种feature的pass进渲染器
for (int i = 0; i < rendererFeatures.Count; ++i)
{
rendererFeatures[i].AddRenderPasses(this, ref renderingData);
}
int count = activeRenderPassQueue.Count;
for (int i = count - 1; i >= 0; i--)
{
if(activeRenderPassQueue[i] == null)
activeRenderPassQueue.RemoveAt(i);
}
//查找是否有afterrender的pass
bool hasAfterRendering = activeRenderPassQueue.Find(x => x.renderPassEvent == RenderPassEvent.AfterRendering) != null;
//添加主光阴影pass
if (mainLightShadows)
EnqueuePass(m_MainLightShadowCasterPass);
//添加附加光阴影pass
if (additionalLightShadows)
EnqueuePass(m_AdditionalLightsShadowCasterPass);
//添加并设置深度预处理pass
if (requiresDepthPrepass)
{
m_DepthPrepass.Setup(cameraTargetDescriptor, m_DepthTexture);
EnqueuePass(m_DepthPrepass);
}
//添加屏幕空间阴影pass
if (resolveShadowsInScreenSpace)
{
m_ScreenSpaceShadowResolvePass.Setup(cameraTargetDescriptor);
EnqueuePass(m_ScreenSpaceShadowResolvePass);
}
//不透明物体渲染pass
EnqueuePass(m_RenderOpaqueForwardPass);
//设置不透明物体后处理
if (hasOpaquePostProcess)
m_OpaquePostProcessPass.Setup(cameraTargetDescriptor, m_ActiveCameraColorAttachment, m_ActiveCameraColorAttachment);
//天空盒pass
if (camera.clearFlags == CameraClearFlags.Skybox && RenderSettings.skybox != null)
EnqueuePass(m_DrawSkyboxPass);
// If a depth texture was created we necessarily need to copy it, otherwise we could have render it to a renderbuffer
//拷贝深度贴图
if (createDepthTexture)
{
m_CopyDepthPass.Setup(m_ActiveCameraDepthAttachment, m_DepthTexture);
EnqueuePass(m_CopyDepthPass);
}
//拷贝颜色贴图
if (renderingData.cameraData.requiresOpaqueTexture)
{
// TODO: Downsampling method should be store in the renderer isntead of in the asset.
// We need to migrate this data to renderer. For now, we query the method in the active asset.
Downsampling downsamplingMethod = LightweightRenderPipeline.asset.opaqueDownsampling;
m_CopyColorPass.Setup(m_ActiveCameraColorAttachment.Identifier(), m_OpaqueColor, downsamplingMethod);
EnqueuePass(m_CopyColorPass);
}
//添加半透明渲染Pass
EnqueuePass(m_RenderTransparentForwardPass);
bool afterRenderExists = renderingData.cameraData.captureActions != null ||
hasAfterRendering;
// if we have additional filters
// we need to stay in a RT
//添加最后的几个pass 后处理pass 捕获pass 最终拷贝pass
if (afterRenderExists)
{
// perform post with src / dest the same
if (postProcessEnabled)
{
m_PostProcessPass.Setup(cameraTargetDescriptor, m_ActiveCameraColorAttachment, m_ActiveCameraColorAttachment);
EnqueuePass(m_PostProcessPass);
}
//now blit into the final target
if (m_ActiveCameraColorAttachment != RenderTargetHandle.CameraTarget)
{
if (renderingData.cameraData.captureActions != null)
{
m_CapturePass.Setup(m_ActiveCameraColorAttachment);
EnqueuePass(m_CapturePass);
}
m_FinalBlitPass.Setup(cameraTargetDescriptor, m_ActiveCameraColorAttachment);
EnqueuePass(m_FinalBlitPass);
}
}
else
{
if (postProcessEnabled)
{
m_PostProcessPass.Setup(cameraTargetDescriptor, m_ActiveCameraColorAttachment, RenderTargetHandle.CameraTarget);
EnqueuePass(m_PostProcessPass);
}
else if (m_ActiveCameraColorAttachment != RenderTargetHandle.CameraTarget)
{
m_FinalBlitPass.Setup(cameraTargetDescriptor, m_ActiveCameraColorAttachment);
EnqueuePass(m_FinalBlitPass);
}
}
#if UNITY_EDITOR
if (renderingData.cameraData.isSceneViewCamera)
{
m_SceneViewDepthCopyPass.Setup(m_DepthTexture);
EnqueuePass(m_SceneViewDepthCopyPass);
}
#endif
}