Unity的SRP

Unity从2018开始添加了对可编程渲染管线的支持,使得我们可以从头开始设计自定义的管线。我们先从设计一个最小的可编程渲染管线开始,一步一步了解Unity的SRP。这里,我们使用的Unity版本为2019.4.22f1。

要使用SRP,首先要在Project Settings中设置自定义的Render Pipeline Asset,这个asset是一个ScriptableObject,需要继承自RenderPipelineAsset

Unity的SRP1

那么我们先自己创建一个空的类:

using UnityEngine.Rendering;

public class MyPipelineAsset : RenderPipelineAsset
{
}

编译发现会有报错,提示需要实现RenderPipelineAsset的抽象方法,通过该方法Unity可以获取到一个自定义管线的对象实例:

Unity的SRP2

同样先实现一个空方法:

using UnityEngine.Rendering;
using UnityEngine;

[CreateAssetMenu(menuName = "Rendering/My Pipeline")]
public class MyPipelineAsset : RenderPipelineAsset
{
    protected override RenderPipeline CreatePipeline () {
		return null;
	}
}

然后我们就可以通过Create/Rendering/My Pipeline创建出一个asset出来了,再把它拖到Project Settings里去,可以发现Unity的Scene/Game窗口都变得一片漆黑,打开Frame Debug,里面也是一片空白:

Unity的SRP3

说明我们自定义的管线生效了,这个管线啥也没干,当然就一片漆黑了,一个Draw Call也没有。显然下一步我们需要继承并扩展Unity的RenderPipeline

using UnityEngine.Rendering;

public class MyPipeline : RenderPipeline
{

}

这时候又有编译报错了,提示我们没有实现抽象方法Render,也就是最核心的渲染接口:

Unity的SRP4

那么,我们先把天空盒显示出来。ScriptableRenderContext提供了一个绘制天空盒的方法,调用之后再提交绘制指令给GPU执行:

using UnityEngine.Rendering;
using UnityEngine;

public class MyPipeline : RenderPipeline
{
	protected override void Render (ScriptableRenderContext renderContext, Camera[] cameras)
    {
        renderContext.DrawSkybox(cameras[0]);
		renderContext.Submit();
	}
}

可以看到,此时天空盒正常出现了,查看Frame Debug,也是一切正常:

Unity的SRP5

Unity的SRP6

不过context只提供一些简单的绘制命令,对于更为复杂的,我们需要借助command buffer来执行。比如我们模拟内置渲染管线,在渲染之前清一下render target:

var buffer = new CommandBuffer {
    name = camera.name
};
CameraClearFlags clearFlags = camera.clearFlags;
buffer.ClearRenderTarget(
    (clearFlags & CameraClearFlags.Depth) != 0,
    (clearFlags & CameraClearFlags.Color) != 0,
    camera.backgroundColor
);
context.ExecuteCommandBuffer(buffer);
buffer.Release();

然后打开Frame Debug,可以看到多了一个Clear的pass:

Unity的SRP7

下一步,我们来考虑如何绘制场景中的objects。首先,我们希望,只有相机可见范围内的objects是需要绘制的,其他不可见的可以提前被剔除掉。SRP中也提供了剔除相关的接口,我们在绘制前先获取剔除的参数,如果获取不到则说明当前相机没有可见的objects需要绘制:

ScriptableCullingParameters cullingParameters;
if(!camera.TryGetCullingParameters(out cullingParameters))
{
    return;
}

CullingResults cull = context.Cull(ref cullingParameters);

然后,我们调用DrawRenderers进行绘制,这个函数除了前面的CullingResults之外,还需要DrawingSettingsFilteringSettings两个参数。DrawingSettings用来控制场景中objects的绘制顺序,以及需要使用哪个shader pass进行绘制;FilteringSettings用来进一步筛选场景中的objects是否参与绘制。

var drawSettings = new DrawingSettings(new ShaderTagId("SRPDefaultUnlit"), new SortingSettings(camera));

var filterSettings = FilteringSettings.defaultValue;

context.DrawRenderers(
    cull, ref drawSettings, ref filterSettings
);

这里,我们使用的是SRPDefaultUnlit这个shader pass,也就是Unity内置的unlit shader。我们新建两个材质,分别使用Unlit/Color和Unlit/Transparent两个shader,然后创建两个使用该材质的Cube:

Unity的SRP8

场景中只看到了不透明的Cube,虽然在Frame Debug中,显示了绘制两个Cube的pass:

Unity的SRP9

为什么呢?这是因为绘制透明物体是不写深度的,意味着如果先绘制了透明物体,再绘制天空盒时,天空盒会覆盖掉透明物体。因此我们需要对绘制的顺序进行调整,在绘制天空盒之前,只绘制不透明的物体,最后再绘制透明物体。

var drawSettings = new DrawingSettings(new ShaderTagId("SRPDefaultUnlit"), new SortingSettings(camera));
var filterSettings = new FilteringSettings(RenderQueueRange.opaque);

context.DrawRenderers(
    cull, ref drawSettings, ref filterSettings
);

context.DrawSkybox(camera);

filterSettings.renderQueueRange = RenderQueueRange.transparent;
context.DrawRenderers(
    cull, ref drawSettings, ref filterSettings
);

Unity的SRP10

Unity的SRP11

另外,为了尽量避免overdraw,我们希望在绘制不透明的物体时,按照从近到远的顺序进行渲染;在绘制透明的物体时,则要按照从远到近的顺序进行渲染。可以通过设置sortingSettings.criteria的值来进行控制。

var sortingSettings = new SortingSettings(camera);
sortingSettings.criteria = SortingCriteria.CommonOpaque;
var drawSettings = new DrawingSettings(new ShaderTagId("SRPDefaultUnlit"), sortingSettings);
var filterSettings = new FilteringSettings(RenderQueueRange.opaque);

context.DrawRenderers(
    cull, ref drawSettings, ref filterSettings
);

context.DrawSkybox(camera);

sortingSettings.criteria = SortingCriteria.CommonTransparent;
drawSettings.sortingSettings = sortingSettings;
filterSettings.renderQueueRange = RenderQueueRange.transparent;
context.DrawRenderers(
    cull, ref drawSettings, ref filterSettings
);

自此,一个简单的自定义管线就算完成了,后面再让我们进一步完善它。

如果你觉得我的文章有帮助,欢迎关注我的微信公众号 我是真的想做游戏啊

Reference

[1] Custom Pipeline

猜你喜欢

转载自blog.csdn.net/weixin_45776473/article/details/129462172
今日推荐