# [翻譯]Scriptable Render Pipeline Overview
#### [原文](https://blogs.unity3d.com/2018/01/31/srp-overview/) by Tim Cooper, January 31, 2018
> Scriptable Render Pipeline (SRP), 在 2018.1 beta 開始引入, 是一個在 Unity 中使用 C# 配置和執行渲染的方法. 在撰寫自定義渲染管道之前, 很重要的是先了解我們所謂的渲染管道是意味著甚麼.
## What is a Render Pipeline
“Render Pipeline(渲染管道)” 是將物件放到螢幕上的數個技術的總稱. 它涵蓋了幾個高層級的部分:
- Culling(剔除)
- Rendering Objects(渲染物件)
- Post processing(後期製作, 或稱**後製**)
除了這些高層級的概念之外, 每個職責還可以根據你的實作需求細分它們. 例如, 渲染物件可以分成如下:
- Multi-pass rendering
+ 每個物件對每個光源對應一個 pass.
- Single-pass
+ 每個物件對應一個 pass.
- Deferred
+ 渲染表面屬性至 g-buffer, 接著進行 screen space lighting(螢幕空間的光照技術).
當你撰寫一個自製的 SRP 時, 你需要進行幾個決策. 每種技術都有數個因素需要權衡.
{%youtube 2wUPgl7upnU %}
## Demo Project
所有在此文章會提及的功能都含在[GitHub上的demo專案](https://github.com/stramit/SRPBlog/tree/master/SRP-Demo)裡面.
## The Rendering entry point
當你使用 SRP 時, 需要定義一個類別來控制渲染; 這就是你將要創建的渲染管道. 進入點是對 `Render` 的呼叫, 它會傳遞渲染內容(下面會詳述)以及需要渲染的攝影機陣列.
```csharp=
public class BasicPipeInstance : RenderPipeline
{
public override void Render(ScriptableRenderContext context, Camera[] cameras){}
}
```
## The Render Pipeline Context
SRP 渲染使用延遲執行的概念. 作為使用者的你, 建立一個指令清單並執行它們. 用來建構這些指令的物件稱之為 `ScriptableRenderContext`. 當你操作完 render context 後, 接著呼叫 `Submit` 以送出隊列中的 draw calls.
這個範例使用 command buffer 來清除渲染目標, 並由 render context 執行:
```csharp=
// 創建一個新的 command buffer
// 用來觸發指令給 render context
var cmd = new CommandBuffer();
// 觸發一個清除繪製目標的指定
cmd.ClearRenderTarget(true, false, Color.green);
// 排進 command buffer
context.ExecuteCommandBuffer(cmd);
```
")
[這裡](https://github.com/stramit/SRPBlog/blob/master/SRP-Demo/Assets/SRP-Demo/1-BasicAssetPipe/BasicAssetPipe.cs)是單純清除螢幕的繪製管道的完整內容.
## Culling
剔除(Culling)是用來確定哪些東西要呈現在螢幕上的過程.
在 Unity 中 Culling 包含:
- 視錐體剔除(Frustum culling): 計算物件存在於攝影機的遠/近截面之間.
- 遮擋剔除(Occlusion culling): 計算那些物件是隱藏在其他物件之後, 並渲染時排除之. 更多的訊息請見 [Occlusion Culling文件](https://docs.unity3d.com/Manual/OcclusionCulling.html).
當渲染開始時, 首先要被計算的就是那些物件要被渲染. This involves taking the camera and performing a cull operation from the perspective of the camera. 剔除操作會傳回一份對於該攝影機有效的物件和燈光的清單. 這些物件在渲染管道中接著會用到.
:::warning
> This involves taking the camera and performing a cull operation from the perspective of the camera.
>
這段不知道要怎翻譯, 但不影響整體理解.
:::
### Culling in SRP
在 SRP 中, 通常是從攝影機的視角實行渲染. 這和 Unity 內建渲染所使用的攝影機物件是相同的. SRP 提供數個 API 來替除. 常見的流程如下:
```csharp=
// 產生一個結構來存放剃除用的參數
ScriptableCullingParameters cullingParams;
// 從攝影機產生剃除參數
if (!CullResults.GetCullingParameters(camera, stereoEnabled, out cullingParams))
continue;
// 如果你願意, 可以在這裡修改剃除參數
cullingParams.isOrthographic = true;
// 產生一個結構用來存放剔除結果
CullResults cullResults = new CullResults();
// 執行剔除操作
CullResults.Cull(ref cullingParams, context, ref cullResults);
```
現在可以使用產生出來的剃除結果來渲染了.
## Drawing
現在我們有一組剃除後的結果, 我們可以將他們渲染到螢幕上.
但是因為有很多配置物件的方式, 所以在此之前要先做一些決定. 其中多數的決定因素為:
- 你的渲染管道鎖定的硬體環境
- 你希望達到的視覺觀感
- 你想要做的專案類型
試想一個移動平台的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
通常渲染物件都會有個特定的分類, 它們是不透明的、透明的、次表面的(sub surface)或其他類別. 在 Unity 裡面使用隊列的概念來表示一個物件何時該被渲染, 這些隊列形成 buckets[^buckets] 來讓物件(來自物件上的 material)被放置進去. 從 SRP 調用渲染時, 你可以指定要使用的 bucket 範圍.
[^buckets]: 有點難翻譯的部分, 這裡泛指存放空間或儲存容器.
除了 bucket 外, Unity 內建的 layer 也可以被用來篩選.
這裡提供了透過 SRP 繪製物件時, 進行額外的過濾功能.
```csharp=
// 產生不透明物件的篩選設定物件
var opaqueRange = new FilterRenderersSettings();
// 設定不透明物件的隊列範圍
opaqueRange.renderQueueRange = new RenderQueueRange()
{
min = 0,
max = (int)UnityEngine.Rendering.RenderQueue.GeometryLast,
};
// 涵蓋所有 layer
opaqueRange.layerMask = ~0;
```
## Draw Settings: How things should be drawn
使用過濾和剃除確認該被渲染的內容, 接著我們需要決定如何繪製它們. SRP 提供了多種選項配置來控制通過篩選的物件該如何被渲染. 這個用來配置的資料格式是 `DrawRenderSettings`. 這格式允許配置許多東西:
- Sorting - 物件被渲染的順序, 像是 `由後而前` 以及 `由前而後` 的順序.
- Per Renderer flags - 應該准許哪些 Unity `內建` 的設定給 shader, 這包含了逐物件的 light probes, 逐物件的 light maps, 以及其他類似的東西.
- Rendering flags - 指定批次處理的演算法, instancing vs non-instancing[^instancing].
- Shader Pass - 目前的 draw call 中要使用的 shader pass.
[^instancing]: 這裡是指 Unity 的 GPU instancing 技術.
https://docs.unity3d.com/Manual/GPUInstancing.html
```csharp=
// 產生一個繪製參數物件
// 注意它有帶著一個 shader pass 的名稱
var drs = new DrawRendererSettings(Camera.current, new ShaderPassName("Opaque"));
// 在此 draw call 中啟用 instancing
drs.flags = DrawRendererFlags.EnableInstancing;
// 將 light probe & lightmap 的資料傳給各 renders
drs.rendererConfiguration = RendererConfiguration.PerObjectLightProbe | RendererConfiguration.PerObjectLightmaps;
// 如同一般的不透明物件般地排序
drs.sorting.flags = SortFlags.CommonOpaque;
```
## Drawing
現在我們有三個東西需要去觸發一個 draw call:
- 剔除結果
- 篩選規則
- 繪製規則
我們可以觸發一個 draw call! 與 SRP 內所有的東西一樣, draw call 可視為一個呼叫放進 context 內. 在 SRP 中, 你通常不會只渲染單一網格, 而是在一次呼叫中讓所有的 renders 同時渲染. 這樣可以減少執行腳本的開銷, 同時可以在 CPU 上快速作業執行(fast jobified execution).
為了觸發 draw call, 我們組合了前面建造出來的方法們.
```csharp=
// 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();
```
這會把物件繪製到目前綁定的渲染目標上. 如果需要, 你可以使用 command buffer 切換渲染目標.

渲染不透明物件的渲染器的程式碼可以在這裡找到:
https://github.com/stramit/SRPBlog/blob/master/SRP-Demo/Assets/SRP-Demo/2-OpaqueAssetPipe/OpaqueAssetPipe.cs
這個範例進一步增加透明物件的渲染:
https://github.com/stramit/SRPBlog/blob/master/SRP-Demo/Assets/SRP-Demo/3-TransparentAssetPipe/TransparentAssetPipe.cs
這裡有個很重要需要注意的一點是, 渲染透明物件時, 順序會更改為`由後至前`.

我們希望這篇文章可以幫助你開始撰寫自己的 SRP.
##### translated by NaCl at 2018/09/03