URP學習之三--ForwardRenderer


上一章講到了URP的RenderSingleCamera函數的Renderer之前,這次我們繼續,開始了解Unity封裝的ForwardRenderer是個什么神秘物體。

為了方便,先把上次講到的方法貼在這里:

 1        public static void RenderSingleCamera(ScriptableRenderContext context, Camera camera)
 2         {
 3             if (!camera.TryGetCullingParameters(IsStereoEnabled(camera), out var cullingParameters))
 4                 return;
 5 
 6             var settings = asset;
 7             UniversalAdditionalCameraData additionalCameraData = null;
 8             if (camera.cameraType == CameraType.Game || camera.cameraType == CameraType.VR)
 9                 camera.gameObject.TryGetComponent(out additionalCameraData);
10 
11             InitializeCameraData(settings, camera, additionalCameraData, out var cameraData);
12             SetupPerCameraShaderConstants(cameraData);
13 
14             ScriptableRenderer renderer = (additionalCameraData != null) ? additionalCameraData.scriptableRenderer : settings.scriptableRenderer;
15             if (renderer == null)
16             {
17                 Debug.LogWarning(string.Format("Trying to render {0} with an invalid renderer. Camera rendering will be skipped.", camera.name));
18                 return;
19             }
20 
21             string tag = (asset.debugLevel >= PipelineDebugLevel.Profiling) ? camera.name: k_RenderCameraTag;
22             CommandBuffer cmd = CommandBufferPool.Get(tag);
23             using (new ProfilingSample(cmd, tag))
24             {
25                 renderer.Clear();
26                 renderer.SetupCullingParameters(ref cullingParameters, ref cameraData);
27 
28                 context.ExecuteCommandBuffer(cmd);
29                 cmd.Clear();
30 
31 #if UNITY_EDITOR
32 
33                 // Emit scene view UI
34                 if (cameraData.isSceneViewCamera)
35                     ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
36 #endif
37 
38                 var cullResults = context.Cull(ref cullingParameters);
39                 InitializeRenderingData(settings, ref cameraData, ref cullResults, out var renderingData);
40 
41                 renderer.Setup(context, ref renderingData);
42                 renderer.Execute(context, ref renderingData);
43             }
44 
45             context.ExecuteCommandBuffer(cmd);
46             CommandBufferPool.Release(cmd);
47             context.Submit();

我們從line 14開始學習:

我們可以看到Renderer是從相機參數中獲取的,即這個位置:

 

 這也就意味着我們可以一個相機一個Renderer,比如A相機Forward,B相機Deffered(當然,前提時支持多相機,目前可以在git上pull到camerastack,然而沒有了depth only的flag,這里不是重點)

接下來就是Renderer對象執行Clear方法,我們看一下ForwardRenderer中有哪些東西:

const int k_DepthStencilBufferBits = 32;
        const string k_CreateCameraTextures = "Create Camera Texture";

        ColorGradingLutPass m_ColorGradingLutPass;
        DepthOnlyPass m_DepthPrepass;
        MainLightShadowCasterPass m_MainLightShadowCasterPass;
        AdditionalLightsShadowCasterPass m_AdditionalLightsShadowCasterPass;
        ScreenSpaceShadowResolvePass m_ScreenSpaceShadowResolvePass;
        DrawObjectsPass m_RenderOpaqueForwardPass;
        DrawSkyboxPass m_DrawSkyboxPass;
        CopyDepthPass m_CopyDepthPass;
        CopyColorPass m_CopyColorPass;
        DrawObjectsPass m_RenderTransparentForwardPass;
        InvokeOnRenderObjectCallbackPass m_OnRenderObjectCallbackPass;
        PostProcessPass m_PostProcessPass;
        PostProcessPass m_FinalPostProcessPass;
        FinalBlitPass m_FinalBlitPass;
        CapturePass m_CapturePass;

撲面而來的就是一堆Pass,熟悉的味道,還是按照Pass組織渲染,大概看了以下Pass的種類,基本上了解了可以做的操作和比之前優化的地方,總的來說和之前內置管線差的不太多。

先讓這些Pass來這里混個眼熟,之后會頻繁提到(哈哈,其實是不知道放在哪個位置好)

然后我們回到剛才那個Renderer的Clear方法(在Renderer基類中):

internal void Clear()
    {
      this.m_CameraColorTarget = (RenderTargetIdentifier) BuiltinRenderTextureType.CameraTarget;
      this.m_CameraDepthTarget = (RenderTargetIdentifier) BuiltinRenderTextureType.CameraTarget;
      ScriptableRenderer.m_ActiveColorAttachment = (RenderTargetIdentifier) BuiltinRenderTextureType.CameraTarget;
      ScriptableRenderer.m_ActiveDepthAttachment = (RenderTargetIdentifier) BuiltinRenderTextureType.CameraTarget;
      this.m_FirstCameraRenderPassExecuted = false;
      ScriptableRenderer.m_InsideStereoRenderBlock = false;
      this.m_ActiveRenderPassQueue.Clear();
    }

主要進行的操作時重置各種渲染對象以及執行狀態,清空了Pass隊列。

之后就是SetupCullingParameters方法:

public override void SetupCullingParameters(ref ScriptableCullingParameters cullingParameters,
            ref CameraData cameraData)
        {
            Camera camera = cameraData.camera;
            // TODO: PerObjectCulling also affect reflection probes. Enabling it for now.
            // if (asset.additionalLightsRenderingMode == LightRenderingMode.Disabled ||
            //     asset.maxAdditionalLightsCount == 0)
            // {
            //     cullingParameters.cullingOptions |= CullingOptions.DisablePerObjectCulling;
            // }

            // If shadow is disabled, disable shadow caster culling
            if (Mathf.Approximately(cameraData.maxShadowDistance, 0.0f))
                cullingParameters.cullingOptions &= ~CullingOptions.ShadowCasters;

            cullingParameters.shadowDistance = cameraData.maxShadowDistance;
        }

可以看到基本上是對於是否有reflection probe和有shadow情況下的culling處理。

接下來調用cull方法就不說了,SRP篇講過,再往后就是InitializeRenderingData方法:

static void InitializeRenderingData(UniversalRenderPipelineAsset settings, ref CameraData cameraData, ref CullingResults cullResults,
            out RenderingData renderingData)
        {
            var visibleLights = cullResults.visibleLights;

            int mainLightIndex = GetMainLightIndex(settings, visibleLights);
            bool mainLightCastShadows = false;
            bool additionalLightsCastShadows = false;

            if (cameraData.maxShadowDistance > 0.0f)
            {
                mainLightCastShadows = (mainLightIndex != -1 && visibleLights[mainLightIndex].light != null &&
                                        visibleLights[mainLightIndex].light.shadows != LightShadows.None);

                // If additional lights are shaded per-pixel they cannot cast shadows
                if (settings.additionalLightsRenderingMode == LightRenderingMode.PerPixel)
                {
                    for (int i = 0; i < visibleLights.Length; ++i)
                    {
                        if (i == mainLightIndex)
                            continue;

                        Light light = visibleLights[i].light;

                        // LWRP doesn't support additional directional lights or point light shadows yet
                        if (visibleLights[i].lightType == LightType.Spot && light != null && light.shadows != LightShadows.None)
                        {
                            additionalLightsCastShadows = true;
                            break;
                        }
                    }
                }
            }

            renderingData.cullResults = cullResults;
            renderingData.cameraData = cameraData;
            InitializeLightData(settings, visibleLights, mainLightIndex, out renderingData.lightData);
            InitializeShadowData(settings, visibleLights, mainLightCastShadows, additionalLightsCastShadows && !renderingData.lightData.shadeAdditionalLightsPerVertex, out renderingData.shadowData);
            InitializePostProcessingData(settings, out renderingData.postProcessingData);
            renderingData.supportsDynamicBatching = settings.supportsDynamicBatching;
            renderingData.perObjectData = GetPerObjectLightFlags(renderingData.lightData.additionalLightsCount);

            bool isOffscreenCamera = cameraData.camera.targetTexture != null && !cameraData.isSceneViewCamera;
            renderingData.killAlphaInFinalBlit = !Graphics.preserveFramebufferAlpha && PlatformNeedsToKillAlpha() && !isOffscreenCamera;
        }

代碼前半部分是在處理要不要陰影,如果沒找到主光,不要,主光以外目前只支持SpotLight的陰影。

后半部分基本上就是在填充renderingData的內容,其中需要注意的幾個方法

1.InitializeLightData方法:

static void InitializeLightData(UniversalRenderPipelineAsset settings, NativeArray<VisibleLight> visibleLights, int mainLightIndex, out LightData lightData)
        {
            int maxPerObjectAdditionalLights = UniversalRenderPipeline.maxPerObjectLights;
            int maxVisibleAdditionalLights = UniversalRenderPipeline.maxVisibleAdditionalLights;

            lightData.mainLightIndex = mainLightIndex;

            if (settings.additionalLightsRenderingMode != LightRenderingMode.Disabled)
            {
                lightData.additionalLightsCount =
                    Math.Min((mainLightIndex != -1) ? visibleLights.Length - 1 : visibleLights.Length,
                        maxVisibleAdditionalLights);
                lightData.maxPerObjectAdditionalLightsCount = Math.Min(settings.maxAdditionalLightsCount, maxPerObjectAdditionalLights);
            }
            else
            {
                lightData.additionalLightsCount = 0;
                lightData.maxPerObjectAdditionalLightsCount = 0;
            }

            lightData.shadeAdditionalLightsPerVertex = settings.additionalLightsRenderingMode == LightRenderingMode.PerVertex;
            lightData.visibleLights = visibleLights;
            lightData.supportsMixedLighting = settings.supportsMixedLighting;
        }

我們可以看到對於每個物體可以接受多少盞燈光的限制,UniversalRenderPipeline.maxPerObjectLights屬性限定了不同Level每個物體接受燈光數量的上限,如下:

public static int maxPerObjectLights
        {
            // No support to bitfield mask and int[] in gles2. Can't index fast more than 4 lights.
            // Check Lighting.hlsl for more details.
            get => (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2) ? 4 : 8;
        }

GLES 2最多四盞燈 其余最多八盞燈。

2.InitializeShadowData方法

        static void InitializeShadowData(UniversalRenderPipelineAsset settings, NativeArray<VisibleLight> visibleLights, bool mainLightCastShadows, bool additionalLightsCastShadows, out ShadowData shadowData)
        {
            m_ShadowBiasData.Clear();

            for (int i = 0; i < visibleLights.Length; ++i)
            {
                Light light = visibleLights[i].light;
                UniversalAdditionalLightData data = null;
                if (light != null)
                {
#if UNITY_2019_3_OR_NEWER
                    light.gameObject.TryGetComponent(out data);
#else
                    data = light.gameObject.GetComponent<LWRPAdditionalLightData>();
#endif
                }

                if (data && !data.usePipelineSettings)
                    m_ShadowBiasData.Add(new Vector4(light.shadowBias, light.shadowNormalBias, 0.0f, 0.0f));
                else
                    m_ShadowBiasData.Add(new Vector4(settings.shadowDepthBias, settings.shadowNormalBias, 0.0f, 0.0f));
            }

            shadowData.bias = m_ShadowBiasData;

            // Until we can have keyword stripping forcing single cascade hard shadows on gles2
            bool supportsScreenSpaceShadows = SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2;

            shadowData.supportsMainLightShadows = SystemInfo.supportsShadows && settings.supportsMainLightShadows && mainLightCastShadows;

            // we resolve shadows in screenspace when cascades are enabled to save ALU as computing cascade index + shadowCoord on fragment is expensive
            shadowData.requiresScreenSpaceShadowResolve = shadowData.supportsMainLightShadows && supportsScreenSpaceShadows && settings.shadowCascadeOption != ShadowCascadesOption.NoCascades;

            int shadowCascadesCount;
            switch (settings.shadowCascadeOption)
            {
                case ShadowCascadesOption.FourCascades:
                    shadowCascadesCount = 4;
                    break;

                case ShadowCascadesOption.TwoCascades:
                    shadowCascadesCount = 2;
                    break;

                default:
                    shadowCascadesCount = 1;
                    break;
            }

            shadowData.mainLightShadowCascadesCount = (shadowData.requiresScreenSpaceShadowResolve) ? shadowCascadesCount : 1;
            shadowData.mainLightShadowmapWidth = settings.mainLightShadowmapResolution;
            shadowData.mainLightShadowmapHeight = settings.mainLightShadowmapResolution;

            switch (shadowData.mainLightShadowCascadesCount)
            {
                case 1:
                    shadowData.mainLightShadowCascadesSplit = new Vector3(1.0f, 0.0f, 0.0f);
                    break;

                case 2:
                    shadowData.mainLightShadowCascadesSplit = new Vector3(settings.cascade2Split, 1.0f, 0.0f);
                    break;

                default:
                    shadowData.mainLightShadowCascadesSplit = settings.cascade4Split;
                    break;
            }

            shadowData.supportsAdditionalLightShadows = SystemInfo.supportsShadows && settings.supportsAdditionalLightShadows && additionalLightsCastShadows;
            shadowData.additionalLightsShadowmapWidth = shadowData.additionalLightsShadowmapHeight = settings.additionalLightsShadowmapResolution;
            shadowData.supportsSoftShadows = settings.supportsSoftShadows && (shadowData.supportsMainLightShadows || shadowData.supportsAdditionalLightShadows);
            shadowData.shadowmapDepthBufferBits = 16;
        }

里面需要注意的幾點:默認的光照數據統一從pipelineasset中讀,如果需要特殊的光照數據,需要在對應的光加上UniversalAdditionalLightData組件;

          屏幕空間陰影需要設備GLES 2以上才支持

          陰影質量由shadowmap分辨率和cascade數共同決定

3.InitializePostProcessingData方法:

static void InitializePostProcessingData(UniversalRenderPipelineAsset settings, out PostProcessingData postProcessingData)
        {
            postProcessingData.gradingMode = settings.supportsHDR
                ? settings.colorGradingMode
                : ColorGradingMode.LowDynamicRange;

            postProcessingData.lutSize = settings.colorGradingLutSize;
        }

這個方法很簡短,主要是針對ColorGrading的相關參數。后面的調用基本上都是數據賦值,我們跳過。

接下來就是renderer.Setup(context, ref renderingData)方法

        /// <inheritdoc />
        public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            Camera camera = renderingData.cameraData.camera;
            ref CameraData cameraData = ref renderingData.cameraData;
            RenderTextureDescriptor cameraTargetDescriptor = renderingData.cameraData.cameraTargetDescriptor;

            // Special path for depth only offscreen cameras. Only write 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;
            }

            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.
            bool requiresDepthPrepass = renderingData.cameraData.isSceneViewCamera ||
                (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 (cameraData.isStereoEnabled && 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.
            bool createDepthTexture = cameraData.requiresDepthTexture && !requiresDepthPrepass;
            bool postProcessEnabled = cameraData.postProcessEnabled;

            m_ActiveCameraColorAttachment = (createColorTexture) ? m_CameraColorAttachment : RenderTargetHandle.CameraTarget;
            m_ActiveCameraDepthAttachment = (createDepthTexture) ? m_CameraDepthAttachment : RenderTargetHandle.CameraTarget;
            bool intermediateRenderTexture = createColorTexture || createDepthTexture;
            
            if (intermediateRenderTexture)
                CreateCameraRenderTarget(context, ref cameraData);

            ConfigureCameraTarget(m_ActiveCameraColorAttachment.Identifier(), m_ActiveCameraDepthAttachment.Identifier());

            // if rendering to intermediate render texture we don't have to create msaa backbuffer
            int backbufferMsaaSamples = (intermediateRenderTexture) ? 1 : cameraTargetDescriptor.msaaSamples;
            
            if (Camera.main == camera && camera.cameraType == CameraType.Game && camera.targetTexture == null)
                SetupBackbufferFormat(backbufferMsaaSamples, renderingData.cameraData.isStereoEnabled);
            
            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);
            }
            bool hasAfterRendering = activeRenderPassQueue.Find(x => x.renderPassEvent == RenderPassEvent.AfterRendering) != null;

            if (mainLightShadows)
                EnqueuePass(m_MainLightShadowCasterPass);

            if (additionalLightShadows)
                EnqueuePass(m_AdditionalLightsShadowCasterPass);

            if (requiresDepthPrepass)
            {
                m_DepthPrepass.Setup(cameraTargetDescriptor, m_DepthTexture);
                EnqueuePass(m_DepthPrepass);
            }

            if (resolveShadowsInScreenSpace)
            {
                m_ScreenSpaceShadowResolvePass.Setup(cameraTargetDescriptor);
                EnqueuePass(m_ScreenSpaceShadowResolvePass);
            }

            if (postProcessEnabled)
            {
                m_ColorGradingLutPass.Setup(m_ColorGradingLut);
                EnqueuePass(m_ColorGradingLutPass);
            }

            EnqueuePass(m_RenderOpaqueForwardPass);

            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 = UniversalRenderPipeline.asset.opaqueDownsampling;
                m_CopyColorPass.Setup(m_ActiveCameraColorAttachment.Identifier(), m_OpaqueColor, downsamplingMethod);
                EnqueuePass(m_CopyColorPass);
            }

            EnqueuePass(m_RenderTransparentForwardPass);
            EnqueuePass(m_OnRenderObjectCallbackPass);

            bool afterRenderExists = renderingData.cameraData.captureActions != null ||
                                     hasAfterRendering;

            bool requiresFinalPostProcessPass = postProcessEnabled &&
                                     renderingData.cameraData.antialiasing == AntialiasingMode.FastApproximateAntialiasing;

            // if we have additional filters
            // we need to stay in a RT
            if (afterRenderExists)
            {
                bool willRenderFinalPass = (m_ActiveCameraColorAttachment != RenderTargetHandle.CameraTarget);
                // perform post with src / dest the same
                if (postProcessEnabled)
                {
                    m_PostProcessPass.Setup(cameraTargetDescriptor, m_ActiveCameraColorAttachment, m_AfterPostProcessColor, m_ActiveCameraDepthAttachment, m_ColorGradingLut, requiresFinalPostProcessPass, !willRenderFinalPass);
                    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);
                    }

                    if (requiresFinalPostProcessPass)
                    {
                        m_FinalPostProcessPass.SetupFinalPass(m_ActiveCameraColorAttachment);
                        EnqueuePass(m_FinalPostProcessPass);
                    }
                    else
                    {
                        m_FinalBlitPass.Setup(cameraTargetDescriptor, m_ActiveCameraColorAttachment);
                        EnqueuePass(m_FinalBlitPass);
                    }
                }
            }
            else
            {
                if (postProcessEnabled)
                {
                    if (requiresFinalPostProcessPass)
                    {
                        m_PostProcessPass.Setup(cameraTargetDescriptor, m_ActiveCameraColorAttachment, m_AfterPostProcessColor, m_ActiveCameraDepthAttachment, m_ColorGradingLut, true, false);
                        EnqueuePass(m_PostProcessPass);
                        m_FinalPostProcessPass.SetupFinalPass(m_AfterPostProcessColor);
                        EnqueuePass(m_FinalPostProcessPass);
                    }
                    else
                    {
                        m_PostProcessPass.Setup(cameraTargetDescriptor, m_ActiveCameraColorAttachment, RenderTargetHandle.CameraTarget, m_ActiveCameraDepthAttachment, m_ColorGradingLut, false, true);
                        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
        }

這個方法最主要的作用就是根據配置確定是否加入對應的Pass參與渲染,首先我們看到代碼:

如果是渲染深度紋理的相機,則只加入了RenderFeature、不透明物體、天空盒、半透明物體的Pass(RenderFeature就是自定義的一些Pass,之后會講到)

接下來是ShadowCasterPass的數據SetUp,根據返回值確定是否開啟屏幕空間陰影。之后根據諸多因素確定是否需要predepthpass。

而后就是判斷是否需要ColorTexture(繪制了不透明物體和天空盒的RT),再往后是對msaa的影響。

接下來加入了許多Pass到渲染隊列:兩個ShadowCaster(主光和非主光),DepthPrePass,ScreenSpaceShadowResolvePass,ColorGradingLutPass,RenderOpaqueForwardPass,DrawSkyboxPass,CopyDepthPass,CopyColorPass,TransparentForwardPass,RenderObjectCallbackPass,PostProcessPass,CapturePass,FinalBlitPass,每個pass是否加入渲染隊列基本上都有對應的條件,具體每個pass在做什么事情其實光看名字就清楚了,思路和默認管線基本上差不多。但是考慮到之后對URP的擴展和熟練運用,這幾個pass之后還會細細的講一下。

最后就到了Execute方法執行了:

public void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
      Camera camera = renderingData.cameraData.camera;
      this.SetCameraRenderState(context, ref renderingData.cameraData);
      ScriptableRenderer.SortStable(this.m_ActiveRenderPassQueue);
      float time = Application.isPlaying ? Time.time : Time.realtimeSinceStartup;
      float deltaTime = Time.deltaTime;
      float smoothDeltaTime = Time.smoothDeltaTime;
      this.SetShaderTimeValues(time, deltaTime, smoothDeltaTime, (CommandBuffer) null);
      NativeArray<RenderPassEvent> blockEventLimits = new NativeArray<RenderPassEvent>(3, Allocator.Temp, NativeArrayOptions.ClearMemory);
      blockEventLimits[ScriptableRenderer.RenderPassBlock.BeforeRendering] = RenderPassEvent.BeforeRenderingPrepasses;
      blockEventLimits[ScriptableRenderer.RenderPassBlock.MainRendering] = RenderPassEvent.AfterRenderingPostProcessing;
      blockEventLimits[ScriptableRenderer.RenderPassBlock.AfterRendering] = (RenderPassEvent) 2147483647;
      NativeArray<int> blockRanges = new NativeArray<int>(blockEventLimits.Length + 1, Allocator.Temp, NativeArrayOptions.ClearMemory);
      this.FillBlockRanges(blockEventLimits, blockRanges);
      blockEventLimits.Dispose();
      this.SetupLights(context, ref renderingData);
      this.ExecuteBlock(ScriptableRenderer.RenderPassBlock.BeforeRendering, blockRanges, context, ref renderingData, false);
      bool isStereoEnabled = renderingData.cameraData.isStereoEnabled;
      context.SetupCameraProperties(camera, isStereoEnabled);
      this.SetShaderTimeValues(time, deltaTime, smoothDeltaTime, (CommandBuffer) null);
      if (isStereoEnabled)
        this.BeginXRRendering(context, camera);
      this.ExecuteBlock(ScriptableRenderer.RenderPassBlock.MainRendering, blockRanges, context, ref renderingData, false);
      this.DrawGizmos(context, camera, GizmoSubset.PreImageEffects);
      this.ExecuteBlock(ScriptableRenderer.RenderPassBlock.AfterRendering, blockRanges, context, ref renderingData, false);
      if (isStereoEnabled)
        this.EndXRRendering(context, camera);
      this.DrawGizmos(context, camera, GizmoSubset.PostImageEffects);
      this.InternalFinishRendering(context);
      blockRanges.Dispose();
    }

首先設置相機渲染狀態,SetCameraRenderState方法:

private void SetCameraRenderState(ScriptableRenderContext context, ref CameraData cameraData)
    {
      CommandBuffer commandBuffer = CommandBufferPool.Get("Clear Render State");
      commandBuffer.DisableShaderKeyword(ShaderKeywordStrings.MainLightShadows);
      commandBuffer.DisableShaderKeyword(ShaderKeywordStrings.MainLightShadowCascades);
      commandBuffer.DisableShaderKeyword(ShaderKeywordStrings.AdditionalLightsVertex);
      commandBuffer.DisableShaderKeyword(ShaderKeywordStrings.AdditionalLightsPixel);
      commandBuffer.DisableShaderKeyword(ShaderKeywordStrings.AdditionalLightShadows);
      commandBuffer.DisableShaderKeyword(ShaderKeywordStrings.SoftShadows);
      commandBuffer.DisableShaderKeyword(ShaderKeywordStrings.MixedLightingSubtractive);
      VolumeManager.instance.Update(cameraData.volumeTrigger, cameraData.volumeLayerMask);
      context.ExecuteCommandBuffer(commandBuffer);
      CommandBufferPool.Release(commandBuffer);
    }

我們可以看到主要是重置了一些keyword。接下來就是對pass隊列進行排序,排序規則按照每個pass的renderPassEvent大小來進行,event枚舉如下:

 public enum RenderPassEvent
  {
    BeforeRendering = 0,
    BeforeRenderingShadows = 50, // 0x00000032
    AfterRenderingShadows = 100, // 0x00000064
    BeforeRenderingPrepasses = 150, // 0x00000096
    AfterRenderingPrePasses = 200, // 0x000000C8
    BeforeRenderingOpaques = 250, // 0x000000FA
    AfterRenderingOpaques = 300, // 0x0000012C
    BeforeRenderingSkybox = 350, // 0x0000015E
    AfterRenderingSkybox = 400, // 0x00000190
    BeforeRenderingTransparents = 450, // 0x000001C2
    AfterRenderingTransparents = 500, // 0x000001F4
    BeforeRenderingPostProcessing = 550, // 0x00000226
    AfterRenderingPostProcessing = 600, // 0x00000258
    AfterRendering = 1000, // 0x000003E8
  }

接下來就是統一全局時間,之后將渲染事件整合成三個階段:渲染前,主渲染,渲染后,然后SetUp燈光:

public void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
    {
      int additionalLightsCount = renderingData.lightData.additionalLightsCount;
      bool additionalLightsPerVertex = renderingData.lightData.shadeAdditionalLightsPerVertex;
      CommandBuffer commandBuffer = CommandBufferPool.Get("Setup Light Constants");
      this.SetupShaderLightConstants(commandBuffer, ref renderingData);
      CoreUtils.SetKeyword(commandBuffer, ShaderKeywordStrings.AdditionalLightsVertex, additionalLightsCount > 0 & additionalLightsPerVertex);
      CoreUtils.SetKeyword(commandBuffer, ShaderKeywordStrings.AdditionalLightsPixel, additionalLightsCount > 0 && !additionalLightsPerVertex);
      CoreUtils.SetKeyword(commandBuffer, ShaderKeywordStrings.MixedLightingSubtractive, renderingData.lightData.supportsMixedLighting && this.m_MixedLightingSetup == MixedLightingSetup.Subtractive);
      context.ExecuteCommandBuffer(commandBuffer);
      CommandBufferPool.Release(commandBuffer);
    }

這是ForwardRenderer中一個ForwardLights類的一個方法,主要設置了shader光照常量和光照相關keyword。

接下來是執行渲染事件:

private void ExecuteBlock(
      int blockIndex,
      NativeArray<int> blockRanges,
      ScriptableRenderContext context,
      ref RenderingData renderingData,
      bool submit = false)
    {
      int blockRange1 = blockRanges[blockIndex + 1];
      for (int blockRange2 = blockRanges[blockIndex]; blockRange2 < blockRange1; ++blockRange2)
      {
        ScriptableRenderPass activeRenderPass = this.m_ActiveRenderPassQueue[blockRange2];
        this.ExecuteRenderPass(context, activeRenderPass, ref renderingData);
      }
      if (!submit)
        return;
      context.Submit();
    }

可以看到執行了對應的渲染隊列Pass的ExecuteRenderPass方法,最后submit

從這里可以看出,AfterRendering、MainRendering、BeforeRendering每個渲染事件執行完畢都會進行一次提交。

最后執行InternalFinishRendering方法完成渲染:

    private void InternalFinishRendering(ScriptableRenderContext context)
    {
      CommandBuffer commandBuffer = CommandBufferPool.Get("Release Resources");
      for (int index = 0; index < this.m_ActiveRenderPassQueue.Count; ++index)
        this.m_ActiveRenderPassQueue[index].FrameCleanup(commandBuffer);
      this.FinishRendering(commandBuffer);
      this.Clear();
      context.ExecuteCommandBuffer(commandBuffer);
      CommandBufferPool.Release(commandBuffer);
    }

主要cleanup了一下所有的pass,釋放了RT,重置渲染對象,清空pass隊列。至此,整個URP的流程我們大致過了一遍,到現在為止我們看到的內容,主要都是URP的一個結構,核心內容都在每一個Pass中,下節我們會依次講一講Pass中的內容。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM