Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Customizing a production pipeline

874 views

Published on

SIGGRAPH 2018 Studio Workshop - Customizing a Production Pipeline

Published in: Software
  • Be the first to comment

Customizing a production pipeline

  1. 1. 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. 2. 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. 3. 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. 4. 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. 5. 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. 6. 6
  7. 7. 7 Checkpoint! ●Open DemoScene ●You should see something like this
  8. 8. 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. 9. 9 How to get SRP and LWRP ●You can also get it by creating a new project with LWRP template
  10. 10. 10 How to get SRP and LWRP ●Or by downloading from package manager UI
  11. 11. 11 So now you have it! ● Let’s convert your project to use LWRP
  12. 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. 13 Before coding let’s take a look at core LWRP rendering
  14. 14. 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. 15. 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. 16. 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. 17. 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. 18. 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. 19. 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. 20. 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. 21. 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. 22. 22 Let’s take a look at the DefaultRendererSetup
  23. 23. 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. 24. 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. 25. 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. 26. 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. 27. 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. 28. 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. 29. 29 Checkpoint ●You should have the following image
  30. 30. 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
  31. 31. Tiled GPU rendering review ●Splits rendering into tiles 31 Textures Framebuffer Main Memory Fragment Shader Tile Memory Geometry Data GPU
  32. 32. 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
  33. 33. 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
  34. 34. 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
  35. 35. 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
  36. 36. 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
  37. 37. 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
  38. 38. 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. 39. 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. 40. 40 Rendering Efficiently on Mobile ●RenderPass API ○ Vulkan: Transient Buffers ○ Metal: Framebuffer Fetch ○ Emulated on all other rendering backends
  41. 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. 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. 43. 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. 44. 44

×