Unity 可编程渲染管线 LWRP 代码详解

RenderPipeLine

Unity渲染管线

渲染管线:

将对象显示到屏幕上所需要的一些流程及技术的总称。

例如:多通道渲染,单通道渲染,延迟渲染。

为什么需要可编程渲染管线:

Unity自带的渲染管道只有Forward和Deferred两种。各有优缺点。
不开源。

可编程渲染管线:

如何使用:需要定一个类,继承RenderPipeline, 并且重写其中的Render方法
渲染管线上下文:Render方法中参数之一,ScriptableRenderContext,可以编写一些缓冲区指令,并且用context去执行这些cmd.

CommandBuffer

CommandBuffer携带一系列的渲染命令,依赖相机,用来拓展渲染管线的渲染效果。
而且可以指定在相机渲染的某个点执行本身的拓展渲染。如上述Unity渲染管线图片,我们可以根据参数指定CommandBuffer在绿色的点上添加执行命令。

Command buffers也可以结合屏幕后期效果使用。 用于拓展Unity渲染管线。包含一系列渲染命令,比如设置渲染目标,绘制网格等,并可以设置为在摄像机期间的各个点执行渲染。

本文所用的Unity版本和LWRP的package版本分别是:

主要讲解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);
        }

一个Rendering Loop过程

整体结构非常简单,设置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();
#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
        }

你可能感兴趣的:(Unity 可编程渲染管线 LWRP 代码详解)