Rasagar/Library/PackageCache/com.unity.render-pipelines.universal/Documentation~/render-graph-pass-textures-between-passes.md
2024-08-26 23:07:20 +03:00

9.1 KiB

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:

You can also store the texture outside the render passes, for example as a TextureHandle in a Scriptable Renderer Feature.

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 instead.

Add a texture to the frame data

You can add a texture to the frame data so you can fetch the texture in a later render pass.

Follow these steps:

  1. Create a class that inherits ContextItem and contains a texture handle field.

    For example:

    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:

    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:

    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameContext)
    {
        using (var builder = renderGraph.AddRasterRenderPass<PassData>("Get frame data", out var passData))
        {
            UniversalResourceData resourceData = frameContext.Get<UniversalResourceData>();
            var customData = contextData.Create<MyCustomData>();
        }
    }
    
  4. Set the texture handle to your texture.

    For example:

    // 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:

// Get the custom data
MyCustomData fetchedData = frameData.Get<MyCustomData>();

// Get the texture
TextureHandle customTexture = customData.textureToTransfer;

For more information about frame data, refer to Use frame data.

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.

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<PassData>("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>();
                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<PassData>("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<AddOwnTexturePass.CustomData>();
                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.

Don't use an unsafe render pass 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 method.

For example:

// Allocate a global shader texture called _GlobalTexture
private int globalTextureID = Shader.PropertyToID("_GlobalTexture")

using (var builder = renderGraph.AddRasterRenderPass<PassData>("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:

    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.