SRP--定制管线

目录

[TOC]

一、创建管道

1.项目设置

“Edit–Project Settings–Player–Other Settings–Rendering–Color Space”中设置颜色空间为线性空间(默认设置为Gamma空间)。

2.创建管道资产

(1)添加创建管道资产的功能–编辑器扩展

使用的API:

  • ” : RenderPipelineAsset”:

    该类需继承自”RenderPipelineAsset”才可创建渲染管线资产。

  • “using UnityEngine.Experimental.Rendering;”:

    若要继承”RenderPipelineAsset”需引入”UnityEngine.Experimental.Rendering”命名空间。该空间在2018.3版中尚属实验性。

  • “protected override IRenderPipeline InternalCreatePipeline()”:

    创建渲染管线。继承自”RenderPipelineAsset”后必须实现的方法,返回值为创建的渲染管线实例。渲染管线实例负责渲染过程。

  • “[CreateAssetMenu(fileName = “My Pipeline”, menuName = “Rendering/My Pipeline”, order = 1)]”:

    特性,添加此特性后,在编辑模式中可以通过鼠标右键创建资产文件。

using UnityEngine;
using UnityEngine.Experimental.Rendering;

namespace StudySRP
{
    [CreateAssetMenu(fileName = "My Pipeline", menuName = "Rendering/My Pipeline", order = 1)]
    public class MyPipelineAsset : RenderPipelineAsset
    {
        /// <summary>
        /// 创建管道
        /// </summary>
        /// <returns>渲染管线实例</returns>
        protected override IRenderPipeline InternalCreatePipeline()
        {
            return null;
        }
    }
}

(2)创建管道资产

鼠标右键–Create–Rendering–My Pipeline“即可创建管道资产

picture0

(3)设置自定义管道资产

将上述创建的管道资产拖拽到”Edit–Project Settings–Graphics–Scriptable Render Pipeline Settings”中。

picture1

设置好了之后,场景和游戏视图都不再绘制任何内容,因为此时使用的是自定义的渲染管线,而自定义的渲染管线中暂时还什么都没有。

(4)创建渲染管线实例

创建管道资产后,还需要为管道资产创建一个渲染管线实例。渲染管线实例负责渲染过程,可以继承自”IRenderPipeline”接口,也可以直接继承自”RenderPipeline”(一个”IRenderPipeline”的基本实现)。

using UnityEngine.Experimental.Rendering;

namespace StudySRP
{
    public class MyPipeline : RenderPipeline
    {

    }
}

在创建管道资产的方法中返回此渲染管线实例:

using UnityEngine;
using UnityEngine.Experimental.Rendering;

namespace StudySRP
{
    [CreateAssetMenu(fileName = "My Pipeline", menuName = "Rendering/My Pipeline", order = 1)]
    public class MyPipelineAsset : RenderPipelineAsset
    {
        /// <summary>
        /// 创建管道
        /// </summary>
        /// <returns>渲染管线实例</returns>
        protected override IRenderPipeline InternalCreatePipeline()
        {
            return new MyPipeline();
        }
    }
}

3.自定义渲染管线的渲染流程

(1)渲染上下文和摄像机

使用的API:

  • “public override void Render(ScriptableRenderContext renderContext, Camera[] cameras)”:

    在自定义渲染管线脚本”MyPipeline”中重写渲染方法。其中”ScriptableRenderContext”为渲染上下文,使用渲染上下文可以向Unity发出指令(渲染事物/控制事物的渲染状态);”Camera[]”为需要渲染的所有相机的数组。

    此函数不会绘制任何内容,但会检查渲染管线是否有效用于渲染,若无效,则会报一个异常。重写此方法,并调用基本实现,以保留此检查。

        /// <summary>
        /// 此函数不会绘制任何内容,但会检查渲染管线是否有效用于渲染。
        /// 若无效,则会报一个异常。
        /// 覆盖此方法,并调用基本实现,以保留此检查。
        /// </summary>
        /// <param 渲染上下文="renderContext"></param>
        /// <param 需要渲染的所有相机的数组="cameras"></param>
        public override void Render(ScriptableRenderContext renderContext, Camera[] cameras)
        {
            base.Render(renderContext, cameras);
            //遍历执行每个摄像机的渲染
            for (int i = 0; i < cameras.Length; i++)
            {
                Render(renderContext, cameras[i]);
            }
        }
        /// <summary>
        /// 负责一个摄像机的渲染
        /// </summary>
        /// <param 渲染上下文="renderContext"></param>
        /// <param 需要渲染的摄像机="camera"></param>
        private void Render(ScriptableRenderContext context, Camera camera)
        {
            //使用渲染上下文可以向Unity发出指令(渲染事物/控制事物的渲染状态)
            //此指令被存放于渲染上下文的内部缓冲中,需提交后Unity才执行。
        }
    }

(2)绘制天空盒

使用的API:

  • “context.SetupCameraProperties(camera)”:

    设置好摄像机属性–VP矩阵,只有设置好VP矩阵才能正确绘制天空盒。

  • “context.DrawSkybox(camera)”:

    向Unity发出绘制天空盒的指令,指令不会立即执行,会存放于渲染上下文的内部缓冲中,只有提交后才会真正执行此指令。

  • “context.Submit()”:

    提交渲染上下文的内部缓冲中的指令。

        /// <summary>
        /// 负责一个摄像机的渲染
        /// </summary>
        /// <param 渲染上下文="renderContext"></param>
        /// <param 需要渲染的摄像机="camera"></param>
        private void Render(ScriptableRenderContext context, Camera camera)
        {
            //使用渲染上下文可以向Unity发出指令(渲染事物/控制事物的渲染状态)
            //此指令被存放于渲染上下文的内部缓冲中,需提交后Unity才执行。

            //设置摄像机属性--VP矩阵
            context.SetupCameraProperties(camera);

            //1.绘制天空盒:
            context.DrawSkybox(camera);

            //提交渲染上下文的内部缓冲中的指令
            context.Submit();
        }

如果没有正确设置摄像机属性–VP矩阵,则天空盒不会被正确绘制,如下图所示:

picture2

此时,查看Unity的帧调试,可以查看到错误的VP矩阵:

picture3

渲染上下文通过”SetupCameraProperties(camera)”正确设置摄像机属性–VP矩阵后,天空盒被正确绘制出来:

picture4

此时,帧调试中显示了正确的VP矩阵:

picture5

(3)命令缓冲区

绘制命令需通过命令缓冲区提交到渲染上下文的内部缓冲区,然后执行。

使用的API:

  • “CommandBuffer buffer = new CommandBuffer()”:

    构建命令缓冲对象。

  • “CameraClearFlags clearFlags = camera.clearFlags”:

    获取指定摄像机的清除标志,如是否清除深度信息、颜色信息。

  • “buffer.ClearRenderTarget(是否清除深度信息 , 是否清除颜色信息 , 背景颜色)”:

    命令缓冲区的命令–清除渲染目标。

  • “context.ExecuteCommandBuffer(buffer)”:

    执行命令缓冲区的命令,此处不会立即执行,会先提交到渲染上下文的内部缓冲中

  • “buffer.Release()”:

    释放命令缓冲,当将命令缓冲提交到渲染上下文的内部缓冲后,应立即释放命令缓冲。

        /// <summary>
        /// 负责一个摄像机的渲染
        /// </summary>
        /// <param 渲染上下文="renderContext"></param>
        /// <param 需要渲染的摄像机="camera"></param>
        private void Render(ScriptableRenderContext context, Camera camera)
        {
		......
            //设置摄像机属性--VP矩阵
            context.SetupCameraProperties(camera);

            //命令缓冲区
            CommandBuffer buffer = new CommandBuffer
            {
                name = camera.name,//命令缓冲区的名字为摄像机的名字
            };
            //获取当前摄像机的清除标志
            //清除标志中每个"位"用于指示是否启用某个功能
            CameraClearFlags clearFlags = camera.clearFlags;
            //设置命令缓冲区中清除渲染目标的命令
            //第一个参数:是否清除深度信息
            //第二个参数:是否清除颜色信息
            //第三个参数:背景颜色
            //使用"位与"运算:前后相同则为1
            buffer.ClearRenderTarget
            (
                (clearFlags & CameraClearFlags.Depth) != 0,
                (clearFlags & CameraClearFlags.Color) != 0,
                camera.backgroundColor
            );
            //执行命令缓冲中的命令
            //不会立即执行,会先提交到渲染上下文的内部缓冲中
            context.ExecuteCommandBuffer(buffer);
            //释放命令缓冲
            buffer.Release();
		......
        }

在帧调试中可以看到执行的命令缓冲:清除了渲染目标,Z(深度缓冲区)和stencil(模板缓冲区)被清除。

注意:模板缓冲区不需要设置是否清除,因为它总是被清除。

picture6

(4)渲染剔除

在渲染之前,应该剔除不在摄像机渲染范围内的物体,只保留可渲染的物体信息。

使用的API:

  • “ScriptableCullingParameters”:

    剔除参数,一个结构体,包含摄像机的设置、矩阵等。

  • “CullResults.GetCullingParameters(camera, out cullingParameters)”:

    获取指定摄像机下的剔除参数。

  • “CullResults cull = CullResults.Cull(ref cullingParameters, context)”:

    根据渲染上下文以及剔除参数获取可见内容的信息。

        /// <summary>
        /// 负责一个摄像机的渲染
        /// </summary>
        /// <param 渲染上下文="renderContext"></param>
        /// <param 需要渲染的摄像机="camera"></param>
        private void Render(ScriptableRenderContext context, Camera camera)
        {
            //剔除不在摄像机渲染范围内的物体
            //声明剔除参数
            ScriptableCullingParameters cullingParameters;
            //获取剔除参数,如果获取失败,就返回
            if (!CullResults.GetCullingParameters(camera, out cullingParameters)) return;
            //可见内容的信息
            CullResults cull = CullResults.Cull(ref cullingParameters, context);
            
            //使用渲染上下文可以向Unity发出指令(渲染事物/控制事物的渲染状态)
            //此指令被存放于渲染上下文的内部缓冲中,需提交后Unity才执行。
		......
        }

注明:out和ref关键字的作用

相同点:

  • 1.方法的定义和调用都必须显式使用out、ref关键字。

  • 2.都会导致参数按引用传递。

不同点:

  • 1.传递给ref关键字的参数必须赋有初始值,而out不用。

  • 2.out关键字无论外部是否赋有初始值,都会清空变量,退出函数时所有out变量都要赋值。

总结:

  • 1.out关键字一般用于给外部变量赋值的情形。(或有多个返回值的情形)

  • 2.ref关键字一般用于修改外部变量值的情形。

(5)绘制图像

在上一步中我们获取了摄像机范围内的物体,渲染此物体即可,超出摄像机范围的可以不做渲染,以此来提升渲染性能。以下将讲述Unity绘制物体的方法。

a.绘制Unlit的不透明物体

使用的API:

  • “DrawRendererSettings drawSettings = new DrawRendererSettings(camera, new ShaderPassName(“SRPDefaultUnlit”))”:

    绘图设置:渲染上下文在渲染物体时需要的参数之一。

    参数1–摄像机:用于设置排序和裁剪;

    参数2–着色器通道名称:用于控制哪个着色器通道用于渲染。其中”SRPDefaultUnlit”:unity默认的SRP不发光的着色器通道名称。

  • “FilterRenderersSettings filterSettings = new FilterRenderersSettings(true)”:

    过滤设置:参数为是否初始化自身。若为false则不会初始化自身,则不会渲染出任何物体;若为true则会初始化自身,因此会包含所有内容。

  • “context.DrawRenderers(cull.visibleRenderers, ref drawSettings, filterSettings)”:

    绘制:使用渲染上下文进行绘制。

    参数1–经过摄像机剔除后的可见渲染物体

    参数2–绘图设置

    参数3–过滤设置

代码如下:

        /// <summary>
        /// 负责一个摄像机的渲染
        /// </summary>
        /// <param 渲染上下文="renderContext"></param>
        /// <param 需要渲染的摄像机="camera"></param>
        private void Render(ScriptableRenderContext context, Camera camera)
        {
		......
            //释放命令缓冲
            buffer.Release();

            //绘图:绘制除天空盒以外的不透明物体
            //绘图设置
            //参数1--摄像机:用于设置排序和裁剪
            //参数2--着色器通道名称:用于控制哪个着色器通道用于渲染
            //SRPDefaultUnlit:unity默认的SRP不发光的着色器通道名称。
            DrawRendererSettings drawSettings = new DrawRendererSettings(camera, new ShaderPassName("SRPDefaultUnlit"));
            //过滤设置(要初始化自己,否则不包括任何内容)
            //FilterRenderersSettings filterSettings = new FilterRenderersSettings();
            FilterRenderersSettings filterSettings = new FilterRenderersSettings(true);
            //使用渲染上下文进行绘制
            //参数1--经过摄像机剔除后的可见渲染物体
            //参数2--绘图设置
            //参数3--过滤设置
            context.DrawRenderers(cull.visibleRenderers, ref drawSettings, filterSettings);

            //1.绘制天空盒:
            context.DrawSkybox(camera);
		......
        }

绘制不发光的不透明物体,如下图所示:

picture7

查看帧调试器,如下图:

picture8

其实此时不仅仅绘制了Unlit的不透明物体,同时还绘制了Unlit的透明物体(Sphere(1))。

因为在绘图设置”DrawRendererSettings”中着色器通道名称为”SRPDefaultUnlit”是Unity自带的默认的SRPUnlit通道,所以会渲染Unlit的不透明和透明物体。

而图中仅仅显示了不透明物体(黄色球体)的原因是天空盒子覆盖了透明物体的渲染:当天空盒子最后渲染的时候由于透明物体没有写入深度信息,所以距离摄像机最远的天空盒子的渲染会覆盖掉透明物体。

要解决此问题的方法就是调整天空盒子的渲染顺序,让它在透明物体渲染之前渲染,如此一来,透明物体就不会再被覆盖渲染而不显现出来了。

b.绘制Unlit的透明物体

使用的API:

  • “drawSettings.sorting.flags = SortFlags.CommonOpaque”:

    绘图设置的绘制排序:普通的不透明物体的排序方式。

  • “renderQueueRange = RenderQueueRange.opaque”:

    设置渲染队列范围:不透明物体渲染(0~2500)。

绘制Unlit(不发光物体)会进行三段绘制:先绘制不透明物体,然后绘制天空盒,最后绘制透明物体。

在绘制不透明物体的时候使用”drawSettings.sorting.flags = SortFlags.CommonOpaque”来设置不透明物体的排序方式:先绘制距离摄像机近的,然后绘制远的,这样一来当近处的被绘制之后,会存储深度信息,当相同xy坐标的较远处的被绘制时比较深度信息,只绘制深度小的片元,如此一来可以剔除不需要绘制的部分,以提高绘制性能。

在绘制透明物体时,绘制排序刚好相反:先绘制远处的,再绘制近处的。使用Unity的API:”drawSettings.sorting.flags = SortFlags.CommonTransparent”。

代码如下:

        /// <summary>
        /// 负责一个摄像机的渲染
        /// </summary>
        /// <param 渲染上下文="renderContext"></param>
        /// <param 需要渲染的摄像机="camera"></param>
        private void Render(ScriptableRenderContext context, Camera camera)
        {
		......
            //释放命令缓冲
            buffer.Release();

            //绘图:绘制除天空盒以外的不透明物体
            //绘图设置
            //参数1--摄像机:用于设置排序和裁剪
            //参数2--着色器通道名称:用于控制哪个着色器通道用于渲染
            //SRPDefaultUnlit:unity默认的SRP不发光的着色器通道名称。
            DrawRendererSettings drawSettings = new DrawRendererSettings(camera, new ShaderPassName("SRPDefaultUnlit"));
            //绘图设置的绘制排序:普通的不透明物体的排序方式
            drawSettings.sorting.flags = SortFlags.CommonOpaque;
            //过滤设置(要初始化自己,否则不包括任何内容)
            //FilterRenderersSettings filterSettings = new FilterRenderersSettings();
            FilterRenderersSettings filterSettings = new FilterRenderersSettings(true)
            {
                //设置渲染队列范围:不透明物体渲染(0~2500)
                renderQueueRange = RenderQueueRange.opaque,
            };
            //使用渲染上下文进行绘制
            //参数1--经过摄像机剔除后的可见渲染物体
            //参数2--绘图设置
            //参数3--过滤设置
            context.DrawRenderers(cull.visibleRenderers, ref drawSettings, filterSettings);

            //1.绘制天空盒:
            context.DrawSkybox(camera);

            //绘制完天空盒之后再绘制透明物体(防止天空盒覆盖透明物体渲染)
            //绘图设置的绘制排序:普通透明物体的排序方式
            drawSettings.sorting.flags = SortFlags.CommonTransparent;
            //设置渲染队列范围:透明物体渲染(2501~5000)
            filterSettings.renderQueueRange = RenderQueueRange.transparent;
            //使用渲染上下文进行绘制
            context.DrawRenderers(cull.visibleRenderers, ref drawSettings, filterSettings);

            //提交渲染上下文的内部缓冲中的指令
            context.Submit();
        }

绘制Unlit(不发光)的透明物体,如下图所示:白色即为透明物体(透明度为1)

picture9

查看帧调试器,如下图:透明物体(Sphere(1))在天空盒绘制之后才被绘制,避免了天空盒对透明物体的覆盖绘制。

picture10

4.优化渲染管线

(1)内存分配

点击”Window–Analysis–Profiler“,打开分析器,然后选中”CPU Usage–Hierarchy“,可以看见每一帧CPU的运行情况。选择”GC Alloc”为从大到小排序,然后点开”PlayerLoop”下拉项,可以看见GC从大到小的产生情况。如下图所示:

picture11

渲染管线管理中的内部渲染循环的GC由三个地方产生:

  • 命令缓冲区

    首先,在每次的渲染循环中都需要创建一个新的命令缓冲对象,这其实是不必要的。可以复用同一个命令缓冲,而且命令缓冲对象在提交命令到渲染上下文内部缓冲后不需要立即释放命令缓冲对象,只需要清空命令缓冲即可,分配给命令缓冲的内存可以重复使用。这样避免了频繁为命令缓冲对象分配新内存。

    其次,为命令缓冲对象命名的时候使用了摄像机的名字,而获取摄像机名字的过程会创建新的字符串,这也是一个连续内存分配的操作。可以避免获取摄像机名字的操作,直接命名为”Render Camera”固定字符串即可。

原代码:

            //命令缓冲区
            CommandBuffer buffer = new CommandBuffer
            {
                name = camera.name,//命令缓冲区的名字为摄像机的名字
            };
		......
            //释放命令缓冲
            buffer.Release();

优化后:

        /// <summary>
        /// 命令缓冲区
        /// </summary>
        private CommandBuffer mCameraBuffer = new CommandBuffer
        {
            name = "Render Camera",//命令缓冲区的名字为"Render Camera"
        };
		......
        private void Render(ScriptableRenderContext context, Camera camera)
        {
		......
            mCameraBuffer.ClearRenderTarget
            (
                (clearFlags & CameraClearFlags.Depth) != 0,
                (clearFlags & CameraClearFlags.Color) != 0,
                camera.backgroundColor
            );
            //执行命令缓冲中的命令
            //不会立即执行,会先提交到渲染上下文的内部缓冲中
            context.ExecuteCommandBuffer(mCameraBuffer);
            //清除命令缓冲
            mCameraBuffer.Clear();
		......
        }
  • 经过摄像机视锥体剔除后的结果CullResults

    CullResults是一个结构体,内部包含三个列表对象,每次渲染循环中都会为这三个列表对象重新分配内存,所以此处会产生GC。”CullResults.Cull()”存在一种重载方法,可以不在每次渲染循环中返回新的”CullResults”,而是使用”ref”为原”CullResults”重新赋值,这样一来结构体内部的列表就可以重复使用,而不需要重新分配内存。

原代码:

            //可见内容的信息
            CullResults cull = CullResults.Cull(ref cullingParameters, context);

优化后:

        /// <summary>
        /// 可见内容的信息
        /// </summary>
        private CullResults mCull;
		......
        private void Render(ScriptableRenderContext context, Camera camera)
        {
		......
            //获取剔除参数,如果获取失败,就返回
            if (!CullResults.GetCullingParameters(camera, out cullingParameters)) return;
            //可见内容的信息
            CullResults.Cull(ref cullingParameters, context, ref mCull);
		......
        }

再一次打开分析器,查看渲染循环中的内存分配结果:

picture12

此时渲染循环中的GC已经全部消除了。

(2)优化帧调试器显示的渲染层次结构

当前的帧调试器中显示的渲染层次结构如下图所示:

picture13

所有的渲染命令都起始于根节点。

可以使用mCameraBuffer.BeginSample("Render Camera")mCameraBuffer.EndSample("Render Camera")来构建渲染层次结构。

        /// <summary>
        /// 负责一个摄像机的渲染
        /// </summary>
        /// <param 渲染上下文="renderContext"></param>
        /// <param 需要渲染的摄像机="camera"></param>
        private void Render(ScriptableRenderContext context, Camera camera)
        {
		......
            //添加一个命令,开始帧调试器中的采样
            mCameraBuffer.BeginSample("Render");
            //执行命令缓冲中的命令
            //不会立即执行,会先提交到渲染上下文的内部缓冲中
            context.ExecuteCommandBuffer(mCameraBuffer);
		......
            //添加一个命令,结束帧调试器中的采样
            mCameraBuffer.EndSample("Render");
            context.ExecuteCommandBuffer(mCameraBuffer);
            mCameraBuffer.Clear();

            //提交渲染上下文的内部缓冲中的指令
            context.Submit();
        }

查看帧调试器,结果如下图:

picture14

其中根节点的名称”Render Camera”来源于命令缓冲区的名称。

(3)绘制默认管道–渲染使用了SRP不支持的Shader的对象

目前的SRP只支持Unlit Shader,所以使用其他着色器的对象不会被渲染,这是在DrawRendererSettings中所决定的。

在未使用SRP所支持的Shader的情况,可以认为其使用了错误的Shader。因此使用Unity内置的错误着色器来渲染使用了错误Shader的对象会比不渲染更好,至少在编辑模式中是这样的。

创建一个新的方法DrawDefaultPipeline(ScriptableRenderContext context, Camera camera)来处理此事。

此方法只在”DEVELOPMENT_BUILD”和”UNITY_EDITOR”模式下使用,在发行Bulid时不使用。所以还需要加上特性[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]。使用此特性需引入命名空间using Conditional = System.Diagnostics.ConditionalAttribute;

此特性的作用是:如果在编译过程中定义了括号内的字符串参数,则特性下的方法将被包含在内一起编译,否则不参与编译。

using Conditional = System.Diagnostics.ConditionalAttribute;
		......
        /// <summary>
        /// 错误材质
        /// 用于渲染使用了错误Shader的对象
        /// </summary>
        private Material mErrorMat;
		......
        private void Render(ScriptableRenderContext context, Camera camera)
        {
		......
            //渲染使用了错误着色器的对象
            DrawDefaultPipeline(context, camera);

            //添加一个命令,结束帧调试器中的采样
            mCameraBuffer.EndSample("Render");
		......
        }

        /// <summary>
        /// 绘制默认管道
        /// 渲染使用了错误着色器的对象
        /// </summary>
        /// <param 渲染上下文="context"></param>
        /// <param 摄像机="camera"></param>
        [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
        private void DrawDefaultPipeline(ScriptableRenderContext context, Camera camera)
        {
            //获取错误材质
            if (mErrorMat == null)
            {
                //Unity内置的错误Shader,洋红色。
                Shader errorShader = Shader.Find("Hidden/InternalErrorShader");
                mErrorMat = new Material(errorShader)
                {
                    //该材质不会出现在ProjectWindow中,同时也不会保存
                    hideFlags = HideFlags.HideAndDontSave
                };
            }
            //绘图设置
            DrawRendererSettings drawSettings = new DrawRendererSettings(camera, new ShaderPassName("ForwardBase"));
            //设置Shader的通道名称
            //涵盖目前Unity所有的Shader
            drawSettings.SetShaderPassName(1, new ShaderPassName("PrepassBase"));
            drawSettings.SetShaderPassName(2, new ShaderPassName("Always"));
            drawSettings.SetShaderPassName(3, new ShaderPassName("Vertex"));
            drawSettings.SetShaderPassName(4, new ShaderPassName("VertexLMRGBM"));
            drawSettings.SetShaderPassName(5, new ShaderPassName("VertexLM"));
            //设置错误材质
            drawSettings.SetOverrideMaterial(mErrorMat, 0);
            //过滤设置
            FilterRenderersSettings filterSettings = new FilterRenderersSettings(true);
            //使用渲染上下文进行绘制
            context.DrawRenderers(mCull.visibleRenderers, ref drawSettings, filterSettings);
        }

此时使用了SRP不支持的着色器的对象将显示为洋红色,就像Unity内置渲染管线对错误渲染对象所做的一样。

picture15

(4)支持UI显示

创建一个”Button”,”Canvas”默认的渲染模式为”Screen Space - Overlay”。

picture16

查看帧调试器,此时UI会单独呈现:

picture17

Scene视图中会显示UI:

picture18

将”Canvas”的渲染模式调为世界空间,并将主摄像机拖拽上去:

picture19

然后移动”Button”到Game视图可见位置。

picture20

查看帧调试器,UI同一般的透明物体一起渲染:

picture21

然而此时Scene视图中却不显示UI:

picture22

要处理此问题需要在剔除摄像机视锥体以外对象之前使用ScriptableRenderContext.EmitWorldGeometryForSceneView(camera)发出UI几何图形到Scene视图中。

        /// <summary>
        /// 负责一个摄像机的渲染
        /// </summary>
        /// <param 渲染上下文="renderContext"></param>
        /// <param 需要渲染的摄像机="camera"></param>
        private void Render(ScriptableRenderContext context, Camera camera)
        {
		......
            //在场景窗口中显示UI
            //只在渲染场景窗口时发出UI几何图形,防止UI重复渲染
#if UNITY_EDITOR
            if (camera.cameraType == CameraType.SceneView)
            {
                ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
            }
#endif
            //可见内容的信息
            CullResults.Cull(ref cullingParameters, context, ref mCull);
		......
        }

此时Scene视图中可以正常显示UI:

picture23