|
| 1 | +# Render from another camera inside a camera's rendering loop |
| 2 | + |
| 3 | +Render from cameras nested inside the render loop of other cameras. |
| 4 | + |
| 5 | +Attach the provided script to a GameObject with a Camera component to nest multiple cameras, that are rendering with [RenderPipeline.SubmitRenderRequest](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Rendering.RenderPipeline.SubmitRenderRequest.html), inside the render loop of other cameras. |
| 6 | + |
| 7 | +**Note**: If your project uses the [Universal Render Pipeline](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/index.html) (URP), the recommended best practice is to use [UniversalRenderPipeline.SingleCameraRequest](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@17.0/api/UnityEngine.Rendering.Universal.UniversalRenderPipeline.SingleCameraRequest.html) instead of [StandardRequest](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Rendering.RenderPipeline.StandardRequest.html), to make sure you only render the camera provided to the `RenderRequest` API instead of the full stack of cameras. |
| 8 | + |
| 9 | +## Attach the script to nest |
| 10 | + |
| 11 | +Follow these steps: |
| 12 | + |
| 13 | +1. Create a new C# script. |
| 14 | +2. Add the `using` statements shown below and the `RequireComponent` attribute with the `Camera` type. |
| 15 | + |
| 16 | + ```c# |
| 17 | + using System.Collections.Generic; |
| 18 | + using UnityEngine; |
| 19 | + using UnityEngine.Rendering; |
| 20 | + |
| 21 | + [RequireComponent(typeof(Camera))] |
| 22 | + public class InLoopRenderRequest : MonoBehaviour |
| 23 | + { |
| 24 | + |
| 25 | + } |
| 26 | + ``` |
| 27 | + |
| 28 | +3. Add a property with the type `Camera` to the `InLoopRenderRequest` class. |
| 29 | +4. Add a property with the type `RenderTexture` for each callback the camera uses, as shown below: |
| 30 | + |
| 31 | + ```c# |
| 32 | + [RequireComponent(typeof(Camera))] |
| 33 | + public class InLoopRenderRequest : MonoBehaviour |
| 34 | + { |
| 35 | + public Camera renderRequestCamera; |
| 36 | + |
| 37 | + public RenderTexture onBeginCameraRendering; |
| 38 | + public RenderTexture onBeginContextRendering; |
| 39 | + public RenderTexture onEndCameraRendering; |
| 40 | + public RenderTexture onEndContextRendering; |
| 41 | + } |
| 42 | + ``` |
| 43 | + |
| 44 | +## Full code example |
| 45 | + |
| 46 | +The following is an example of the finalized code which you attach to the secondary camera: |
| 47 | + |
| 48 | +```c# |
| 49 | +using System.Collections.Generic; |
| 50 | +using UnityEngine; |
| 51 | +using UnityEngine.Rendering; |
| 52 | + |
| 53 | +[RequireComponent(typeof(Camera))] |
| 54 | +public class InLoopRenderRequest : MonoBehaviour |
| 55 | +{ |
| 56 | + // Add a reference to the secondary camera that will render the textures |
| 57 | + // It's recommended to disable the secondary camera. |
| 58 | + public Camera renderRequestCamera; |
| 59 | + |
| 60 | + // Add references to the Render Textures that will contain the rendered image from the secondary camera. |
| 61 | + public RenderTexture onBeginCameraRendering; |
| 62 | + public RenderTexture onBeginContextRendering; |
| 63 | + public RenderTexture onEndCameraRendering; |
| 64 | + public RenderTexture onEndContextRendering; |
| 65 | + |
| 66 | + void OnEnable() |
| 67 | + { |
| 68 | + // Subscribe to the RenderPipelineManager callbacks |
| 69 | + RenderPipelineManager.beginCameraRendering += OnBeginCameraRender; |
| 70 | + RenderPipelineManager.beginContextRendering += OnBeginContextRendering; |
| 71 | + RenderPipelineManager.endCameraRendering += OnEndCameraRender; |
| 72 | + RenderPipelineManager.endContextRendering += OnEndContextRendering; |
| 73 | + } |
| 74 | + |
| 75 | + public void OnDisable() |
| 76 | + { |
| 77 | + // Unsubscribe to the callbacks from RenderPipelineManager when we disable the component |
| 78 | + RenderPipelineManager.beginCameraRendering -= OnBeginCameraRender; |
| 79 | + RenderPipelineManager.beginContextRendering -= OnBeginContextRendering; |
| 80 | + RenderPipelineManager.endCameraRendering -= OnEndCameraRender; |
| 81 | + RenderPipelineManager.endContextRendering -= OnEndContextRendering; |
| 82 | + } |
| 83 | + |
| 84 | + void SubmitStandardRenderRequest(RenderTexture rt, Camera cam) |
| 85 | + { |
| 86 | + RenderPipeline.StandardRequest request = new(); |
| 87 | + |
| 88 | + // Check that the Scriptable Render Pipeline (SRP) we're using supports the given render data. |
| 89 | + if (RenderPipeline.SupportsRenderRequest(cam, request)) |
| 90 | + { |
| 91 | + // Set the request RenderTexture |
| 92 | + request.destination = rt; |
| 93 | + |
| 94 | + // Render the camera output to the RenderTexture synchronously |
| 95 | + // When this is complete, the RenderTexture in renderTextures[i] contains the scene rendered from the point of view of the secondary cameras |
| 96 | + RenderPipeline.SubmitRenderRequest(cam, request); |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + // StandardRequest and UniversalRenderPipeline.SingleCameraRequest also trigger RenderPipelineManager callbacks. |
| 101 | + // Check that the callbacks are from the GameObject's Camera component to avoid a recursive rendering of the same camera. |
| 102 | + private void OnBeginContextRendering(ScriptableRenderContext ctx, List<Camera> cams) |
| 103 | + { |
| 104 | + if (cams.Contains(GetComponent<Camera>())) |
| 105 | + { |
| 106 | + SubmitStandardRenderRequest(onBeginContextRendering, renderRequestCamera); |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + private void OnEndContextRendering(ScriptableRenderContext ctx, List<Camera> cams) |
| 111 | + { |
| 112 | + if (cams.Contains(GetComponent<Camera>())) |
| 113 | + { |
| 114 | + SubmitStandardRenderRequest(onEndContextRendering, renderRequestCamera); |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + private void OnBeginCameraRender(ScriptableRenderContext ctx, Camera cam) |
| 119 | + { |
| 120 | + if (cam == GetComponent<Camera>()) |
| 121 | + { |
| 122 | + SubmitStandardRenderRequest(onBeginCameraRendering, renderRequestCamera); |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + private void OnEndCameraRender(ScriptableRenderContext ctx, Camera cam) |
| 127 | + { |
| 128 | + if (cam == GetComponent<Camera>()) |
| 129 | + { |
| 130 | + SubmitStandardRenderRequest(onEndCameraRendering, renderRequestCamera); |
| 131 | + } |
| 132 | + } |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +## Additional resources |
| 137 | +- [Render Requests](User-Render-Requests.md) |
| 138 | +- [Creating a custom render pipeline](srp-custom.md) |
| 139 | + |
0 commit comments