這篇文章的主要內容是講Unity的內置可編程渲染管線LWRP的渲染流程。即這一幀內從開始到結束所經過的渲染步驟。
由於版本時效性,我寫博文時所用的Unity版本和LWRP的package版本分別是:
Unity版本:2018.3.0f2 LWRP的版本爲:4.10.0.preview
先上LWRP裏面關於核心渲染步驟的源代碼腳本(DefaultRendererSetup.cs):
using System;
using UnityEngine.Rendering;
using System.Collections.Generic;
namespace UnityEngine.Experimental.Rendering.LightweightPipeline
{
internal class DefaultRendererSetup : IRendererSetup
{
private DepthOnlyPass m_DepthOnlyPass;
private MainLightShadowCasterPass m_MainLightShadowCasterPass;
private AdditionalLightsShadowCasterPass m_AdditionalLightsShadowCasterPass;
private SetupForwardRenderingPass m_SetupForwardRenderingPass;
private ScreenSpaceShadowResolvePass m_ScreenSpaceShadowResolvePass;
private CreateLightweightRenderTexturesPass m_CreateLightweightRenderTexturesPass;
private BeginXRRenderingPass m_BeginXrRenderingPass;
private SetupLightweightConstanstPass m_SetupLightweightConstants;
private RenderOpaqueForwardPass m_RenderOpaqueForwardPass;
private OpaquePostProcessPass m_OpaquePostProcessPass;
private DrawSkyboxPass m_DrawSkyboxPass;
private CopyDepthPass m_CopyDepthPass;
private CopyColorPass m_CopyColorPass;
private RenderTransparentForwardPass m_RenderTransparentForwardPass;
private TransparentPostProcessPass m_TransparentPostProcessPass;
private FinalBlitPass m_FinalBlitPass;
private EndXRRenderingPass m_EndXrRenderingPass;
private CustomRenderPass m_CustomPass;
#if UNITY_EDITOR
private SceneViewDepthCopyPass m_SceneViewDepthCopyPass;
#endif
private RenderTargetHandle ColorAttachment;
private RenderTargetHandle DepthAttachment;
private RenderTargetHandle DepthTexture;
private RenderTargetHandle OpaqueColor;
private RenderTargetHandle MainLightShadowmap;
private RenderTargetHandle AdditionalLightsShadowmap;
private RenderTargetHandle ScreenSpaceShadowmap;
private List<IBeforeRender> m_BeforeRenderPasses = new List<IBeforeRender>(10);
[NonSerialized]
private bool m_Initialized = false;
private void Init()
{
if (m_Initialized)
return;
m_DepthOnlyPass = new DepthOnlyPass();
m_MainLightShadowCasterPass = new MainLightShadowCasterPass();
m_AdditionalLightsShadowCasterPass = new AdditionalLightsShadowCasterPass();
m_SetupForwardRenderingPass = new SetupForwardRenderingPass();
m_ScreenSpaceShadowResolvePass = new ScreenSpaceShadowResolvePass();
m_CreateLightweightRenderTexturesPass = new CreateLightweightRenderTexturesPass();
m_BeginXrRenderingPass = new BeginXRRenderingPass();
m_SetupLightweightConstants = new SetupLightweightConstanstPass();
m_RenderOpaqueForwardPass = new RenderOpaqueForwardPass();
m_OpaquePostProcessPass = new OpaquePostProcessPass();
m_DrawSkyboxPass = new DrawSkyboxPass();
m_CopyDepthPass = new CopyDepthPass();
m_CopyColorPass = new CopyColorPass();
m_RenderTransparentForwardPass = new RenderTransparentForwardPass();
m_TransparentPostProcessPass = new TransparentPostProcessPass();
m_FinalBlitPass = new FinalBlitPass();
m_EndXrRenderingPass = new EndXRRenderingPass();
m_CustomPass = new CustomRenderPass();
#if UNITY_EDITOR
m_SceneViewDepthCopyPass = new SceneViewDepthCopyPass();
#endif
// RenderTexture format depends on camera and pipeline (HDR, non HDR, etc)
// Samples (MSAA) depend on camera and pipeline
ColorAttachment.Init("_CameraColorTexture");
DepthAttachment.Init("_CameraDepthAttachment");
DepthTexture.Init("_CameraDepthTexture");
OpaqueColor.Init("_CameraOpaqueTexture");
MainLightShadowmap.Init("_MainLightShadowmapTexture");
AdditionalLightsShadowmap.Init("_AdditionalLightsShadowmapTexture");
ScreenSpaceShadowmap.Init("_ScreenSpaceShadowmapTexture");
m_Initialized = true;
}
public void Setup(ScriptableRenderer renderer, ref RenderingData renderingData)
{
Init();
Camera camera = renderingData.cameraData.camera;
camera.GetComponents(m_BeforeRenderPasses);
renderer.SetupPerObjectLightIndices(ref renderingData.cullResults, ref renderingData.lightData);
RenderTextureDescriptor baseDescriptor = ScriptableRenderer.CreateRenderTextureDescriptor(ref renderingData.cameraData);
RenderTextureDescriptor shadowDescriptor = baseDescriptor;
ClearFlag clearFlag = ScriptableRenderer.GetCameraClearFlag(renderingData.cameraData.camera);
shadowDescriptor.dimension = TextureDimension.Tex2D;
bool requiresRenderToTexture = ScriptableRenderer.RequiresIntermediateColorTexture(ref renderingData.cameraData, baseDescriptor)
|| m_BeforeRenderPasses.Count != 0;
RenderTargetHandle colorHandle = RenderTargetHandle.CameraTarget;
RenderTargetHandle depthHandle = RenderTargetHandle.CameraTarget;
if (requiresRenderToTexture)
{
colorHandle = ColorAttachment;
depthHandle = DepthAttachment;
var sampleCount = (SampleCount)renderingData.cameraData.msaaSamples;
m_CreateLightweightRenderTexturesPass.Setup(baseDescriptor, colorHandle, depthHandle, sampleCount);
renderer.EnqueuePass(m_CreateLightweightRenderTexturesPass);
}
foreach (var pass in m_BeforeRenderPasses)
{
renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle, clearFlag));
}
bool mainLightShadows = false;
if (renderingData.shadowData.supportsMainLightShadows)
{
mainLightShadows = m_MainLightShadowCasterPass.Setup(MainLightShadowmap, ref renderingData);
if (mainLightShadows)
renderer.EnqueuePass(m_MainLightShadowCasterPass);
}
if (renderingData.shadowData.supportsAdditionalLightShadows)
{
bool additionalLightShadows = m_AdditionalLightsShadowCasterPass.Setup(AdditionalLightsShadowmap, ref renderingData, renderer.maxVisibleAdditionalLights);
if (additionalLightShadows)
renderer.EnqueuePass(m_AdditionalLightsShadowCasterPass);
}
bool resolveShadowsInScreenSpace = mainLightShadows && renderingData.shadowData.requiresScreenSpaceShadowResolve;
bool requiresDepthPrepass = resolveShadowsInScreenSpace || renderingData.cameraData.isSceneViewCamera ||
(renderingData.cameraData.requiresDepthTexture && (!CanCopyDepth(ref renderingData.cameraData) || renderingData.cameraData.isOffscreenRender));
// For now VR requires a depth prepass until we figure out how to properly resolve texture2DMS in stereo
requiresDepthPrepass |= renderingData.cameraData.isStereoEnabled;
renderer.EnqueuePass(m_SetupForwardRenderingPass);
if (requiresDepthPrepass)
{
m_DepthOnlyPass.Setup(baseDescriptor, DepthTexture, SampleCount.One);
renderer.EnqueuePass(m_DepthOnlyPass);
foreach (var pass in camera.GetComponents<IAfterDepthPrePass>())
renderer.EnqueuePass(pass.GetPassToEnqueue(m_DepthOnlyPass.descriptor, DepthTexture));
}
if (resolveShadowsInScreenSpace)
{
m_ScreenSpaceShadowResolvePass.Setup(baseDescriptor, ScreenSpaceShadowmap);
renderer.EnqueuePass(m_ScreenSpaceShadowResolvePass);
}
if (renderingData.cameraData.isStereoEnabled)
renderer.EnqueuePass(m_BeginXrRenderingPass);
RendererConfiguration rendererConfiguration = ScriptableRenderer.GetRendererConfiguration(renderingData.lightData.additionalLightsCount);
m_SetupLightweightConstants.Setup(renderer.maxVisibleAdditionalLights, renderer.perObjectLightIndices);
renderer.EnqueuePass(m_SetupLightweightConstants);
// If a before all render pass executed we expect it to clear the color render target
if (m_BeforeRenderPasses.Count != 0)
clearFlag = ClearFlag.None;
m_RenderOpaqueForwardPass.Setup(baseDescriptor, colorHandle, depthHandle, clearFlag, camera.backgroundColor, rendererConfiguration);
renderer.EnqueuePass(m_RenderOpaqueForwardPass);
foreach (var pass in camera.GetComponents<IAfterOpaquePass>())
renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle));
if (renderingData.cameraData.postProcessEnabled &&
renderingData.cameraData.postProcessLayer.HasOpaqueOnlyEffects(renderer.postProcessingContext))
{
m_OpaquePostProcessPass.Setup(baseDescriptor, colorHandle);
renderer.EnqueuePass(m_OpaquePostProcessPass);
foreach (var pass in camera.GetComponents<IAfterOpaquePostProcess>())
renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle));
}
if (camera.clearFlags == CameraClearFlags.Skybox && RenderSettings.skybox != null)
{
m_DrawSkyboxPass.Setup(colorHandle, depthHandle);
renderer.EnqueuePass(m_DrawSkyboxPass);
}
foreach (var pass in camera.GetComponents<IAfterSkyboxPass>())
renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle));
if (renderingData.cameraData.requiresDepthTexture && !requiresDepthPrepass)
{
m_CopyDepthPass.Setup(depthHandle, DepthTexture);
renderer.EnqueuePass(m_CopyDepthPass);
}
if (renderingData.cameraData.requiresOpaqueTexture)
{
m_CopyColorPass.Setup(colorHandle, OpaqueColor);
renderer.EnqueuePass(m_CopyColorPass);
}
m_RenderTransparentForwardPass.Setup(baseDescriptor, colorHandle, depthHandle, rendererConfiguration);
renderer.EnqueuePass(m_RenderTransparentForwardPass);
foreach (var pass in camera.GetComponents<IAfterTransparentPass>())
renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle));
if (renderingData.cameraData.postProcessEnabled)
{
m_TransparentPostProcessPass.Setup(baseDescriptor, colorHandle, BuiltinRenderTextureType.CameraTarget);
renderer.EnqueuePass(m_TransparentPostProcessPass);
}
else if (!renderingData.cameraData.isOffscreenRender && colorHandle != RenderTargetHandle.CameraTarget)
{
m_FinalBlitPass.Setup(baseDescriptor, colorHandle);
renderer.EnqueuePass(m_FinalBlitPass);
}
foreach (var pass in camera.GetComponents<IAfterRender>())
renderer.EnqueuePass(pass.GetPassToEnqueue());
if (renderingData.cameraData.isStereoEnabled)
{
renderer.EnqueuePass(m_EndXrRenderingPass);
}
#if UNITY_EDITOR
if (renderingData.cameraData.isSceneViewCamera)
{
m_SceneViewDepthCopyPass.Setup(DepthTexture);
renderer.EnqueuePass(m_SceneViewDepthCopyPass);
}
#endif
}
bool CanCopyDepth(ref CameraData cameraData)
{
bool msaaEnabledForCamera = (int)cameraData.msaaSamples > 1;
bool supportsTextureCopy = SystemInfo.copyTextureSupport != CopyTextureSupport.None;
bool supportsDepthTarget = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.Depth);
bool supportsDepthCopy = !msaaEnabledForCamera && (supportsDepthTarget || supportsTextureCopy);
// TODO: We don't have support to highp Texture2DMS currently and this breaks depth precision.
// currently disabling it until shader changes kick in.
//bool msaaDepthResolve = msaaEnabledForCamera && SystemInfo.supportsMultisampledTextures != 0;
bool msaaDepthResolve = false;
return supportsDepthCopy || msaaDepthResolve;
}
}
}
這個腳本里描述了LWRP在一幀內所做的所有事情。
1.設置每個物體的燈光信息。(renderer.SetupPerObjectLightIndices)
2.創建管線中即將用到了兩個主要的renderTexture,一個是color texture,一個是depth texture,其中color是用來保存物體渲染的顏色信息,depth 用來保存物體渲染的深度信息 renderer.EnqueuePass(m_CreateLightweightRenderTexturesPass)
3.如果相機上有IBeforeRender相關的組件話,將會執行這些組件所定義的行爲。這一般是開放給開發者,自己定義的某些渲染行爲的接口
4.如果場景裏需要主光源渲染陰影,那麼開始給所有物體繪製陰影。在繪製陰影時,會使用所需要繪製物體的“ShadowCaster” pass進行陰影的渲染。並且,在這一階段,cpu會準備各種渲染陰影所需要的數據,設置各種參數和狀態,供shader使用。
5.如果場景需要渲染AdditionalLightsShadow,那麼就渲染這些陰影。這一過程與上一階段類似。
6.爲LWRP設置攝像機參數。包括攝像機的視口,各種矩陣參數,位置,屏幕信息,以及全局時間屬性等,供後續Shader的使用
7.如果需要處理深度相關的信息,就會開始進入到DepthOnlyPass階段。這一階段主要處理的是RenderQueue爲opaque的物體,然後通過物體材質上的"DepthOnly"的shader,將處理結果保存在名爲“_CameraDepthTexture”的貼圖上。這樣,在後續的階段,我們就可以在shader中直接使用“_CameraDepthTexture”的property來獲取場景中物體的深度信息了
8.如果相機上有IAfterDepthPrePass組件,還會進行這些組件所定義的pass行爲。這些都是開放給開發者去自定義一些深度處理行爲的接口
9.如果處理屏幕空間的陰影,則會進入ScreenSpaceShadowResolvePass階段。這一階段好像就是用一個內置的材質處理了一下得到的屏幕空間遮擋的貼圖。這一階段的作用作者也不是很清楚=。=
10.設置LightWeightConstansPass。顧名思義,就是設置渲染管線裏面的shader所需要的常數。比如我們可以在渲染不透明物體之前執行這個階段來增加許多我們想要在渲染時獲取的常數。
11.渲染不透明物體。這一階段就是真正的將場景裏renderQueue爲opaque的物體渲染到之前創建的color texture上,這一階段使用的Pass是shader中LightMode爲LightweightForward和SRPDefaultUnlit的pass。
12.如果攝像機上有IAfterOpaquePass的組件,則會進行這些組件所定義的pass行爲。這些是開放給開發者自定義行爲的階段
13.如果開啓了不透明物體的後處理相關的行爲(postProcess),會在這一階段處理後處理的pass。後處理效果是unity官方推出的一個提高畫面質量的插件,這裏不做過多描述
14.如果攝像機上有IAfterOpaquePostProcess組件,執行開發者自定義的Pass行爲。
15.如果攝像機渲染天空盒,就進行天空盒的渲染。顏色和深度信息會分別渲染到之前我們所創建的color Texture 和depth Texture上
16.如果攝像機上有IAfterSkyboxPass組件,執行開發者自定義的這個Pass行爲
17.如果需要使用DepthTexture,則把之前渲染得到的深度信息拷貝到深度貼圖上,並設置全局的深度貼圖“_CameraDepthAttachment”屬性供shader使用,此外還會設置一些關鍵詞的狀態。
18.如果需要獲取到OpaqueTexture的貼圖,還會將之前渲染不透明物體得到的color Texture的顏色信息拷貝到另外一個render texture上,這個texture可以用“_CameraOpaqueTexture”屬性獲得到。
19.渲染透明物體。和不透明物體一樣,都使用LightMode爲LightweightForward和SRPDefaultUnlit的pass。但是這個階段只渲染透明物體。最終它也會把渲染結果保存在之前創建的color Texture上。
20.如果攝像機上有IAfterTransparentPass組件,執行開發者自定義的這個Pass行爲
21.如果開啓了透明物體的postProcess相關行爲,在這一階段處理後處理的pass,類似之前不透明的物體。
22.如果沒有開啓透明物體的postProcess行爲,且不是offScreenRender,那麼會進行混合。把 之前我們創建的color Texture的像素信息拷貝到屏幕上,顯示最終這一幀的屏幕圖像。
23.如果攝像機上有IAfterRender的組件,執行開發者自定義的這個Pass行爲。
至此,在遊戲運行中,整個LWRP的渲染步驟結束,最終的一幀畫面就顯示在了屏幕上。
注:具體以源碼爲主,因爲以上的步驟都是對源碼的簡練總結,不一定100%的準確,但是大部分的步驟是正確的。