GenerativeArt–MadewithUnity
Customizing a Production Pipeline
(Lightweight Render Pipeline)
Felipe Lira
Graphics Programmer
Unity Technologies
1
Note: If you are using a laptop download Unity 2018.2
and the following project: bit.ly/siggraph18
2
Workshop Pre-requisites
●If you are on a SIGGRAPH computer
○ Start Unity and open SIGGRAPH2018-WORKSHOP
project in your Desktop
●If you are on your own computer
○ Download Unity 2018.2
○ Download the following project bit.ly/siggraph18
○ Start Unity and open the project you downloaded
●Slides: bit.ly/siggraph18_slides
3
Workshop Agenda
●What is the Lightweight Render Pipeline (LWRP)
●Add LWRP to your project
●Convert a project to use LWRP
●Overview of LWRP rendering
●Customize LWRP renderer by adding custom passes
●Override LWRP renderer to implement a deferred renderer
4
What is Lightweight Render Pipeline (LWRP)
●It’s a Scriptable Render Pipeline (SRP)
○ Rendering API exposed in C#
○ Open Source: https://github.com/Unity-Technologies/ScriptableRenderPipeline
●LWRP
○ Currently 1 of 2 of our inhouse Render Pipelines we are developing
○ Designed from the ground up to perform at best on mobile hardware
○ Modern APIs in mind, as well as Legacy APIs, eg GLES 2.0
5
Why should you care about SRP and LWRP
●If you are
○ Student:
■ Learn Graphics Programming with a robust API
○ Game Developer:
■ Performance out of the box, explicit, lean and extensible
○ Researchers:
■ Implement, deploy and profile your research many platforms
6
7
Checkpoint!
●Open DemoScene
●You should see something like this
8
How to get SRP and LWRP
●In this workshop you already have it!
○ The project you are using is bundled with LWRP for simplicity
9
How to get SRP and LWRP
●You can also get it by creating a new project with LWRP
template
10
How to get SRP and LWRP
●Or by downloading from package manager UI
11
So now you have it!
● Let’s convert your project to use LWRP
12
Checkpoint!
●Created LWRP asset
○ Assets -> Create -> Rendering -> Lightweight Pipeline Asset
●Assigned LWRP to Graphics Settings
○ Edit -> Project Settings -> Graphics Settings
○ Drag and Drop LWRP asset in SRP settings
●Upgraded materials to LWRP materials
○ Edit -> Render Pipeline -> Upgrade Project Materials …
●Checked rendering settings in LWRP asset
●You should have the same image as before converting
13
Before coding let’s take a look at
core LWRP rendering
14
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
SetupPerFrameShaderConstants();
foreach (Camera camera in cameras)
{
CameraData cameraData;
InitializeCameraData(camera, out cameraData);
SetupPerCameraShaderConstants(cameraData);
CullResults.Cull(cullingParameters, context, cullResults);
List<VisibleLight> visibleLights = cullResults.visibleLights;
RenderingData renderingData;
InitializeRenderingData(cameraData, visibleLights, out renderingData);
var setup = cameraData.camera.GetComponent<IRendererSetup>();
if (setup == null)
setup = defaultRendererSetup;
setup.Setup(renderer, context, cullResults, renderingData);
renderer.Execute(context, cullResults, renderingData);
context.Submit();
}
}
15
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
SetupPerFrameShaderConstants();
foreach (Camera camera in cameras)
{
CameraData cameraData;
InitializeCameraData(camera, out cameraData);
SetupPerCameraShaderConstants(cameraData);
CullResults.Cull(cullingParameters, context, cullResults);
List<VisibleLight> visibleLights = cullResults.visibleLights;
RenderingData renderingData;
InitializeRenderingData(cameraData, visibleLights, out renderingData);
var setup = cameraData.camera.GetComponent<IRendererSetup>();
if (setup == null)
setup = defaultRendererSetup;
setup.Setup(renderer, context, cullResults, renderingData);
renderer.Execute(context, cullResults, renderingData);
context.Submit();
}
}
Called from C++ for:
● Scene
● Offscreen
● Editor
● Preview
● Reflection
16
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
SetupPerFrameShaderConstants();
foreach (Camera camera in cameras)
{
CameraData cameraData;
InitializeCameraData(camera, out cameraData);
SetupPerCameraShaderConstants(cameraData);
CullResults.Cull(cullingParameters, context, cullResults);
List<VisibleLight> visibleLights = cullResults.visibleLights;
RenderingData renderingData;
InitializeRenderingData(cameraData, visibleLights, out renderingData);
var setup = cameraData.camera.GetComponent<IRendererSetup>();
if (setup == null)
setup = defaultRendererSetup;
setup.Setup(renderer, context, cullResults, renderingData);
renderer.Execute(context, cullResults, renderingData);
context.Submit();
}
}
Shader constants setup per
access frequency
● PerFrame
● PerCamera
● PerPass
● PerObject
17
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
SetupPerFrameShaderConstants();
foreach (Camera camera in cameras)
{
CameraData cameraData;
InitializeCameraData(camera, out cameraData);
SetupPerCameraShaderConstants(cameraData);
CullResults.Cull(cullingParameters, context, cullResults);
List<VisibleLight> visibleLights = cullResults.visibleLights;
RenderingData renderingData;
InitializeRenderingData(cameraData, visibleLights, out renderingData);
var setup = cameraData.camera.GetComponent<IRendererSetup>();
if (setup == null)
setup = defaultRendererSetup;
setup.Setup(renderer, context, cullResults, renderingData);
renderer.Execute(context, cullResults, renderingData);
context.Submit();
}
}
Camera Related Rendering:
● HDR
● MSAA
● Shadows
● Render Scale
● PostProcessing
● Stereo Rendering
18
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
SetupPerFrameShaderConstants();
foreach (Camera camera in cameras)
{
CameraData cameraData;
InitializeCameraData(camera, out cameraData);
SetupPerCameraShaderConstants(cameraData);
CullResults.Cull(cullingParameters, context, cullResults);
List<VisibleLight> visibleLights = cullResults.visibleLights;
RenderingData renderingData;
InitializeRenderingData(cameraData, visibleLights, out renderingData);
var setup = cameraData.camera.GetComponent<IRendererSetup>();
if (setup == null)
setup = defaultRendererSetup;
setup.Setup(renderer, context, cullResults, renderingData);
renderer.Execute(context, cullResults, renderingData);
context.Submit();
}
}
Visible:
● Renderers
● Lights
● ...
19
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
SetupPerFrameShaderConstants();
foreach (Camera camera in cameras)
{
CameraData cameraData;
InitializeCameraData(camera, out cameraData);
SetupPerCameraShaderConstants(cameraData);
CullResults.Cull(cullingParameters, context, cullResults);
List<VisibleLight> visibleLights = cullResults.visibleLights;
RenderingData renderingData;
InitializeRenderingData(cameraData, visibleLights, out renderingData);
var setup = cameraData.camera.GetComponent<IRendererSetup>();
if (setup == null)
setup = defaultRendererSetup;
setup.Setup(renderer, context, cullResults, renderingData);
renderer.Execute(context, cullResults, renderingData);
context.Submit();
}
}
Data-drive rendering!
Rendering Data Based on:
● Cull Results
● Quality Settings
● Rendering Features
● Platform
20
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
SetupPerFrameShaderConstants();
foreach (Camera camera in cameras)
{
CameraData cameraData;
InitializeCameraData(camera, out cameraData);
SetupPerCameraShaderConstants(cameraData);
CullResults.Cull(cullingParameters, context, cullResults);
List<VisibleLight> visibleLights = cullResults.visibleLights;
RenderingData renderingData;
InitializeRenderingData(cameraData, visibleLights, out renderingData);
var setup = cameraData.camera.GetComponent<IRendererSetup>();
if (setup == null)
setup = defaultRendererSetup;
setup.Setup(renderer, context, cullResults, renderingData);
renderer.Execute(context, cullResults, renderingData);
context.Submit();
}
}
● Setup render passes
○ Allocate Resources
○ Enqueue Render Passes
○ Configure pipeline state
● Can be overridden to your
own!
21
public override void Render(ScriptableRenderContext context, Camera[] cameras)
{
SetupPerFrameShaderConstants();
foreach (Camera camera in cameras)
{
CameraData cameraData;
InitializeCameraData(camera, out cameraData);
SetupPerCameraShaderConstants(cameraData);
CullResults.Cull(cullingParameters, context, cullResults);
List<VisibleLight> visibleLights = cullResults.visibleLights;
RenderingData renderingData;
InitializeRenderingData(cameraData, visibleLights, out renderingData);
var setup = cameraData.camera.GetComponent<IRendererSetup>();
if (setup == null)
setup = defaultRendererSetup;
setup.Setup(renderer, context, cullResults, renderingData);
renderer.Execute(context, cullResults, renderingData);
context.Submit();
}
}
● Execute enqued passes
● Submit all queued rendering
22
Let’s take a look at the
DefaultRendererSetup
23
public void Setup(ScriptableRenderer renderer, … , RenderingData renderingData)
{
if (renderingData.shadowData.renderShadows)
renderer.Enqueue(m_ShadowPass);
bool requiresDepthPrepass = RequireDepthPrepass(renderingData.cameraData.requiresDepthTexture);
if (requiresDepthPrepass)
renderer.Enqueue(m_DepthPrepass);
renderer.Enqueue(m_SetupLightConstants);
renderer.Enqueue(m_RenderOpaquesForwardPass);
foreach (var pass in camera.GetComponents<IAfterOpaquePass>())
renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle));
if (camera.clearFlags == CameraClearFlags.Skybox)
renderer.Enqueue(m_RenderSkyboxPass);
renderer.Enqueue(m_RenderTransparentsPass);
if (renderingData.cameraData.postProcessEnabled)
renderer.Enqueue(m_PostProcessPass);
}
24
public void Setup(ScriptableRenderer renderer, … , RenderingData renderingData)
{
if (renderingData.shadowData.renderShadows)
renderer.Enqueue(m_ShadowPass);
bool requiresDepthPrepass = RequireDepthPrepass(renderingData.cameraData.requiresDepthTexture);
if (requiresDepthPrepass)
renderer.Enqueue(m_DepthPrepass);
renderer.Enqueue(m_SetupLightConstants);
renderer.Enqueue(m_RenderOpaquesForwardPass);
foreach (var pass in camera.GetComponents<IAfterOpaquePass>())
renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle));
if (camera.clearFlags == CameraClearFlags.Skybox)
renderer.Enqueue(m_RenderSkyboxPass);
renderer.Enqueue(m_RenderTransparentsPass);
if (renderingData.cameraData.postProcessEnabled)
renderer.Enqueue(m_PostProcessPass);
}
● Enqueue render passes
based on received data
● ScriptableRenderPass is a
building block to a render
pipeline
25
public void Setup(ScriptableRenderer renderer, … , RenderingData renderingData)
{
if (renderingData.shadowData.renderShadows)
renderer.Enqueue(m_ShadowPass);
bool requiresDepthPrepass = RequireDepthPrepass(renderingData.cameraData.requiresDepthTexture);
if (requiresDepthPrepass)
renderer.Enqueue(m_DepthPrepass);
renderer.Enqueue(m_SetupLightConstants);
renderer.Enqueue(m_RenderOpaquesForwardPass);
foreach (var pass in camera.GetComponents<IAfterOpaquePass>())
renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle));
if (camera.clearFlags == CameraClearFlags.Skybox)
renderer.Enqueue(m_RenderSkyboxPass);
renderer.Enqueue(m_RenderTransparentsPass);
if (renderingData.cameraData.postProcessEnabled)
renderer.Enqueue(m_PostProcessPass);
}
● Camera can contain scripts
that inject passes at specific
points in the pipeline
● IAfterOpaque is just one of
the many injecting points
26
Quick Recap
●LWRP do every frame
1.Culling
2.Setup per frame and per camera data
3.IRendererSetup enqueue multiple ScriptableRenderPass
a.A custom ScriptableRenderPass can be added at many
hook points
b.The default renderer can be even completely overridden
4.Renderer execute all enqueued passes
27
Adding a custom pass in LWRP
● Let’s switch to coding!
● We will:
○ Create a custom ScriptableRenderPass (MyNyanCatPass)
○ Inject the MyNyanCatPass after rendering opaques
(IAfterOpaque)
28
Now it’s your time to code!
● What if I get lost?
○ Call for help!
○ Use AddPassAfterOpaqueCompleted.cs in your project as
reference
29
Checkpoint
●You should have the following image
30
Customizing the render pipeline
● How about we complete override the renderer now?!
● But first let’s review how to do a mobile deferred renderer
Tiled GPU rendering review
●Splits rendering into tiles
31
Textures Framebuffer
Main Memory
Fragment Shader Tile Memory
Geometry
Data
GPU
Tiled GPU rendering review
●Splits rendering into tiles
●For each tile
○ Tile LOAD Action
32
Textures
Main Memory
Geometry
Data
GPU
Fragment Shader Tile Memory
Framebuffer
Tiled GPU rendering review
●Splits rendering into tiles
●For each tile
○ Tile LOAD Action
○ Rendering
33
Textures
Main Memory
Geometry
Data
GPU
Fragment Shader Tile Memory
Framebuffer
Tiled GPU rendering review
●Splits rendering into tiles
●For each tile
○ Tile LOAD Action
○ Rendering
○ Tile STORE Action
34
Textures
Main Memory
Geometry
Data
GPU
Fragment Shader Tile Memory
Framebuffer
Tiled GPU rendering review
●Splits rendering into tiles
●For each tile
○ Tile LOAD Action
○ Rendering
○ Tile STORE Action
35
Textures
Main Memory
Geometry
Data
GPU
Fragment Shader Tile Memory
Framebuffer
Tiled GPU rendering review
●Splits rendering into tiles
●For each tile
○ Tile LOAD Action
○ Rendering
○ Tile STORE Action
36
Textures
Main Memory
Geometry
Data
GPU
Fragment Shader Tile Memory
Framebuffer
Traditional Deferred Rendering
●GBuffer pass
○ Allocate Images
○ Setup Framebuffer (MRT)
○ Render to MRT
○ Write tiles to main memory
Textures Framebuffer
Main Memory
GPU
Fragment Shader Tile Memory
Geometry
Data
Diffuse Specular +
Rougness
Normals LightAccum
(GI + Emissive)
Depth
Traditional Deferred Rendering
●Lighting pass
○ SetFramebuffer (LightAccum)
○ Load tiles from main memory
○ Read Input Textures from main memory
○ Render to LightAccumulation
○ Write tiles to main memory
Textures Framebuffer
Main Memory
GPU
Fragment Shader Tile Memory
Geometry
Data
Diffuse Specular Normals Light
Accumulation
Depth
39
Rendering Efficiently on Mobile
●Minimize external memory read and writes
●Render Pass allows Tiled GPU interleaved rendering
○ Vulkan Multipass Mobile Deferred Done Right (Arntzen, GDC 2017)
Tile #0
Render GBuffer
Tile #0
Render Lighting
Tile #1
Render GBuffer
Tile #1
Render Lighting
Interleaved Rendering
40
Rendering Efficiently on Mobile
●RenderPass API
○ Vulkan: Transient Buffers
○ Metal: Framebuffer Fetch
○ Emulated on all other rendering backends
41
Mobile Deferred Renderer
● We will create a combined GBufferAndLighting renderpass
● For simplicity we will only support directional lights now
● It can easily be extended to support puntual lights
● Create an IRendererSetup
● Enqueue our render pass
● Add IRendererSetup to our camera
42
Now it’s your time to code!
● What if I get lost?
○ Call for help!
○ Use MyDeferredRendererCompleted.cs in your project as
reference
43
Before you leave!
1.Please rate this session in your SIGGRAPH app!
2.Save this link: bit.ly/siggraph18
3.We will have another awesome workshop on ShaderGraph!
a.Creating a Custom Production Ready Pipeline.
b.Same place, same hour!
4.We need to clear the booth for the next workshop.
5.Meet me outside for questions
44

Customizing a production pipeline

  • 1.
    GenerativeArt–MadewithUnity Customizing a ProductionPipeline (Lightweight Render Pipeline) Felipe Lira Graphics Programmer Unity Technologies 1 Note: If you are using a laptop download Unity 2018.2 and the following project: bit.ly/siggraph18
  • 2.
    2 Workshop Pre-requisites ●If youare on a SIGGRAPH computer ○ Start Unity and open SIGGRAPH2018-WORKSHOP project in your Desktop ●If you are on your own computer ○ Download Unity 2018.2 ○ Download the following project bit.ly/siggraph18 ○ Start Unity and open the project you downloaded ●Slides: bit.ly/siggraph18_slides
  • 3.
    3 Workshop Agenda ●What isthe Lightweight Render Pipeline (LWRP) ●Add LWRP to your project ●Convert a project to use LWRP ●Overview of LWRP rendering ●Customize LWRP renderer by adding custom passes ●Override LWRP renderer to implement a deferred renderer
  • 4.
    4 What is LightweightRender Pipeline (LWRP) ●It’s a Scriptable Render Pipeline (SRP) ○ Rendering API exposed in C# ○ Open Source: https://github.com/Unity-Technologies/ScriptableRenderPipeline ●LWRP ○ Currently 1 of 2 of our inhouse Render Pipelines we are developing ○ Designed from the ground up to perform at best on mobile hardware ○ Modern APIs in mind, as well as Legacy APIs, eg GLES 2.0
  • 5.
    5 Why should youcare about SRP and LWRP ●If you are ○ Student: ■ Learn Graphics Programming with a robust API ○ Game Developer: ■ Performance out of the box, explicit, lean and extensible ○ Researchers: ■ Implement, deploy and profile your research many platforms
  • 6.
  • 7.
  • 8.
    8 How to getSRP and LWRP ●In this workshop you already have it! ○ The project you are using is bundled with LWRP for simplicity
  • 9.
    9 How to getSRP and LWRP ●You can also get it by creating a new project with LWRP template
  • 10.
    10 How to getSRP and LWRP ●Or by downloading from package manager UI
  • 11.
    11 So now youhave it! ● Let’s convert your project to use LWRP
  • 12.
    12 Checkpoint! ●Created LWRP asset ○Assets -> Create -> Rendering -> Lightweight Pipeline Asset ●Assigned LWRP to Graphics Settings ○ Edit -> Project Settings -> Graphics Settings ○ Drag and Drop LWRP asset in SRP settings ●Upgraded materials to LWRP materials ○ Edit -> Render Pipeline -> Upgrade Project Materials … ●Checked rendering settings in LWRP asset ●You should have the same image as before converting
  • 13.
    13 Before coding let’stake a look at core LWRP rendering
  • 14.
    14 public override voidRender(ScriptableRenderContext context, Camera[] cameras) { SetupPerFrameShaderConstants(); foreach (Camera camera in cameras) { CameraData cameraData; InitializeCameraData(camera, out cameraData); SetupPerCameraShaderConstants(cameraData); CullResults.Cull(cullingParameters, context, cullResults); List<VisibleLight> visibleLights = cullResults.visibleLights; RenderingData renderingData; InitializeRenderingData(cameraData, visibleLights, out renderingData); var setup = cameraData.camera.GetComponent<IRendererSetup>(); if (setup == null) setup = defaultRendererSetup; setup.Setup(renderer, context, cullResults, renderingData); renderer.Execute(context, cullResults, renderingData); context.Submit(); } }
  • 15.
    15 public override voidRender(ScriptableRenderContext context, Camera[] cameras) { SetupPerFrameShaderConstants(); foreach (Camera camera in cameras) { CameraData cameraData; InitializeCameraData(camera, out cameraData); SetupPerCameraShaderConstants(cameraData); CullResults.Cull(cullingParameters, context, cullResults); List<VisibleLight> visibleLights = cullResults.visibleLights; RenderingData renderingData; InitializeRenderingData(cameraData, visibleLights, out renderingData); var setup = cameraData.camera.GetComponent<IRendererSetup>(); if (setup == null) setup = defaultRendererSetup; setup.Setup(renderer, context, cullResults, renderingData); renderer.Execute(context, cullResults, renderingData); context.Submit(); } } Called from C++ for: ● Scene ● Offscreen ● Editor ● Preview ● Reflection
  • 16.
    16 public override voidRender(ScriptableRenderContext context, Camera[] cameras) { SetupPerFrameShaderConstants(); foreach (Camera camera in cameras) { CameraData cameraData; InitializeCameraData(camera, out cameraData); SetupPerCameraShaderConstants(cameraData); CullResults.Cull(cullingParameters, context, cullResults); List<VisibleLight> visibleLights = cullResults.visibleLights; RenderingData renderingData; InitializeRenderingData(cameraData, visibleLights, out renderingData); var setup = cameraData.camera.GetComponent<IRendererSetup>(); if (setup == null) setup = defaultRendererSetup; setup.Setup(renderer, context, cullResults, renderingData); renderer.Execute(context, cullResults, renderingData); context.Submit(); } } Shader constants setup per access frequency ● PerFrame ● PerCamera ● PerPass ● PerObject
  • 17.
    17 public override voidRender(ScriptableRenderContext context, Camera[] cameras) { SetupPerFrameShaderConstants(); foreach (Camera camera in cameras) { CameraData cameraData; InitializeCameraData(camera, out cameraData); SetupPerCameraShaderConstants(cameraData); CullResults.Cull(cullingParameters, context, cullResults); List<VisibleLight> visibleLights = cullResults.visibleLights; RenderingData renderingData; InitializeRenderingData(cameraData, visibleLights, out renderingData); var setup = cameraData.camera.GetComponent<IRendererSetup>(); if (setup == null) setup = defaultRendererSetup; setup.Setup(renderer, context, cullResults, renderingData); renderer.Execute(context, cullResults, renderingData); context.Submit(); } } Camera Related Rendering: ● HDR ● MSAA ● Shadows ● Render Scale ● PostProcessing ● Stereo Rendering
  • 18.
    18 public override voidRender(ScriptableRenderContext context, Camera[] cameras) { SetupPerFrameShaderConstants(); foreach (Camera camera in cameras) { CameraData cameraData; InitializeCameraData(camera, out cameraData); SetupPerCameraShaderConstants(cameraData); CullResults.Cull(cullingParameters, context, cullResults); List<VisibleLight> visibleLights = cullResults.visibleLights; RenderingData renderingData; InitializeRenderingData(cameraData, visibleLights, out renderingData); var setup = cameraData.camera.GetComponent<IRendererSetup>(); if (setup == null) setup = defaultRendererSetup; setup.Setup(renderer, context, cullResults, renderingData); renderer.Execute(context, cullResults, renderingData); context.Submit(); } } Visible: ● Renderers ● Lights ● ...
  • 19.
    19 public override voidRender(ScriptableRenderContext context, Camera[] cameras) { SetupPerFrameShaderConstants(); foreach (Camera camera in cameras) { CameraData cameraData; InitializeCameraData(camera, out cameraData); SetupPerCameraShaderConstants(cameraData); CullResults.Cull(cullingParameters, context, cullResults); List<VisibleLight> visibleLights = cullResults.visibleLights; RenderingData renderingData; InitializeRenderingData(cameraData, visibleLights, out renderingData); var setup = cameraData.camera.GetComponent<IRendererSetup>(); if (setup == null) setup = defaultRendererSetup; setup.Setup(renderer, context, cullResults, renderingData); renderer.Execute(context, cullResults, renderingData); context.Submit(); } } Data-drive rendering! Rendering Data Based on: ● Cull Results ● Quality Settings ● Rendering Features ● Platform
  • 20.
    20 public override voidRender(ScriptableRenderContext context, Camera[] cameras) { SetupPerFrameShaderConstants(); foreach (Camera camera in cameras) { CameraData cameraData; InitializeCameraData(camera, out cameraData); SetupPerCameraShaderConstants(cameraData); CullResults.Cull(cullingParameters, context, cullResults); List<VisibleLight> visibleLights = cullResults.visibleLights; RenderingData renderingData; InitializeRenderingData(cameraData, visibleLights, out renderingData); var setup = cameraData.camera.GetComponent<IRendererSetup>(); if (setup == null) setup = defaultRendererSetup; setup.Setup(renderer, context, cullResults, renderingData); renderer.Execute(context, cullResults, renderingData); context.Submit(); } } ● Setup render passes ○ Allocate Resources ○ Enqueue Render Passes ○ Configure pipeline state ● Can be overridden to your own!
  • 21.
    21 public override voidRender(ScriptableRenderContext context, Camera[] cameras) { SetupPerFrameShaderConstants(); foreach (Camera camera in cameras) { CameraData cameraData; InitializeCameraData(camera, out cameraData); SetupPerCameraShaderConstants(cameraData); CullResults.Cull(cullingParameters, context, cullResults); List<VisibleLight> visibleLights = cullResults.visibleLights; RenderingData renderingData; InitializeRenderingData(cameraData, visibleLights, out renderingData); var setup = cameraData.camera.GetComponent<IRendererSetup>(); if (setup == null) setup = defaultRendererSetup; setup.Setup(renderer, context, cullResults, renderingData); renderer.Execute(context, cullResults, renderingData); context.Submit(); } } ● Execute enqued passes ● Submit all queued rendering
  • 22.
    22 Let’s take alook at the DefaultRendererSetup
  • 23.
    23 public void Setup(ScriptableRendererrenderer, … , RenderingData renderingData) { if (renderingData.shadowData.renderShadows) renderer.Enqueue(m_ShadowPass); bool requiresDepthPrepass = RequireDepthPrepass(renderingData.cameraData.requiresDepthTexture); if (requiresDepthPrepass) renderer.Enqueue(m_DepthPrepass); renderer.Enqueue(m_SetupLightConstants); renderer.Enqueue(m_RenderOpaquesForwardPass); foreach (var pass in camera.GetComponents<IAfterOpaquePass>()) renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle)); if (camera.clearFlags == CameraClearFlags.Skybox) renderer.Enqueue(m_RenderSkyboxPass); renderer.Enqueue(m_RenderTransparentsPass); if (renderingData.cameraData.postProcessEnabled) renderer.Enqueue(m_PostProcessPass); }
  • 24.
    24 public void Setup(ScriptableRendererrenderer, … , RenderingData renderingData) { if (renderingData.shadowData.renderShadows) renderer.Enqueue(m_ShadowPass); bool requiresDepthPrepass = RequireDepthPrepass(renderingData.cameraData.requiresDepthTexture); if (requiresDepthPrepass) renderer.Enqueue(m_DepthPrepass); renderer.Enqueue(m_SetupLightConstants); renderer.Enqueue(m_RenderOpaquesForwardPass); foreach (var pass in camera.GetComponents<IAfterOpaquePass>()) renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle)); if (camera.clearFlags == CameraClearFlags.Skybox) renderer.Enqueue(m_RenderSkyboxPass); renderer.Enqueue(m_RenderTransparentsPass); if (renderingData.cameraData.postProcessEnabled) renderer.Enqueue(m_PostProcessPass); } ● Enqueue render passes based on received data ● ScriptableRenderPass is a building block to a render pipeline
  • 25.
    25 public void Setup(ScriptableRendererrenderer, … , RenderingData renderingData) { if (renderingData.shadowData.renderShadows) renderer.Enqueue(m_ShadowPass); bool requiresDepthPrepass = RequireDepthPrepass(renderingData.cameraData.requiresDepthTexture); if (requiresDepthPrepass) renderer.Enqueue(m_DepthPrepass); renderer.Enqueue(m_SetupLightConstants); renderer.Enqueue(m_RenderOpaquesForwardPass); foreach (var pass in camera.GetComponents<IAfterOpaquePass>()) renderer.EnqueuePass(pass.GetPassToEnqueue(baseDescriptor, colorHandle, depthHandle)); if (camera.clearFlags == CameraClearFlags.Skybox) renderer.Enqueue(m_RenderSkyboxPass); renderer.Enqueue(m_RenderTransparentsPass); if (renderingData.cameraData.postProcessEnabled) renderer.Enqueue(m_PostProcessPass); } ● Camera can contain scripts that inject passes at specific points in the pipeline ● IAfterOpaque is just one of the many injecting points
  • 26.
    26 Quick Recap ●LWRP doevery frame 1.Culling 2.Setup per frame and per camera data 3.IRendererSetup enqueue multiple ScriptableRenderPass a.A custom ScriptableRenderPass can be added at many hook points b.The default renderer can be even completely overridden 4.Renderer execute all enqueued passes
  • 27.
    27 Adding a custompass in LWRP ● Let’s switch to coding! ● We will: ○ Create a custom ScriptableRenderPass (MyNyanCatPass) ○ Inject the MyNyanCatPass after rendering opaques (IAfterOpaque)
  • 28.
    28 Now it’s yourtime to code! ● What if I get lost? ○ Call for help! ○ Use AddPassAfterOpaqueCompleted.cs in your project as reference
  • 29.
  • 30.
    30 Customizing the renderpipeline ● How about we complete override the renderer now?! ● But first let’s review how to do a mobile deferred renderer
  • 31.
    Tiled GPU renderingreview ●Splits rendering into tiles 31 Textures Framebuffer Main Memory Fragment Shader Tile Memory Geometry Data GPU
  • 32.
    Tiled GPU renderingreview ●Splits rendering into tiles ●For each tile ○ Tile LOAD Action 32 Textures Main Memory Geometry Data GPU Fragment Shader Tile Memory Framebuffer
  • 33.
    Tiled GPU renderingreview ●Splits rendering into tiles ●For each tile ○ Tile LOAD Action ○ Rendering 33 Textures Main Memory Geometry Data GPU Fragment Shader Tile Memory Framebuffer
  • 34.
    Tiled GPU renderingreview ●Splits rendering into tiles ●For each tile ○ Tile LOAD Action ○ Rendering ○ Tile STORE Action 34 Textures Main Memory Geometry Data GPU Fragment Shader Tile Memory Framebuffer
  • 35.
    Tiled GPU renderingreview ●Splits rendering into tiles ●For each tile ○ Tile LOAD Action ○ Rendering ○ Tile STORE Action 35 Textures Main Memory Geometry Data GPU Fragment Shader Tile Memory Framebuffer
  • 36.
    Tiled GPU renderingreview ●Splits rendering into tiles ●For each tile ○ Tile LOAD Action ○ Rendering ○ Tile STORE Action 36 Textures Main Memory Geometry Data GPU Fragment Shader Tile Memory Framebuffer
  • 37.
    Traditional Deferred Rendering ●GBufferpass ○ Allocate Images ○ Setup Framebuffer (MRT) ○ Render to MRT ○ Write tiles to main memory Textures Framebuffer Main Memory GPU Fragment Shader Tile Memory Geometry Data Diffuse Specular + Rougness Normals LightAccum (GI + Emissive) Depth
  • 38.
    Traditional Deferred Rendering ●Lightingpass ○ SetFramebuffer (LightAccum) ○ Load tiles from main memory ○ Read Input Textures from main memory ○ Render to LightAccumulation ○ Write tiles to main memory Textures Framebuffer Main Memory GPU Fragment Shader Tile Memory Geometry Data Diffuse Specular Normals Light Accumulation Depth
  • 39.
    39 Rendering Efficiently onMobile ●Minimize external memory read and writes ●Render Pass allows Tiled GPU interleaved rendering ○ Vulkan Multipass Mobile Deferred Done Right (Arntzen, GDC 2017) Tile #0 Render GBuffer Tile #0 Render Lighting Tile #1 Render GBuffer Tile #1 Render Lighting Interleaved Rendering
  • 40.
    40 Rendering Efficiently onMobile ●RenderPass API ○ Vulkan: Transient Buffers ○ Metal: Framebuffer Fetch ○ Emulated on all other rendering backends
  • 41.
    41 Mobile Deferred Renderer ●We will create a combined GBufferAndLighting renderpass ● For simplicity we will only support directional lights now ● It can easily be extended to support puntual lights ● Create an IRendererSetup ● Enqueue our render pass ● Add IRendererSetup to our camera
  • 42.
    42 Now it’s yourtime to code! ● What if I get lost? ○ Call for help! ○ Use MyDeferredRendererCompleted.cs in your project as reference
  • 43.
    43 Before you leave! 1.Pleaserate this session in your SIGGRAPH app! 2.Save this link: bit.ly/siggraph18 3.We will have another awesome workshop on ShaderGraph! a.Creating a Custom Production Ready Pipeline. b.Same place, same hour! 4.We need to clear the booth for the next workshop. 5.Meet me outside for questions
  • 44.