(翻译)可编程渲染管线SRP预览

发布于 2019-02-23  143 次阅读


本篇内容为Unity官方博客的翻译整理
原文链接
日期:2018年1月31日
作者:Tim Cooper
翻译:咲夜詩

序言:在2018.1中我们引入了可编程渲染管线SRP,使用C#配置渲染。在写自定义渲染管线之前,需要先明确理解我们所说的渲染管线。

什么是一个渲染管线

渲染管线是一个概述性术语,用于描述物体被渲染到屏幕上的一系列技术。总体上分为:
·剔除 Culling
·渲染目标 Rendering Objects
·后期处理 Post processing
每一个高级概念根据您希望如何执行又可以进一步细分。例如,渲染目标可以使用以下方式执行:
·多pass渲染:每个pass中逐物体逐光
·单pass渲染:每个pass中逐物体
·延迟渲染:将表面属性存储到g-buffer,执行屏幕空间光照

在写一个自定义srp时,需要权衡使用哪种技术。
https://youtu.be/2wUPgl7upnU

案例工程

本篇文章中所讨论的特性均包含在一个Github上的项目

渲染入口

使用SRP时,需要定义一个类来控制渲染,也就是您将要创建的渲染管线。
入口是一个“Render”命令,用于接收render context和将要渲染的摄像机列表作为参数。

public class BasicPipeInstance : RenderPipeline
{
   public override void Render(ScriptableRenderContext context, Camera[] cameras){}
}

Render Pipeline Context

SRP使用延迟执行的概念。作为用户,您可以构建命令列表,然后执行它们。用于构建这些命令的对象称为“ScriptableRenderContext”。当您使用操作填充上下文时,您可以调用“Submit”以提交所有排队的draw call。

这里有一个案例,由render context执行:使用一个command buffer清除一个render target

// Create a new command buffer that can be used
// to issue commands to the render context
var cmd = new CommandBuffer();

// issue a clear render target command
cmd.ClearRenderTarget(true, false, Color.green);

// queue the command buffer
context.ExecuteCommandBuffer(cmd);


注:这是一个并不那么令人兴奋的渲染管线

这里是一个完整的渲染管线,用于简单清除屏幕。

剔除

剔除流程指定了要渲染到屏幕上的内容。
在Unity中剔除包括:
·截体剔除:计算出存在于摄像机近点和远点之间的对象
·遮挡剔除:计算出被其他对象遮挡的对象,并从渲染中排除,更多详情可以参考Occlusion Culling

在渲染开始时,首先需要计算需要渲染的内容。获取相机,并从相机的视角执行剔除操作。
剔除操作返回该相机的有效物体列表和灯光,用于稍后的渲染管线。

在SRP中的剔除

SRP和内置渲染管线一样通过相机视角执行渲染,用的是一样的摄像机。
SRP提供一系列api用于剔除,其工作流看起来如下:

// Create an structure to hold the culling paramaters
ScriptableCullingParameters cullingParams;

//Populate the culling paramaters from the camera
if (!CullResults.GetCullingParameters(camera, stereoEnabled, out cullingParams))
    continue;

// if you like you can modify the culling paramaters here
cullingParams.isOrthographic = true;

// Create a structure to hold the cull results
CullResults cullResults = new CullResults();

// Perform the culling operation
CullResults.Cull(ref cullingParams, context, ref cullResults);

上传的剔除结果将被用于执行渲染。以上代码参考demo工程中OpaqueAssetPipe.cs。

描绘

现在我们有了剔除结果,可以将他们渲染到屏幕上。

但是有太多可配置项,需要提前决定,这些决定可以分为:
·渲染管线的目标硬件
·想要实现的特殊表现
·项目类型

例如,2D横板卷轴游戏和高端PC第一人称射击游戏。
这些游戏有不同的约束,也就会有不同的渲染管线,一些实际的差别在于:
·HDR vs LDR
·Linear vs Gamma
·MSAA vs Post Process AA
·PBR Materials vs Simple Materials
·Lighting vs No Lighting
·Lighting Technique
·Shadowing Technique

这些决定有帮助于在写渲染管线时设置约束。
接下来,我们将展示一个没有灯光的简单渲染器,可以渲染一些不透明物体。

过滤设置:Render Buckets and Layers

当渲染目标有一个指定类型,不透明/透明/次表面,或其他类型。
Unity使用队列的概念来表示一个物体何时被渲染。
物体根据材质类型会被分配到不同的队列,这些队列属于不同buckets。
当SRP调用渲染时,您将指定buckets的范围。

除了buckets,Unity图层也可以用于过滤。
这为SRP增添了过滤能力。

// Get the opaque rendering filter settings
var opaqueRange = new FilterRenderersSettings();

//Set the range to be the opaque queues
opaqueRange.renderQueueRange = new RenderQueueRange()
{
    min = 0,
    max = (int)UnityEngine.Rendering.RenderQueue.GeometryLast,
};

//Include all layers
opaqueRange.layerMask = ~0;

描绘设置:如何描绘目标

过滤和剔除决定了要渲染的内容,现在我们要决定如何渲染。
SRP提供了很多配置项用于渲染通过过滤的内容,“DrawRenderSetting”是用于配置的结构。
这个结构允许很多可配置项:
·分类整理:物体被渲染的顺序,例如back to front和front to back
·逐渲染标签:Unity传递给着色器的内置设定,包括逐物体light probes、逐物体light maps等
·渲染标签:用什么批处理算法,实例化或非实例化。
·Shader Pass:当前draw call使用哪个shader pass。

// Create the draw render settings
// note that it takes a shader pass name
var drs = new DrawRendererSettings(Camera.current, new ShaderPassName("Opaque"));

// enable instancing for the draw call
drs.flags = DrawRendererFlags.EnableInstancing;

// pass light probe and lightmap data to each renderer
drs.rendererConfiguration = RendererConfiguration.PerObjectLightProbe | RendererConfiguration.PerObjectLightmaps;

// sort the objects like normal opaque objects
drs.sorting.flags = SortFlags.CommonOpaque;

提交context

在提交一个draw call之前,我们需要准备三样:
·剔除结果
·过滤规则
·描绘规则

现在我们可以提交draw call了,就像其他东西在SRP中一样,一个draw call相当于在context中插入一个调用。
在SRP中,通常不单个渲染mesh,而是一次提交大量的mesh,以减少脚本执行开销和提高cpu效率。

为了提交一个draw call,我们合并了已创建的代码。

// draw all of the renderers
context.DrawRenderers(cullResults.visibleRenderers, ref drs, opaqueRange);



// submit the context, this will execute all of the queued up commands.
context.Submit();

这样内容就会绘制到当前render target的界限内,可以使用command buffer切换render target。

这里是一个渲染不透明物体的renderer。
这里是将上例扩展后得到的透明物体的renderer。

值得一提的是,在透明渲染中,渲染顺序是back to front。


关注成长,注重因果。