写在前面
要在屏幕上绘制图像,我们得知道几个要素:绘制什么东西(What),何时绘制(When),在屏幕上哪个位置绘制(Where),用什么设置绘制(How)。另外还要把灯光、阴影、透明效果、后处理效果等等都要正确的绘制出来,才能得到最后的一张图像,这也是渲染管线所要做的。
以前Unity只开放了很小部分供我们对绘制进行设置,Unity2018开始,Unity支持自定义渲染管线(Scriptable Render Pipeline),虽然还是要依赖Unity的一些基础方法,比如调用绘制天空盒,剔除等,但这样我们可以自己对绘制进行更多的设置。Unity 2018提供了两个实验性的自定义渲染管线:轻量级渲染管线和高清渲染管线。在Unity 2019中轻量级渲染管线成为正式功能,并且在2019.3中改名为通用渲染管线。 
通用渲染管线的目的是替换现在用的旧的默认管线,顾名思义,就是希望这个渲染管线能够满足大部分的需求,并且呢,也容易去自己修改一部分内容。
我们这里主要是从0开始自己定义渲染管线,并不是使用Unity提供的管线。
软件环境
我使用的Unity版本:Unity2020.3.10f1
自定义渲染管线
工程设置
因为我们是自定义渲染管线,所以不要选Unity的渲染管线,选择3D 
打开设置,将色彩空间切换为线性空间 
然后我们像下面随便创建一些物体,使用不同的材质,Unlit/Color,Standard(Mode设置Transparent),Unlit/Transparent 
创建渲染管线
新建脚本(因为直接上的都是最终代码,有些地方直接贴上去可能会报错,一步一步做的话,可以先注掉报错的部分) 
using UnityEngine;
using UnityEngine.Rendering;
public class CustomRenderPipeline : RenderPipeline
{
CameraRenderer cameraRenderer = new CameraRenderer();
protected override void Render(ScriptableRenderContext context, Camera[] cameras)
{
foreach(var camera in cameras)
{
cameraRenderer.Render(context, camera);
}
}
}

using UnityEngine;
using UnityEngine.Rendering;
[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset
{
protected override RenderPipeline CreatePipeline()
{
return new CustomRenderPipeline();
}
}
然后创建我们的渲染管线资源  
完成创建后,我们需要在ProjectSettings-Graphics中设置我们的渲染管线,设置后我们会发现整个设置页面都变了,很多设置都消失了。另外由于我们的渲染管线中还没有写任何的渲染设置,因此现在什么东西都不会被绘制了,无论是在Scene视图还是Game视图,甚至FrameDebugger中也是什么东西都没有了。这就是渲染管线的真正开始,从0开始自定义我们的渲染管线    
绘制天空盒
我们先来绘制下天空盒,很简单的操作 关键代码
context.DrawSkybox(camera);
context.Submit();
然后我们就能看到天空盒了,并且在FrameDebuger中可以看到绘制的DrawCall   但是这个时候当我们旋转相机时,画面并不会发生变化,这是因为我们没有将相机的旋转这些属性应用到Shader中 关键代码
context.SetupCameraProperties(camera);
另外,我们可以通过添加BufferName来让Profile和FrameDebuger中相应的部分显示我们想要的名称,这样便于分析 关键代码
const string bufferName = "Render Camera";
CommandBuffer buffer = new CommandBuffer
{
name = bufferName,
};
buffer.BeginSample(SampleName);
buffer.EndSample(SampleName);

ClearRenderTarget
我们在每一次绘制之前,都应该清除一些必要的数据,这样以保证正确的绘制 关键代码
context.SetupCameraProperties(camera);
var flags = camera.clearFlags;
buffer.ClearRenderTarget(
flags != CameraClearFlags.Nothing,
flags == CameraClearFlags.Color,
flags == CameraClearFlags.Color ? camera.backgroundColor.linear : Color.clear
);
要注意ClearRenderTarget的调用位置,不然会使用内置Shader绘制的方法来清除,这样子性能不好    
Cull
通过Cull来确定是否绘制 关键代码
public void Render(ScriptableRenderContext context, Camera camera)
{
this.context = context;
this.camera = camera;
PrepareBuffer();
PrepareForSceneWindow();
if (!Cull())
return;
Setup();
DrawVisibleGeometry();
DrawUnsupportedShaders();
DrawGizmos();
Submit();
}
bool Cull()
{
if (camera.TryGetCullingParameters(out var cullingParameters))
{
cullingResults = context.Cull(ref cullingParameters);
return true;
}
return false;
}
|