Scriptable Render Pipeline

SRP的核心是一堆API集合,使得整个渲染过程及相关配置暴露给用户,使得用户可以精确地控制项目的渲染流程。

SRP API为原有的Unity构件提供了一些新的接口(interface):

  • Lights
  • Materials
  • Cameras
  • Command Buffers

但是和Unity交互的方式变了。由于性能原因,当自定义SRP时,通常是在使用一组renderers(这里的render应该是指MeshRender之类的),而不是一个。

What is a Render Pipeline

什么是渲染管线?“Render Pipeline”,渲染管线,是许多技术的总称,用于把物体(Objects,比如三维物体)显示到屏幕上。概括性讲,渲染管线包括:

  • Culling
  • Rendering Objects
  • Post processing

除了上面这些高级概念,渲染管线的每一个环节或者子任务,还可以进一步分解,你可以选择如何去完成它。例如:可以使用一下方式渲染物体:

  • Multi-pass rendering - one pass per object per light 即一个物体被"渲染多次(多个pass)",每一个影响到该物体的light,都在一个单独的pass中渲染
  • Single-pass - one pass per object 即每个物体中一个pass中渲染,也即一个物体只“渲染”一次
  • Deferred - Render surface properties to a g-buffer, perform screen space lighting 即先在g-buffer pass中把法线等几何属性“渲染”到g-buffer纹理中,然后在Lighting pass中进行屏幕空间的光照计算

当自定义SRP时,上面这些就是需要你做出决策的地方。每种技术都有许多权衡(trade offs)需要考虑,没有一种技术是完美的。

What is a Scriptable Render Pipeline

如果Render Pipeline是为了完成渲染而需要执行的许多步骤,则Scriptable Render Pipeline是一个可以让用户使用Unity的C#脚本控制的pipeline,用于以用户定义的方式完成渲染。

The Problem (Built-in pipelines存在的问题)

之前Unity已经提供了一些built-in pipelines,一些适合用于手游或者VR游戏(如 forward renderer),另一些用于高级终端游戏上,如主机游戏 (如 deferred renderer)。这些Unity内置的拿来即用的渲染方案具有通用性,“黑盒性”,但也有一些缺陷:

  • 只能用于做它们设计好的任务
  • 它们被设计的非常具有通用性,功能过于宽泛,意味着它们可以完成任何需求,但没有任何完美精通或者适用的地方。(But they need to do everything, they are masters at nothing)
  • 它们不是那可配置的。(black box with injection points)
  • 扩展和修改容易产生错误(小的内部改变可能产生大的外部影响)
  • 有许多bugs无法修复(因为擅自“修复”可能会破坏项目)。

The Solution (用SRP来解决)

SRP API的出现就是为了解决上面的问题。它把渲染部分从Unity内置的“黑盒”概念 转变成了用户可控的每个项目可脚本控制的概念。也就是说,使用SRP,每一个项目都可以有一个符合自己的特色的渲染管线。使用SRP api可以对how rendering进行精细粒度的自定义,从低层到高层(form the low level to the high level)。

SRP 概述

从高层次用户角度来看,SRP可以分为两部分:SRP asset和SRP instance。在制作custom rendering pipeline时,两者都很重要。

SRP Asset

SRP Asset是project asset,表示渲染管线的一个具体配置,例如:

  • 是否支持阴影?
  • 使用什么级别的shader quality?
  • shadow distance值是多少?
  • 默认材质配置

用户想要控制的东西可以保存为配置的一部分,基本上是任何需要序列化的东西。SRP Asset表示SRP的类型(type)和其中的设置(settings)。

SRP Instance

SRP Instance是实际完成渲染的类(class)。当Unity发现SRP被启用时,Unity会查看当前选择的SRP Asset并且要求它提供一个渲染实例("rendering instance"),即SRP asset要返回一个实例包含一个'Render'函数。通常这个实例会缓存许多对应SRP asset中的配置信息。

SRP instance表示一个已知的渲染管线配置(pipeline configuration)。从渲染器角度看,调用动作可能如下:

  • Clearing the framebuffer
  • Performing scene culling
  • Rendering sets of objects
  • Doing blits from one frame buffer to another
  • Rendering shadows
  • Applying post process effects

SRP instance表示一个实际将要被实施的渲染(actual rendering)。

SRP Asset

SRP Asset包含一个接口,用户用它配置渲染管线。当Unity第一次开始渲染时,Unity会调用 SRP Asset上的InternalCreatePipeline函数,然后,SRP Asset会返回一个可用的SRP instance。

SRP Asset是一个ScriptableObject,这意味着它可以保存为一个project asset(见下面的BasicAssetPipe)。

为了使项目启用一个SRP asset,需要在Edit>Project Settings>Graphics的Scriptable Render Pipeline Settings栏中选择你生成的SRP Asset。这样设置好后,Unity就会使用SRP Asset中的配置进行rendering了,而不再使用standard Unity rendering。

An Simple Asset Example

SRP Asset的职责是包含配置信息和返回一个渲染实例。若SRP Asset上的某个设置发生了变化,则所有由这个Asset创建的实例会被销毁,并且用新的设置创建新的实例,来进行下一帧的渲染。

下面的代码是一个简单的pipeline asset类。它包含一个color,由它返回的实例用来清除屏幕。CreateBasicAssetPipeline是在Editor下使用的一个工具, 在菜单栏上点SRP-Demo,再点01 - Create Basic Asset Pipeline即可创建一个BasicAssetPipe对应的SRP Asset,它在项目中的路径为Assets/BasicAssetPipe.asset

[ExecuteInEditMode]
public class BasicAssetPipe : RenderPipelineAsset
{
public Color clearColor = Color.green; #if UNITY_EDITOR
// Call to create a simple pipeline
[UnityEditor.MenuItem("SRP-Demo/01 - Create Basic Asset Pipeline")]
static void CreateBasicAssetPipeline()
{
var instance = ScriptableObject.CreateInstance<BasicAssetPipe>();
UnityEditor.AssetDatabase.CreateAsset(instance, "Assets/BasicAssetPipe.asset");
}
#endif // Function to return an instance of this pipeline
protected override IRenderPipeline InternalCreatePipeline()
{
return new BasicPipeInstance(clearColor);
}
}

A complete asset example

除了返回实例和保存配置,SRP asset也用于做许多辅助功能,如:

  • Default material to use when creating 3d objects
  • Default material to use when creating 2d objects
  • Default material to use when creating particle systems
  • Default material to use when creating terrain systems

The Rendering entry point

SRP asset控制渲染配置,但是最终渲染是由SRP Render Pipeline instance完成的。SRP instance对应的类(class)是渲染逻辑(rendering logic)实际存在的地方。

SRP Instance的最简单的形式仅仅包含一个Render函数。Render函数有两个参数:

  • 一个ScriptableRenderContext类型的参数,相当于一个队列,其中的元素是command buffer,可以把将要完成的渲染操作入队(enqueue)。
  • 一个相机数组Camera[],表示已经启用的,用于渲染的相机列表。

A basic pipeline

下面这个BasicPipeInstance类就是上面的BasicAssetPipeIRenderPipeline函数返回的SRP Instance。

public class BasicPipeInstance : RenderPipeline
{
private Color m_ClearColor = Color.black; public BasicPipeInstance(Color clearColor)
{
m_ClearColor = clearColor;
} public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
// does not so much yet :()
base.Render(context, cameras); // clear buffers to the configured color
var cmd = new CommandBuffer();
cmd.ClearRenderTarget(true, true, m_ClearColor);
context.ExecuteCommandBuffer(cmd);
cmd.Release();
context.Submit();
}
}

上面代码所表示的pipeline仅仅完成了简单地用给定的颜色(由SRP Asset指定)清除屏幕的操作。注意:

  • Unity现有的CommandBuffers被用来完成许多操作(此处用于完成ClearRenderTarget)。
  • CommandBuffers是根据于对应的context进行调试的(scheduled)。
  • “渲染”的最后一步是调用Submit,它将(引起)执行所有入队的command。

RenderPipelineRender函数就是你输入渲染代码,来自定义renderer的地方。Culling, Filtering, Changing render targets和Drawing操作都是在这里完成的。这就是你构造渲染器的地方!

SRP使用延迟执行的方式进行渲染。作为用户,你的任务就是用ScriptableRenderContext构建一个命令队列,然后去执行它们。

Culling

Culling(剔出)是指明将要在屏幕上渲染什么的过程。

Unity的Culling过程包含两类:

  • Frustum culling: 视锥体剔出,计算在视锥体内的物体(这些物体会被渲染)。
  • Occlusion culling: 遮挡剔出,计算被别的物体遮挡的物体,这些物体将不会被渲染。

渲染开始时,首先要计算需要渲染哪些物体。这涉及到获取相机,然后完成剔出(cull)操作(从给定相机的角度)。Culling操作返回一个对于给定相机有效的物体(objects和lights)列表,这些物体用于该相机进行后面的渲染步骤。

Culling in SRP

在SRP,用户通常站在相机的视角来渲染对象。这和Unity built-in rendering一样。SRP提供了一些有用的Culling相关的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);

cullResults被用来完成接下来的rendering。

SRP Drawing

经过上面的步骤,该剔出的物体已经被剔出了,现在可把cull results渲染到屏幕上了。

需要提前做一些决策(考虑到以下因素):

  • 完成渲染的硬件
  • 想要实现的具体样子(specific look)和风格/感觉(feel),即要实现的效果
  • 项目的类型

如,一个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

目前,我们将要展示一个简单的没有光照、可以渲染一些不透明物体的渲染器。

Filtering: Render Buckets and Layers

通常,渲染对象(rendering object)有一个具体的分类:透明的、不透明的、sub surface,或者别的类型。Unity使用队列表示什么时候一个对象将会被渲染,即相同分类的对象被放在同一个队列中,这些队列也被称为“桶”(bucket)。在SRP中,用户指定使用哪些桶进行渲染。

除了“桶”的概念,标准的Unity layers也可以被使用。

这提供了额外的过滤能力。

// 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;

Draw Settings: How things should be drawn

上面的filtering和culling决定了将要渲染哪些对象。接下来,我们需要决定怎样渲染它们。SRP提供了许多可配置选项。用于配置的数据结构是DrawRenderSettings。它允许配置以下选项:

  • Sorting - 物体渲染的顺序,如:从前到后(front to back)或者从后到前(back to front)。
  • Per Renderer flags - Unity应该向shader传入什么'built in'设置,包括:per object light probes, per object light maps等。
  • Rendering flags - 使用哪种batching算法,instancing vs non-instancing。
  • Shader Pass - 当前draw call应该使用哪个shader pass
// Create the draw render settings
// note that it takes a shader pass name
var drs = new DrawRendererSettings(myCamera, 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;

Drawing

现在我们已经有了发起一次draw call所需要的三样东西:

  • Culling results (剔出结果)
  • Filtering rules (过滤规则)
  • Drawing rules (绘制规则)

下面的代码发起了一次draw call。在SRP中,你一般不渲染单独的一个或几个网格(individual meshes),而是发起一次draw call,一次渲染一大批网格。这能减少执行开销。

发起一次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来切换不同的渲染目标。

参考:

  1. Scriptable Render Pipeline Wiki
  2. Unity SRP Overview: Scriptable Render Pipeline

第一次发表于我的知乎专栏:https://zhuanlan.zhihu.com/p/69046003

Scriptable Render Pipeline的更多相关文章

  1. Scriptable render pipeline unity

    https://www.youtube.com/watch?v=zbjkEQMEShM LWRP https://blogs.unity3d.com/cn/2018/02/21/the-lightwe ...

  2. 可编程渲染管线(Scriptable Render Pipeline, SRP)

    原文链接 可编程渲染管线处理数据的流程可分为以下3大阶段 1. 应用阶段 这个阶段大概会由CPU处理4件事情.首先会对模型数据进行可见性判断.模型数据由顶点位置.法线方向.顶点颜色.纹理坐标等构成.然 ...

  3. 聊聊2018.2的Scriptable Build Pipeline以及构建Assetbundle

    0x00 前言 在这篇文章中,我们选择了过去几周Unity官方社区交流群以及UUG社区群中比较有代表性的几个问题,总结在这里和大家进行分享.主要涵盖了Scriptable Build Pipeline ...

  4. 1.5:Unity Render Pipeline

    文章著作权归作者所有.转载请联系作者,并在文中注明出处,给出原文链接. 本系列原更新于作者的github博客,这里给出链接. 这一节主要是为上一节中没有提到的一些概念作补充. 上一节提到了Unity中 ...

  5. [Unity] Shader Graph Error 当前渲染管道与此主节点不兼容(The current render pipeline is not compatible with this master node)

    Shader Graph Error  : The current render pipeline is not compatible with this master node 问题产生环境: Un ...

  6. Lightweight Render Pipeline

    (翻译) Lightweight Render Pipeline (LWRP),轻量级渲染管线,是一个Unity预制的Scriptable Render Pipeline (SRP).LWRP可以为移 ...

  7. 1.3:Render Pipeline and GPU Pipeline

    文章著作权归作者所有.转载请联系作者,并在文中注明出处,给出原文链接. 本系列原更新于作者的github博客,这里给出链接. 在学习SubShader之前,我们有必要对 Render Pipeline ...

  8. unity render pipeline

    post process v2 GUI temp8->TaregtPool0->temp8       tem8                      temp8->backbu ...

  9. 开发自定义ScriptableRenderPipeline,将DrawCall降低180倍

    0x00 前言 大家都知道,Unity在2018版本中正式推出了Scriptable Render Pipeline.我们既可以通过Package Manager下载使用Unity预先创建好的Ligh ...

随机推荐

  1. 基于React.js网页版弹窗|react pc端自定义对话框组件RLayer

    基于React.js实现PC桌面端自定义弹窗组件RLayer. 前几天有分享一个Vue网页版弹框组件,今天分享一个最新开发的React PC桌面端自定义对话框组件. RLayer 一款基于react. ...

  2. 雪花算法 Java 版

    雪花算法根据时间戳生成有序的 64 bit 的 Long 类型的唯一 ID 各 bit 含义: 1 bit: 符号位,0 是正数 1 是负数, ID 为正数,所以恒取 0 41 bit: 时间差,我们 ...

  3. Python学习随笔:使用xlwings设置和操作excel多行多列数据以及设置数据字体颜色填充色对齐方式的方法

    ☞ ░ 前往老猿Python博文目录 ░ 在前面老猿的文章中,<Python学习随笔:使用xlwings读取和操作Excel文件>.<Python学习随笔:使用xlwings读取和操 ...

  4. PyQt(Python+Qt)学习随笔:QTreeWidgetItem项的子项索引、删除子项的方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 树型部件QTreeWidget中的QTreeWidgetItem项: 获取子项索引 可通过index ...

  5. JDBC(一)—— JDBC概述

    Jdbc概述 Java DataBase connectivity(Java语言连接数据库) Jdbc本质是什么? 是Sun公司制定的一套接口,java.sql.* 接口都有调用者和实现者 面向接口调 ...

  6. 半夜删你代码队 Day5冲刺

    一.每日站立式会议 1.站立式会议 成员 昨日完成工作 今日计划工作 遇到的困难 陈惠霖 跟进作业 完成注册界面 相关界面设计内容知识不充足 侯晓龙 开始双人合作 开始与数据库结合 无 周楚池 初步完 ...

  7. AcWing 1194. 岛和桥

    \(f[s][i][j]\) 表示一条有向路径(不经过重复点),当前路径点集合为 \(s\),最后两个点是 \(j\) → \(i\) 的最大价值 \(g[s][i][j]\) 类似,不过是方案数. ...

  8. HDU3686 Traffic Real Time Query System

    P.S.此题无代码,只有口胡,因为作者码炸了. 题目大意 给你一个有 \(n\) 个点, \(m\) 条边的无向图,进行 \(q\) 次询问,每次询问两个点 \(u\) \(v\),输出两个点的之间的 ...

  9. 深入分析 Java、Kotlin、Go 的线程和协程

    前言 协程是什么 协程的好处 进程 进程是什么 进程组成 进程特征 线程 线程是什么 线程组成 任务调度 进程与线程的区别 线程的实现模型 一对一模型 多对一模型 多对多模型 线程的"并发& ...

  10. Angular:路由的配置、传参以及跳转

    ①路由的配置 1.首先用脚手架新建一个项目,在路由配置时选择yes 2.用ng g component创建组件 3.在src/app/app-routing.module.ts中配置路由 import ...