--- uid: urp-render-graph-pass-textures-between-passes --- # Transfer a texture between render passes You can transfer a texture between render passes, for example if you need to create a texture in one render pass and read it in a later render pass. Use the following methods to transfer textures between render passes: - [Add a texture to the frame data](#add-a-texture-to-the-frame-data) - [Set a texture as a global texture](#set-a-texture-as-a-global-texture) You can also store the texture outside the render passes, for example as a `TextureHandle` in a [Scriptable Renderer Feature](renderer-features/scriptable-renderer-features/scriptable-renderer-features-landing.md). If you need to use make sure a texture is available across multiple frames, or that multiple cameras can access it, refer to [Import a texture into the render graph system](render-graph-import-a-texture.md) instead. ## Add a texture to the frame data You can add a texture to the [frame data](accessing-frame-data.md) so you can fetch the texture in a later render pass. Follow these steps: 1. Create a class that inherits [`ContextItem`](https://docs.unity3d.com/Packages/com.unity.render-pipelines.core@17.0/api/UnityEngine.Rendering.ContextItem.html) and contains a texture handle field. For example: ```csharp public class MyCustomData : ContextItem { public TextureHandle textureToTransfer; } ``` 2. You must implement the `Reset()` method in your class, to reset the texture when the frame resets. For example: ```csharp public class MyCustomData : ContextItem { public TextureHandle textureToTransfer; public override void Reset() { textureToTransfer = TextureHandle.nullHandle; } } ``` 3. In your `RecordRenderGraph` method, add an instance of your class to the frame data. For example: ```csharp public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameContext) { using (var builder = renderGraph.AddRasterRenderPass("Get frame data", out var passData)) { UniversalResourceData resourceData = frameContext.Get(); var customData = contextData.Create(); } } ``` 4. Set the texture handle to your texture. For example: ```csharp // Create texture properties that match the screen RenderTextureDescriptor textureProperties = new RenderTextureDescriptor(Screen.width, Screen.height, RenderTextureFormat.Default, 0); // Create the texture TextureHandle texture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, textureProperties, "My texture", false); // Set the texture in the custom data instance customData.textureToTransfer = texture; ``` In a later render pass, in your `RecordRenderGraph` method, you can get your custom data and fetch your texture: For example: ```csharp // Get the custom data MyCustomData fetchedData = frameData.Get(); // Get the texture TextureHandle customTexture = customData.textureToTransfer; ``` For more information about frame data, refer to [Use frame data](accessing-frame-data.md). ### Example The following example adds a `CustomData` class that contains a texture. The first render pass clears the texture to yellow, and the second render pass fetches the yellow texture and draws a triangle onto it. ```csharp using UnityEngine; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering; public class AddOwnTextureToFrameData : ScriptableRendererFeature { AddOwnTexturePass customPass1; DrawTrianglePass customPass2; public override void Create() { customPass1 = new AddOwnTexturePass(); customPass2 = new DrawTrianglePass(); customPass1.renderPassEvent = RenderPassEvent.AfterRenderingOpaques; customPass2.renderPassEvent = RenderPassEvent.AfterRenderingOpaques; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { renderer.EnqueuePass(customPass1); renderer.EnqueuePass(customPass2); } // Create the first render pass, which creates a texture and adds it to the frame data class AddOwnTexturePass : ScriptableRenderPass { class PassData { internal TextureHandle copySourceTexture; } // Create the custom data class that contains the new texture public class CustomData : ContextItem { public TextureHandle newTextureForFrameData; public override void Reset() { newTextureForFrameData = TextureHandle.nullHandle; } } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameContext) { using (var builder = renderGraph.AddRasterRenderPass("Create new texture", out var passData)) { // Create a texture and set it as the render target RenderTextureDescriptor textureProperties = new RenderTextureDescriptor(Screen.width, Screen.height, RenderTextureFormat.Default, 0); TextureHandle texture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, textureProperties, "My texture", false); CustomData customData = frameContext.Create(); customData.newTextureForFrameData = texture; builder.SetRenderAttachment(texture, 0, AccessFlags.Write); builder.AllowPassCulling(false); builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecutePass(data, context)); } } static void ExecutePass(PassData data, RasterGraphContext context) { // Clear the render target (the texture) to yellow context.cmd.ClearRenderTarget(true, true, Color.yellow); } } // Create the second render pass, which fetches the texture and writes to it class DrawTrianglePass : ScriptableRenderPass { class PassData { // No local pass data needed } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameContext) { using (var builder = renderGraph.AddRasterRenderPass("Fetch texture and draw triangle", out var passData)) { // Fetch the yellow texture from the frame data and set it as the render target var customData = frameContext.Get(); var customTexture = customData.newTextureForFrameData; builder.SetRenderAttachment(customTexture, 0, AccessFlags.Write); builder.AllowPassCulling(false); builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecutePass(data, context)); } } static void ExecutePass(PassData data, RasterGraphContext context) { // Generate a triangle mesh Mesh mesh = new Mesh(); mesh.vertices = new Vector3[] { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 0) }; mesh.triangles = new int[] { 0, 1, 2 }; // Draw a triangle to the render target (the yellow texture) context.cmd.DrawMesh(mesh, Matrix4x4.identity, new Material(Shader.Find("Universal Render Pipeline/Unlit"))); } } } ``` ## Set a texture as a global texture If you need to use a texture as the input for the shader on a GameObject, you can set a texture as a global texture. A global texture is available to all shaders and render passes. Setting a texture as a global texture can make rendering slower. Refer to [SetGlobalTexture](https://docs.unity3d.com/ScriptReference/Shader.SetGlobalTexture.html). Don't use an [unsafe render pass](render-graph-unsafe-pass.md) and `CommandBuffer.SetGlobal` to set a texture as a global texture, because it might cause errors. To set a global texture, in the `RecordRenderGraph` method, use the [`SetGlobalTextureAfterPass`](https://docs.unity3d.com/Packages/com.unity.render-pipelines.core@17.0/api/UnityEngine.Rendering.RenderGraphModule.IBaseRenderGraphBuilder.html#UnityEngine_Rendering_RenderGraphModule_IBaseRenderGraphBuilder_SetGlobalTextureAfterPass_UnityEngine_Rendering_RenderGraphModule_TextureHandle__System_Int32_) method. For example: ```csharp // Allocate a global shader texture called _GlobalTexture private int globalTextureID = Shader.PropertyToID("_GlobalTexture") using (var builder = renderGraph.AddRasterRenderPass("MyPass", out var passData)){ // Set a texture to the global texture builder.SetGlobalTextureAfterPass(texture, globalTextureID); } ``` If you don't already call `SetRenderFunc`, you must also add an empty render function. For example: ```csharp builder.SetRenderFunc((PassData data, RasterGraphContext context) => { }); ``` You can now: - Access the texture in a different render pass, using the `UseGlobalTexture()` or `UseAllGlobalTextures()` API. - Use the texture on any material in your scene.