初识Unity自定义渲染管线

Unity官方文档种对自定义渲染管线的介绍并不是非常多,且内容也相当简单。由于这部分内容涉及到的知识点非常多,在学习过程中也会感觉非常吃力。一旦掌握了相关技术,在游戏开发种不仅仅可以制作出非常满意的效果,同时也能保证更加高效的运行。

Unity除了内置的渲染管线,还提供两个扩展,一个称之为“通用渲染管线”,另外一个叫做“高清渲染管线”。你可以在官方文档种找到针对这三种渲染管线的功能对比。而后两种渲染管线都是基于Unity提供的自定义渲染管线实现的。

值得注意的是,由于Unity改变了版本更新策略,自定义渲染管线在不同版本中,其功能和接口都存在一些差异,而这些差异往往都是以渐进式进行升级。与此同时,由于通用渲染管线一开始的名称为“轻量级渲染管线”所以在查看源码时,可以看到非常多的兼容性代码,这些是为了兼容旧的命名规则和不同Unity版本间的API差异。

在学习之初,我们可以先来编写一些简单的,可运行的Demo。来理清楚自定义渲染管线开发中的必要环节和核心功能。而后在参考阅读通用渲染管线的代码。这样可以更加清楚的了解对应模块的设计思路和功能。

本篇先简单介绍一下如何搭建一套自定义渲染管线,但文中所使用的API可能会与读者的版本存在不同。此时可以参考相关的官方文档,查阅变更部分。笔者所使用的版本为2020.3 LTS版本。

创建一个默认3D核心模板工程

在官方文档中,推荐加入SRP Core包,事实上在一开始,我们完全可以不添加任何扩展包即可开发自定义渲染管线。

为了让我们的代码能够更好的和业务逻辑代码进行区分,也方便后续的维护,可以将自己的渲染管线放在一个独立的命名空间内,这里,我们将所有相关代码全部访问名称为“Max2D.Rendering.Teacup”内。

至于为何这个渲染器的名称要叫做Teacup,很简单,因为在取这个名字时,笔者手边正好放着一杯茶。

两个核心类 RenderPipelineAsset 与 RenderPipeline

RenderPipelineAsset

RenderPipelineAsset负责生成自定义渲染管线资源,它实现了IRenderPipelineAsset 接口。该接口位于Native层,你无法在C# API中找到对应的实现。而RenderPipelineAsset类则是对这个接口的默认实现。我们在使用时需要继承RenderPipelineAsset,并重写其中的CreatePipeline 方法。

此处编写代码如下:

using UnityEngine;
using UnityEngine.Rendering;


namespace Max2D.Rendering.Teacup
{
    [CreateAssetMenu(menuName = "Max2D/Rendering/TeacupRenderingPipelineAssets")]
    public class TeacupRenderingPipelineAssets : RenderPipelineAsset
    {
        [SerializeField] public bool UseSRPBatcher = true;
        
        protected override RenderPipeline CreatePipeline()
        {
            return new TeacupRenderingPipeline(this);
        }
    }
}

我们可以在该类中加入当前渲染管线的一些配置参数,这样在创建渲染管线资源后,可在Inspector面板中进行设置。为了当前演示效果,我们先来定义一个名称为UseSRPBatcher的bool型参数,方便后面使用。

通过在Project面板中右键可创建当前渲染管线的资源文件。

RenderPipeline

这是自定义渲染管线中最为核心的类,它是自定义渲染管线实例,其中的Render方法则是我们渲染逻辑的处理函数。具体的类实现结构如下:

using UnityEngine;
using UnityEngine.Rendering;

namespace Max2D.Rendering.Teacup
{
    public class TeacupRenderingPipeline : RenderPipeline
    {

        public TeacupRenderingPipeline(TeacupRenderingPipelineAssets assets)
        {
            //构造函数
        }
        
        protected override void Render(ScriptableRenderContext context, Camera[] cameras)
        {
            //渲染管线入口
        }
    }
}

我们可以在构造函数中接收预定义的 TeacupRenderingPipelineAssets 实例,然后对其进行保存。推荐在这个地方对配置项目进行设备支持检查。

此处可参考通用渲染管线中的实现方式。在构造函数中,存在一个名为SetSupportedRenderingFeatures的函数调用,针对编辑器的支持进行了检查。同时也对一些全局配置和参数进行了初始化。

Render 函数需要我们自己重写,我们的渲染流程是在Render函数中实现的。在官方文档中,Render被称之为渲染入口点。由于Unity编辑器中的Scene 和 Game 视窗,以及最终游戏画面渲染都会调用Render。所以在开发过程中,我们不仅要考虑游戏画面的支持,还要考虑编辑器的支持。

关于 Render 函数的注意实现,由于Unity驱动渲染最终会调用Render函数,所以该函数的执行速度越快,我们的FPS越高(将业务逻辑因素排除)。你可以尝试阅读通用渲染管线的代码,我们可以使用一些技巧来优化一部分平台性的代码。例如,使用宏来区分编辑器环境和Runtime环境。又或者说,当你所开发的自定义渲染管线仅针对移动平台,可不对XR平台或其他平台做判断处理。虽然这种做法在兼容性上有所缺失,但避免了更多的平台判定和硬件条件差异所带来的约束。

启动自定义渲染管线

在Project Settings面板中,切换到Graphics标签,针对Scriptable Render Pipeline Settings 一项进行设置,选择创建好的Teacup Rendering Pipeline Assets即可。