ScriptableRenderPipelineでアプリに最適な描画をしよう
2018/7/13
講演者名 黒河 優介
所属団体 Unity Technologies Japan
肩書・役職 Developer Relation Engineer
•
•
•
•
•
• Unity RenderPipeline
• ScriptableRenderPipeline(SRP)
•
※ ForwardRendering
Unity
•
•
• Shadow
Opaque
→ Depth Test
Transparent
→
Opaque
Opaque
Opaque
Opaque
Z-Buffer ZTest
Z-Test
Transparent
Transparent
Transparent
Transparent
• Camera Depth
• Renderer sortingLayerName
• Renderer sortingOrder
• MeshRenderer sortingOrder
• Material.renderQueue
地面の部分は3つのオブジェクトを描画後に描画して、塗るピクセル数を
減らしてほしいが、一番最初に描画されてしまっています。
これはRendererのBoundingBoxの中心位置で手前・奥の判定を行っている
ため、地面が一番手前と判定されてしまっています。
まず最初に三つのオブジェクトが描画されて…
その後に地面が描画されることが理想形です。
Unity
…
vertex
このようにポイントライト3つ
あるシーンを描画する場合…
まず、ポイントライト適応前の状態を描画します
その後、ポイントライト一つ一つ当たった状態のオブ
ジェクトを描画して加算合成していきます。
そのため余計な描画負荷を伴います
Point Light....
Shadow
Shadow
QualitySettings
Shadow …
Unity
ScriptableRenderPipeline(SRP)
• SRP
• LightWeightRenderPipeline(LWRP)
•
SRP
• Unity
SRP
•
•
• Shadow
•
SRP
// 描画時にコールバックされます。引数contextに対して描画命令を発行していきます
void OnRender(ScriptableRenderContext context, Camera[] cameras) {
foreach (var camera in cameras){
// まず Camera毎のShader のパラメーターセットアップして…
SetupShaderParams(camera);
// スクリプトで決めたルールでカリングをして…
var res = CullResults.Cull(ref cullingParams, context, ref cull);
// そしてカリングして得た Rendererを引数で指定したルールで描画します
context.DrawRenderers(res.visibleRenderers, ref settings, filterSettings);
}
}
SRP
1.RenderPipelineAsset/RenderPipeline
2. RenderPipelineAsset Asset
GraphicsSettings.renderPipelineAsset
3.Shader Renderpipeline
LightMode
1.
[ExecuteInEditMode]
public class MySRPAsset : RenderPipelineAsset{
protected override IRenderPipeline InternalCreatePipeline(){
return new MyScriptableRenderPipelineInstance();
}
}
public class MyScriptableRenderPipelineInstance : RenderPipeline{
// 描画のタイミングでコールバックされます
public override void Render(ScriptableRenderContext context, Camera[] cameras){
base.Render(context, cameras);
foreach (var camera in cameras){
// 描画処理をここに書いていきます
}
}
}
2-1. GraphicsSettings
var instance = ScriptableObject.CreateInstance<MySRPAsset>();
AssetDatabase.CreateAsset(instance, "Assets/MyScriptableRenderPipeline.asset");
上記のようなEditor拡張で、独自のRenderPipelineAsset
のアセットを作成します
2-2. GraphicsSettings
先ほど作成したRenderPipelineAssetを指定します
3. Shader
RenderPipeline Renderer ShaderPassName
Pass
Shader Pass LightMode
var settings = new DrawRendererSettings(camera, new ShaderPassName("BasicPass"));
settings.sorting.flags = SortFlags.CommonOpaque;
var filterSettings = new FilterRenderersSettings(true)
{
renderQueueRange = RenderQueueRange.opaque,
layerMask = 1 << LayerDefine.BG
};
context.DrawRenderers(cull.visibleRenderers, ref settings, filterSettings);
SubShader{
Tags { "Queue"=“Geometry" "RenderType"=“Opaque"}
Pass{
Tags { "LightMode" = "BasicPass"}
....処理等
}
}
C#側の処理
Shader側
var settings = new DrawRendererSettings(camera, new ShaderPassName("BasicPass"));
settings.sorting.flags = SortFlags.CommonOpaque;
var filterSettings = new FilterRenderersSettings(true)
{
renderQueueRange = RenderQueueRange.opaque,
layerMask = 1 << LayerDefine.BG
};
context.DrawRenderers(cull.visibleRenderers, ref settings, filterSettings);
SubShader{
Tags { "Queue"=“Geometry" "RenderType"=“Opaque"}
Pass{
Tags { "LightMode" = "BasicPass"}
....処理等
}
}
C#側の処理
Shader側 描画設定で指定したShaderPassNameと
ShaderのLightModeを一致させる必要が
あります。
SRP
SRP
Camera
ImageEffect
public class MyScriptableRenderPipelineInstance : RenderPipeline{
// 描画のタイミングでコールバックされます
public override void Render(ScriptableRenderContext context, Camera[] cameras){
base.Render(context, cameras);
foreach (var camera in cameras){
// 描画処理をここに書いていきます
}
// ImageEffect等の実装はココで実行されるようにする等が必要です
}
}
SRP
Surface Shader
SRP vertex shader / fragment shader
2018.1 ShaderGraph Shader
ScriptableRender Pipeline
LightWeightRenderPipeline(LWRP)
SRP 0
Unity
LightWeithRenderPipeline(LWRP)
HDRenderPipeline(HDRP)
LightWeightRenderPipeline(LWRP)
Unity (Built-In) RenderPipeline
LWRP URL
https://blogs.unity3d.com/jp/2018/02/21/the-lightweight-render-
pipeline-optimizing-real-time-performance/
LightWeightRenderPipeline(LWRP)
標準のレンダリングパイプラインで問題だったポイントライトの加算問題も
LWRPでは起こりません。ピクセルシェーダー側でキチンとポイントライトを
扱い1パスで描画されます
LWRP
• PackageManager
•
• github
•
https://github.com/Unity-Technologies/ScriptableRenderPipeline
PackageManager LWRP
LightweightRenderPipelineをInstall
するだけです
LWRP
• Shader LWRP
• Unity Shader Material
Editor
LWRP
• Shader LWRP
• LightMode LightweightForward
• ShadowMapTexture
Shadow
参考URL
https://connect.unity.com/p/lightweightrenderpipelinexiang-kenishaderwoshu-itemita
LWRP
• LightWeightRenderPipeline
• PostProcessingStack v2
• ShaderGraph
• Core RP
• [ShaderIncludePath]
• Stripping scriptable shader variants(2018.2 )
• SRP Batcher
[ShaderIncludePath]
• Unity Shader include
• Assets or
[ShaderIncludePath]
public class ShaderIncludePathSetting{
[ShaderIncludePath]
public static string[] GetPaths(){
return new[]{
"Assets/SRP/Core/",
"Assets/SRP/LightWeightPipeline/",
};
}
} Shader内のincludeをコチラからの相対パスで
指定できるようになります。
[ShaderIncludePath]
• Github LWRP
Shader ShaderIncludePath
Stripping scriptable shader variants
• Shader variants Shader
•
•
参考URL
https://blogs.unity3d.com/2018/05/14/stripping-scriptable-shader-variants/
Stripping scriptable shader variants
LWRP
• LWRP 2.0
Stripping scriptable shader variants
• LWRP 2.x Unity2018.1
• LWRP 2.0 Renderpipeline
SRP Batcher
• CPU
• Shader
• GPU
• 5
• DirectX11/Metal Experimental
2018.3 Vulkan Experimental
SRP Batcher
GraphicsSettings.useScriptableRenderPipelineBatching= true;
※LWRP/HDRP Unity CBuffer
SRP Batcher
DrawCall CPU
GPU
Material ConstantBuffer
Material CPU
SRP Batcher (RenderDoc )
SRP Batcher有効時 SRP Batcher無効時
SRP Batcher (RenderDoc )
SRP Batcher有効時 SRP Batcher無効時
ドローコール数や順番はSRP Batcherの有無で
は変わっていません。
SRP Batcher (RenderDoc )
SRP Batcher有効時 SRP Batcher無効時
実際に描画する前に行うShaderパラメーター
のセット周りの処理が異なっています
• 0 RenderPipeline
• LWRP RenderPipeline
0 RenderPipeline
キャラクターは板ポリゴンに
スプライトを貼っただけ
RenderPipeline …
RenderPipeline …
Transparentは奥から手前を
遵守します
RenderPipeline …
Materialの切り替えを都度しな
がら奥から手前へ描画します
RenderPipeline …
そのため、描画するのに非常に多くの
Material切り替えが発生しました
Material …
…
ScriptableRender Pipeline
…
var pass = new ShaderPassName("BasicPass");
var settings = new DrawRendererSettings(camera, pass);
settings.sorting.flags = SortFlags.CommonOpaque;
var filterSettings = new FilterRenderersSettings(true){
renderQueueRange = RenderQueueRange.transparent,
layerMask = 1 << LayerDefine.BG
};
context.DrawRenderers(cull.visibleRenderers,
ref settings,
filterSettings);
…
var pass = new ShaderPassName("BasicPass");
var settings = new DrawRendererSettings(camera, pass);
settings.sorting.flags = SortFlags.CommonOpaque;
var filterSettings = new FilterRenderersSettings(true){
renderQueueRange = RenderQueueRange.transparent,
layerMask = 1 << LayerDefine.BG
};
context.DrawRenderers(cull.visibleRenderers,
ref settings,
filterSettings);
描画する時のShaderのPass名
を指定できます
…
var pass = new ShaderPassName("BasicPass");
var settings = new DrawRendererSettings(camera, pass);
settings.sorting.flags = SortFlags.CommonOpaque;
var filterSettings = new FilterRenderersSettings(true){
renderQueueRange = RenderQueueRange.transparent,
layerMask = 1 << LayerDefine.BG
};
context.DrawRenderers(cull.visibleRenderers,
ref settings,
filterSettings);
描画する時のソート順を指
定できます
…
var pass = new ShaderPassName("BasicPass");
var settings = new DrawRendererSettings(camera, pass);
settings.sorting.flags = SortFlags.CommonOpaque;
var filterSettings = new FilterRenderersSettings(true){
renderQueueRange = RenderQueueRange.transparent,
layerMask = 1 << LayerDefine.BG
};
context.DrawRenderers(cull.visibleRenderers,
ref settings,
filterSettings);
描画する対象のオブジェクトの
Opaque・Transparent等を指定
できます。
…
var pass = new ShaderPassName("BasicPass");
var settings = new DrawRendererSettings(camera, pass);
settings.sorting.flags = SortFlags.CommonOpaque;
var filterSettings = new FilterRenderersSettings(true){
renderQueueRange = RenderQueueRange.transparent,
layerMask = 1 << LayerDefine.BG
};
context.DrawRenderers(cull.visibleRenderers,
ref settings,
filterSettings);
Layer単位で描画するオブジェクト
を切り替えられます。
Shader Z Pre-Pass
SubShader{
Tags { "Queue"="Transparent" "RenderType"="Transparent"}
// Z Pre-Pass
Pass{
Tags { "LightMode" = "ZPrepass"}
ZWrite On
ColorMask 0
//...実際の処理
}
// 実際の描画用のパス
Pass{
Tags { "LightMode" = "BasicPass"}
ZWrite Off
ZTest Equal
Blend SrcAlpha OneMinusSrcAlpha
//...実際の処理
}
}
SRP
Depth
αが一定値以下の場合はdiscard
して描画を行うShader Pass
「ZPrepass」で一旦キャラク
ターを描画します。
※Z Bufferの様子です
その後 床を描画します
キャラクターを通常のShaderPassで描画します。
この時、マテリアル切り替えを最小に抑える順番になるように
sorting.flagに「SortFlags.OptimizeStateChanges」を指定します
最後に影を描画します
Z Pre-Pass
※α …
Z BufferのZ値と一致した部分のピクセルでない
と、Zテストを通らず、ピクセルが塗られないた
め絵が破綻しません
Material
LWRP
背景からキャラクター。
キャラクターから背景への影
はリアルタイムShadowで、他
は焼いたShadowです。
LWRP
わかりやすくするため、
リアルタイムのShadowのみ青
くしました。
…
Shadow
→
Shadow
→
→
→
LWRP …
焼いたShadowの上からリアル
タイムのShadowも描いてしま
います
LWRP …
それを防ごうとすると、
キャラクターへのShadowが
なくなってしまいます
• Shadow
• Shadow
•
始めにキャラクターだけの
ShadowMapを生成します
キャラクターだけのShadowMapを
適応して背景を描きます
キャラクターだけのShadowMapに
背景オブジェクトも描き足します。
最後にキャラクターを描画します。
// 描画時にコールバックされます。引数contextに対して描画命令を発行していきます
void OnRender(ScriptableRenderContext context, Camera[] cameras) {
foreach (var camera in cameras){
// 「Chara」レイヤーだけ先にShadowを処理します
ShadowPass( 1 << Layers.Chara);
// 「Chara」レイヤー以外のオブジェクトをレンダリングします
DrawObjects( ~( 1<< Layers.Chara) );
// 「Chara」レイヤー以外のShadowを処理します
ShadowPass( ~( 1 << Layers.Chara) );
// 「Chara」レイヤーのオブジェクトだけをレンダリングします
DrawObjects( 1<< Layers.Chara );
}
}
通常では、SelfShadowが発
生してしまいます
今回の特殊パスを用いれば、
キャラクターのSelfShadowのみオフ
に出来ます
SRP ShadowPass
ScriptableRenderPipeline
•
•
• Shadow
• Unity LWRP
3
Appendix
0
https://github.com/wotakuro/CustomScriptRenderPipelineTest
LWRP
https://github.com/wotakuro/MixedShadowSample
SRP
https://connect.unity.com/u/5768a3b332b306001aaf2d1f

【GTMF2018TOKYO】ScriptableRenderPipelineでアプリに最適な描画をしよう