URP学习之三--ForwardRenderer

Posted shenyibo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,之后会讲到)

但是理论上来说,一般渲染深度纹理是不带半透物体的,半透物体也不会写入深度,但这里带上半透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中的内容。

以上是关于URP学习之三--ForwardRenderer的主要内容,如果未能解决你的问题,请参考以下文章

URP学习之二--初识URP

uboot学习之二----主Makefile学习之三----静默编译

javascript学习之this

Python 学习之《Learn Python3 The Hard Way 》第三部分学习笔记

AspectJ基础学习之三HelloWorld(转载)

spring 学习之三(spring 与hibernate, struts2整合)