Unity正式加入了Universal RP(通用渲染管线),这里会记录一些官方文档,并分析管线的代码,文中使用Unity2019.3.0b1,Universal RP 7.0.1。
可编程渲染管线
为了解决仅有一个默认渲染管线,造成的可配置型、可发现性、灵活性等问题。Unity在管线设计的概念上做了转移,决定在C++端保留一个非常小的渲染内核,让C#端可以通过API暴露出更多的选择性,也就是说,Unity会提供一系列的C# API以及内置渲染管线的C#实现;这样一来,一方面可以保证C++端的代码都能严格通过各种白盒测试,另一方面C#端代码就可以在实际项目中调整,有任何问题也可以方便地进行调试。
新的管线对用户而言主要是C# 端的API以及由这些API编写的一系列定制化的内置渲染管线。而在内部实现上,引擎C++端会负责多线程实现性能关键的部分,如上图所示,而C#端负责更高层的渲染指令调度。
用户可以直接使用开源的内置管线,或者在内置管线的基础上进行修改,甚至直接编写定制化的管线。具体使用上渲染管线在工程中会生成特定的Asset,如下图所示,这个Asset序列化了这条管线的一些公共设置变量,并负责在运行时创建实际的渲染上下文;当这个Asset的设置变量在运行时发生变化,引擎会销毁当前上下文然后重新创建管线。
可编程渲染管线是URP的基础,通过它我们可以知道unity中怎么实现最基本的渲染,unity对渲染管线API封装程度。
详解可编程脚本渲染管线SRP - Unity Connectconnect.unity.com通用渲染管线Universal RP
LWRP是URP之前的名称,两者基本没有区别,URP相对LWRP的变化主要是把PostProcessing集成到内部了。
Unity轻量级渲染管线LWRP源码及案例解析(上) - Unity Connectconnect.unity.comUnity轻量级渲染管线LWRP源码及案例解析,讲解了URP的使用方法和拓展方法,里面有对SRP、URP的一些说明,URP与内置管线的对比,URP的源码结构。
在继续看源码细节之前,先看看URP的主要提升(相对于内置管线),和不完善的地方。
提升:
1 开源
可编程渲染管线最大的好处就是开源,对于有能力的团队,可以选择在SRP的基础上写自己的管线。而使用Unity提供的管线模板URP或HDRP,也可以把他们从Package包中提出到Asset中做更改,上面的文章有提到修改的注意事项(其实就是记着把Shader中的include路径,和代码中的Shader.Find之类的路径改一下),但是并不推荐这么做,因为版本更新的维护是噩梦。开源意味着容易定位到问题,对调试非常友好。
2 渲染路径改为单Pass Forward Rendering
内置管线的多Pass Forward Rendering,会在多光源时对额外的光源使用新的FowardAdd Pass计算,Pass数量是影响物体的光源数量,最大值为8。
URP的单Pass Forward Renderering,会将光源一次性传入Forward Pass,但由于单Pass能够传的数据有限,现在最多支持一个直线光外加4个其他光源。
这样做虽然并不完美,但多光源场景中DrawCall数量会大量下降。
3 拓展性(非代码修改)
URP在渲染队列中嵌入了拓展入口,相当于之前的CommandBuffer的可视化操作。上面的文章中有详细的使用方法。
用新的设计取代了GrabPass的结构,在Opaque渲染之后可以截出一张RenderTexture,提供给之后使用。
4 SRP Batcher
提供了一种新的批处理方式,基于Shader的批处理。不过这个技术还不是正式功能,有些局限,不支持Skinned Meshes、Material Property Blocks。
缺点:
1 不支持多相机叠加
这是很重要的功能,如果真用到了,就去改源码吧,要不等之后更新。
2 Defferred Renderring
现在URP里面没有延迟渲染,也没有时域抗锯齿TAA。
一个SRP代码示例
我们先看 详解可编程脚本渲染管线SRP - Unity Connect 中的最后一个例子,半透明渲染。
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
// RenderPipelineAsset继承自ScriptableObject,用于提供序列化的渲染参数、并根据参数生成管线实例
[ExecuteInEditMode]
public class TransparentAssetPipe : RenderPipelineAsset
{
#if UNITY_EDITOR
// 编辑器中创建ScriptableObject
[UnityEditor.MenuItem("SRP-Demo/03 - Create Transparent Asset Pipeline")]
static void CreateBasicAssetPipeline()
{
var instance = ScriptableObject.CreateInstance<TransparentAssetPipe>();
UnityEditor.AssetDatabase.CreateAsset(instance, "Assets/SRP-Demo/3-TransparentAssetPipe/TransparentAssetPipe.asset");
}
#endif
// 创建渲染管线实例的方法
protected override IRenderPipeline InternalCreatePipeline()
{
return new TransparentAssetPipeInstance();
}
}
public class TransparentAssetPipeInstance : RenderPipeline
{
// 渲染入口点
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
base.Render(context, cameras);
// 遍历相机
foreach (var camera in cameras)
{
// 剔除
// 新建一个结构体,用于存储剔除参数
ScriptableCullingParameters cullingParams;
// 填充来自摄像机的剔除参数
if (!CullResults.GetCullingParameters(camera, out cullingParams))
continue;
// 执行剔除操作,并存储剔除结果
CullResults cull = CullResults.Cull(ref cullingParams, context);
// 设置渲染相机(设置渲染目标, view/projection 矩阵和每相机内置shader变量).
context.SetupCameraProperties(camera);
// 清理深度缓存
var cmd = new CommandBuffer();
cmd.ClearRenderTarget(true, false, Color.black);
context.ExecuteCommandBuffer(cmd);
cmd.Release();
// 使用一个指定的着色器通道,新建渲染设置
var settings = new DrawRendererSettings(camera, new ShaderPassName("BasicPass"));
settings.sorting.flags = SortFlags.CommonOpaque;
// 获取不透明渲染过滤器设置
var filterSettings = new FilterRenderersSettings(true) {
renderQueueRange = RenderQueueRange.opaque };
// 绘制所有渲染器
context.DrawRenderers(cull.visibleRenderers, ref settings, filterSettings);
// 绘制天空盒
context.DrawSkybox(camera);
// 绘制半透明物体(着色器通道还是绘制不透明物体时用到的)
settings.sorting.flags = SortFlags.CommonTransparent;
filterSettings.renderQueueRange = RenderQueueRange.transparent;
context.DrawRenderers(cull.visibleRenderers, ref settings, filterSettings);
// 提交渲染上下文
context.Submit();
}
}
}
渲染入口点为RenderPipeline类中的Render函数,参数为相机列表和渲染上下文,在这个函数的最后提交渲染上下文,完成渲染。填充渲染上下文的过程中,Unity封装好了一些方法,包括剔除操作、CommandBuffer的一系列指令、绘制渲染器等。
Universal RP
推荐先熟悉Universal RP的使用,再去看代码的实现,这样效率会更高。
Universal RP 7.0的文档docs.unity3d.comUniversal RP的依赖Package有两个,CoreRPLibrary是URP和HDRP都用到的一些工具,ShaderGraph是shader的可视化节点编辑器,之前的PostProcessing已经集成到内部了。
Render方法
Universal RP的主要实现就在Universal RP的Runtime文件夹中,我们从渲染入口点开始看实现细节。
整体结构非常简单,设置GraphicSetting参数,设置每帧Shader中的Global 变量,相机排序,相机遍历,每相机渲染。
首先看一下四个方法BeginCameraRen