This tutorial offers a clear and essential guide to mastering the abstraction layers of Unity's Render Graph. It effectively simplifies complex resource management into a structured workflow for modern graphics development.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Migrating to Render Graph: Understanding the Render Graph Samples - Part 2Added:
Welcome back to part two. Let's begin with blit with frame data. Blit with frame data shows an example of how to use the same texture in different passes.
A possible use case would be if you wanted to do before and after effect with two textures side by [music] side.
To do this, you could capture the before texture in an initial custom pass [music] and store the texture in a frame data instance. This is passed as an instance of the context container class, [music] a type of dictionary, to the record render graph method. Subsequent passes do the post-processing and then with a further custom pass, you can fetch the before texture back [music] from the frame data and have the pass draw the current camera color and the before texture side by side.
Take a look at the file blit render feature from the folder URP render graph samples blit with frame data. [music] The key class is blit data, which will be added to the frame data parameter of the record render graph method.
For each pass, it extends context item.
The class creates two texture handles, texture handle front and texture handle back.
The blit data uses the property texture to select between these two textures.
The class also includes three record methods, one for each pass.
This file contains three passes. One, blit start render pass, used to initialize the blit data class.
Two, blit render pass, used to blit using a list of materials.
And three, blit end render pass, used to restore from the texture stored in the first pass.
Notice that for the blit and blit end passes, the record render graph method accesses the blit data instance created in the blit start pass from the frame data parameter using the get method.
Then each pass uses a custom record method found in the blit data class.
The key takeaway here is passing data between passes uses a custom class that is created using the create method of the frame data instance.
Texture reference with frame data.
Similar to the previous example, texture reference with frame data uses a custom class extended from context item to hold a texture reference to be used by future passes.
This is useful to avoid additional blit operations, copying back and forth to the camera's color attachment, instead of copying it back after the blit operation.
We can instead update the reference to the blit destination and use that for future passes.
The frame data is the preferred way to share resources between passes.
Previously, it was common to use global textures for this. However, it's better to avoid using global textures where you can because global textures can impact performance.
Output texture.
In this sample, a named output texture is created that can be attached by name to a material.
Take a look at output texture render feature.cs from the URP render graph samples folder.
This file contains the classes output texture render feature and output texture pass. The render feature contains four properties that can be set in the inspector.
Pass event is a stage in the pipeline to run the pass.
Texture name is the name attached to the texture.
Texture type, the texture input can be color, depth, normal, or motion. And material, the material used for the blit pass.
The create method of the render feature uses the pass event property to set the render pass event for the scriptable pass.
To pass the properties from the render feature to the render pass, this sample uses a setup method. Let's take a look.
The texture type is set using the configure input method of the scriptable render pass.
Then in the record render graph method, we get the source based on the texture type property.
The source might be invalid if the pass event is set to before the source is created. So we check and return if this is the case.
Finally, we configure a blit materials parameters instance.
The source is set to source, destination to active color texture, material to the material property, and the pass to zero.
Then we assign the source texture property ID using the texture name.
This means we can access this texture using a name and a shader graph.
Take a look at the shader graph blit target texture.
You'll see it uses a single property input texture, a texture 2D.
This simple graph uses this as the target input to a sample texture 2D node.
To use the shader, you need to set the texture name property of the render feature to input texture and the material to blit target texture.
If you're using the GitHub repo with complete scenes, notice this uses the shader graph output texture, which uses the texture name my texture.
If you try changing the texture name, you may need to close and reopen the scene for the updates to be used correctly.
Compute.
Render graph does not just deal with raster operations. A developer can use compute shaders [music] to handle computations that benefit from the massive parallelism available via the GPU.
Compute render feature is a simple example to help you understand the code used.
You'll find it in the URP render graph samples compute [music] folder.
If you're using the GitHub repo, open scenes compute compute.
>> [music] >> Take a look at the render graph viewer and check that the pass filter includes compute passes.
Notice that the compute pass uses [music] the input buffer, green indicating read access, and uses the output buffer, red [music] indicating write access, in the resource list.
The work done by the compute shader is shown in the console. If you're new to compute shaders, there are three Unity videos available to explain the topic.
Links to those are in the description.
Let's take a look at the compute render feature code.
>> [music] >> Notice the compute pass class includes a compute shader, two buffer handles, and a list. [music] In the constructor, the list is initialized by storing the first 20 integers, 0 to 19.
The class also includes the definition [music] of two more custom classes.
Pass data that is used when passing data to the execution function. And read back pass that is [music] used by the pass that reads the result of the compute shader.
The record render graph method creates two buffers, input [music] and output.
Then we use the add compute pass method.
It populates the pass data out parameter, sets the access parameters for the buffers, and sets the render function.
As discussed, the sample includes a second pass to read the result of the compute shader.
This uses the add on safe [music] pass method. Here, after assigning the buffer to read, the set render function simply asynchronously gets data from the buffer and dumps it to the console using debug [music] log.
All the compute shader does is double the input value because this is just a example after all. So with the input data 0, 1, 2 to 19, we see 0 to 4 [music] up to 38 in the console.
The important takeaway being how to use a render feature to run a compute shader.
Next up is renderer list. In this sample, we clear the active color texture and then draw geometry filtered by a layer mask via a rendered list handle. The purpose of the sample is to demonstrate combining draw operations into a pass.
Take a look at renderer list render feature.cs from the URP render graph samples.
The file contains a custom pass class under renderer feature.
The renderer feature uses a custom constructor to pass [music] the layer mask to the renderer list pass.
The renderer list referred to by the sample is a list of objects to render after layer mask filtering, >> [music] >> not the renderer list we see for the active URP asset used to define the settings for the render.
The key to the sample is the init renderer list method in the renderer list pass. Here we access the model assets via universal rendering data, cameras via universal camera data, and lights via universal light data. We set sort flags to default opaque. We use render queue range opaque and the layer mask property to create a filter settings variable.
We create a forward only shader tag ID list. This is needed to build a render list. Now we have the properties needed to create an instance of the draw settings class, which gives the necessary properties to generate a render list handle.
Let's look at the record render graph method.
Here we get the frame data and we pass it to the init renderer list method.
We check if this call gave a valid renderer list handle and return if it didn't.
We tell the builder to use a renderer list, set the render target and [music] depth buffer, and finally set the render function.
It will use the execute pass method, which is actually quite simple.
It clears the render target to green and then calls draw renderer list using the renderer [music] list handle as a parameter.
In the repo, we created a desk layer.
The desk and the things on the desk are on this layer.
If we switch to this layer, we see just the desk on a green background.
This is useful for highlighting something in a scene.
Culling.
>> [music] >> The culling sample is very similar to the render list sample. The key difference is demonstrating how to access the culling results from the camera rather than taking them directly from the universal rendering data call result [music] contained in the frame data.
This will be useful if you want to manually customize the culling results.
Take a look at culling render pass render feature.cs from the URP render graph samples culling folder. Notice the init renderer list method includes a call parameter missing in the render list version.
In it, render list is called from the record render graph method.
To create the cull results, we first get the cull context data from the frame data. Then we call the camera method try get [music] calling parameters. Once we have the calling parameters and the cull context data, [music] we can get the calling results by passing the calling parameters to the cull method of the cull context data instance.
Unsafe [music] pass.
This sample shows how to set up a blit when source and target have different dimensions. This means a raster render pass cannot be used and an unsafe pass is used instead. An unsafe pass, as we learned earlier, cannot be merged and so should only be used when essential. In some cases, using an unsafe pass does make sense. [music] For example, in this sample, we know that the set of adjacent passes are not mergeable because of the size differences. So, we can optimize the render graph compile time on top of simplifying the multiple passes setup.
If we review the record render graph method, you can see three textures are created. The first matches the size of the active color texture. The second halves the size and the third halves it again.
The render function uses blit texture, which handles the difference in size between the source and the destination of the blit. We can see the unsafe pass includes a custom pass data class, which has four texture handles.
The record render graph adds an unsafe pass with pass data.
We set the pass data as the current active color texture.
Create three textures, each the half size of the previous.
We set access for each texture.
In the render function, we get a command buffer and set up four blits.
And we can see the result of the blits using the frame debugger.
MRT or multiple render targets.
This sample demonstrates using multiple render targets with a render graph and URP. This will be useful if a pass needs to output more than a single RGBA texture to output various buffers in one pass.
The MRT renderer feature class includes an array of three render textures.
>> [music] >> The MRT pass includes properties, RTs, an array of three RT handles, and an array of three render target infos, RT infos. The setup method includes a reference to the MRT renderer feature render texture properties as parameter three.
In the method, we check if each of the MRT pass properties, RTs, are empty or do not match the MRT renderer feature render texture for the index.
If this is the case, we optionally release an existing reference and allocate the handle. We also copy the descriptor details.
In the record render graph method, we create an array of texture handles and assign these using the RTs and RT infos created in the setup method. We set the render attachment for these handles.
In the render function, we use draw procedural to render the full screen triangle with a material property. You could use this approach to output to color, normals, and depth buffers in a single pass. Useful for deferred rendering or creating custom lighting buffers. G-buffer visualization. This sample demonstrates accessing G-buffer components inside a pass when they are not global. So, you can inspect or use G-buffer data locally in a pass. This sample applies to a deferred rendering pipeline where lighting is delayed until after surfaces are drawn. Storing surface data in a G-buffer.
This makes handling many lights efficient but has tradeoffs with memory and transparency.
Visualizing can be useful for debugging.
>> [music] >> Again, in the render graph samples folder, take a look at the file G-buffer visualization renderer feature.
First, we store an array of integer values, which are the property IDs of each G-buffer.
In the record render graph method, we set use texture for each G-buffer. If you take a look at the render graph viewer, you'll see the visualized G-buffer components pass as G-buffer zero through to G-buffer four with read access.
G-buffer three is skipped, so it doesn't appear in the resources for the path.
In the render graph function for each G-buffer, we set the texture, then use draw procedural to draw the buffer.
This uses a custom material.
Let's take a look at the shader.
>> [music] >> You'll find G-buffer visualization shader sample.shader in the same folder as the render feature.
It's set to use G-buffer two, the normals buffer.
This is a super simple shader. The vertex shader gets the transform position and UV for the vertex.
And the fragment shader uses the interpolated UV values in the triangle being rendered to sample the buffer. The key takeaway of this sample is how to request access to G-buffer textures when they're not set as global shader properties.
Global G-buffers. As an alternative to the previous example, global G-buffers declares G-buffer textures as global resources in the render graph so that subsequent passes can access them.
Although this sample itself doesn't make any use of them. Take a look at global G-buffers renderer feature.cs.
As in the previous sample, in the global G-buffers render pass, we create an array of IDs of the G-buffers.
In the record render graph method, we get the G-buffer array from the universal resource data found in the frame data passed to the method.
The key is the call to set global G-buffer textures.
In this method, we iterate over the G-buffer array.
And if a buffer is valid and the index is not three, the G-buffer lighting index value, the I raster render graph builder parameter builder, has a method set global texture after pass.
This takes a buffer on an ID and sets the G-buffer as global for subsequent passes.
Some global textures are accessed using specific shader IDs that are internal to URP.
To use the G-buffer in these places, we need to set the ID to point to the corresponding G-buffer component.
This applies to G-buffer normal smoothness index and G-buffer renderer layers index.
If we look at the render graph viewer, you'll see that G-buffers zero to four, excluding the lighting index three, are now all global.
The key takeaway for this sample is how to promote textures from local scope to global scope in render graph.
The render graph API has a fairly steep learning curve, but we hope walking through these best practice approaches has equipped you with lots of useful ways to customize the rendered frame.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 viewsβ’2026-05-28
How agent o11y differs from traditional o11y β Phil Hetzel, Braintrust
aiDotEngineer
450 viewsβ’2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanationπ―β
LearnwithSahera
1K viewsβ’2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 viewsβ’2026-05-29
Search Algorithms Explained in 60 Seconds! π€π¨
samarthtuliofficial
218 viewsβ’2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 viewsβ’2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 viewsβ’2026-05-29
So What's Odin Lang Even Good For
TechOverTea
131 viewsβ’2026-06-01











