143 lines
6.4 KiB
C#
143 lines
6.4 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
|
|
// This example uses the gBuffer components in a RenderPass when they are not global.
|
|
// The RenderPass will by default show the contents of the Specular Metallic Texture (_GBuffer2) on geometry in the scene,
|
|
// but you can change the sampled gBuffer component by modifying the example shader.
|
|
// Make sure to (1) set the rendering path to Deferred and (2) add a 3D object in your scene to see a result.
|
|
public class GbufferVisualizationRendererFeature : ScriptableRendererFeature
|
|
{
|
|
class GBufferVisualizationRenderPass : ScriptableRenderPass
|
|
{
|
|
Material m_Material;
|
|
string m_PassName = "Visualize GBuffer Components (and make gBuffer global)";
|
|
|
|
private static readonly int GbufferLightingIndex = 3;
|
|
|
|
// Other gBuffer components indices
|
|
// private static readonly int GBufferNormalSmoothnessIndex = 2;
|
|
// private static readonly int GbufferDepthIndex = 4;
|
|
// private static readonly int GBufferRenderingLayersIndex = 5;
|
|
|
|
// Components marked as optional are only present when the pipeline requests it.
|
|
// If for example there is no rendering layers texture, _GBuffer5 will contain the ShadowMask texture
|
|
private static readonly int[] s_GBufferShaderPropertyIDs = new int[]
|
|
{
|
|
// Contains Albedo Texture
|
|
Shader.PropertyToID("_GBuffer0"),
|
|
|
|
// Contains Specular Metallic Texture
|
|
Shader.PropertyToID("_GBuffer1"),
|
|
|
|
// Contains Normals and Smoothness, referenced as _CameraNormalsTexture in other shaders
|
|
Shader.PropertyToID("_GBuffer2"),
|
|
|
|
// Contains Lighting texture
|
|
Shader.PropertyToID("_GBuffer3"),
|
|
|
|
// Contains Depth texture, referenced as _CameraDepthTexture in other shaders (optional)
|
|
Shader.PropertyToID("_GBuffer4"),
|
|
|
|
// Contains Rendering Layers Texture, referenced as _CameraRenderingLayersTexture in other shaders (optional)
|
|
Shader.PropertyToID("_GBuffer5"),
|
|
|
|
// Contains ShadowMask texture (optional)
|
|
Shader.PropertyToID("_GBuffer6")
|
|
};
|
|
|
|
private class PassData
|
|
{
|
|
// In this example, we want to use the gBuffer components in our pass.
|
|
public TextureHandle[] gBuffer;
|
|
public Material material;
|
|
}
|
|
|
|
public void Setup(Material material)
|
|
{
|
|
m_Material = material;
|
|
}
|
|
|
|
// This method will draw the contents of the gBuffer component requested in the shader
|
|
static void ExecutePass(PassData data, RasterGraphContext context)
|
|
{
|
|
// Here, we read all the gBuffer components as an example even though the shader only needs one.
|
|
// We still need to set it explicitly since it is not accessible globally (so the
|
|
// shader won't have access to it by default).
|
|
for (int i = 0; i < data.gBuffer.Length; i++)
|
|
{
|
|
data.material.SetTexture(s_GBufferShaderPropertyIDs[i], data.gBuffer[i]);
|
|
}
|
|
|
|
// Draw the gBuffer component requested by the shader over the geometry
|
|
context.cmd.DrawProcedural(Matrix4x4.identity, data.material, 0, MeshTopology.Triangles, 3, 1);
|
|
}
|
|
|
|
// RecordRenderGraph is where the RenderGraph handle can be accessed, through which render passes can be added to the graph.
|
|
// FrameData is a context container through which URP resources can be accessed and managed.
|
|
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
|
{
|
|
UniversalRenderingData universalRenderingData = frameData.Get<UniversalRenderingData>();
|
|
// The gBuffer components are only used in deferred mode
|
|
if (m_Material == null || universalRenderingData.renderingMode != RenderingMode.Deferred)
|
|
return;
|
|
|
|
// Get the gBuffer texture handles stored in the resourceData
|
|
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
|
|
TextureHandle[] gBuffer = resourceData.gBuffer;
|
|
|
|
using (var builder = renderGraph.AddRasterRenderPass<PassData>(m_PassName, out var passData))
|
|
{
|
|
passData.material = m_Material;
|
|
|
|
// For this pass, we want to write to the activeColorTexture, which is the gBuffer Lighting component (_GBuffer3) in the deferred path.
|
|
builder.SetRenderAttachment(resourceData.activeColorTexture, 0, AccessFlags.Write);
|
|
|
|
// We are reading the gBuffer components in our pass, so we need call UseTexture on them.
|
|
// When they are global, they can be all read with builder.UseAllGlobalTexture(true), but
|
|
// in this pass they are not global.
|
|
for (int i = 0; i < resourceData.gBuffer.Length; i++)
|
|
{
|
|
if (i == GbufferLightingIndex)
|
|
{
|
|
// We already specify we are writing to it above (SetRenderAttachment)
|
|
continue;
|
|
}
|
|
|
|
builder.UseTexture(resourceData.gBuffer[i]);
|
|
}
|
|
|
|
// We need to set the gBuffer in the pass' data, otherwise the pass won't have access to it when it is executed.
|
|
passData.gBuffer = gBuffer;
|
|
|
|
// Assigns the ExecutePass function to the render pass delegate. This will be called by the render graph when executing the pass.
|
|
builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecutePass(data, context));
|
|
}
|
|
}
|
|
}
|
|
|
|
GBufferVisualizationRenderPass m_GBufferRenderPass;
|
|
public Material m_Material;
|
|
|
|
/// <inheritdoc/>
|
|
public override void Create()
|
|
{
|
|
m_GBufferRenderPass = new GBufferVisualizationRenderPass
|
|
{
|
|
// This pass must be injected after rendering the deferred lights or later.
|
|
renderPassEvent = RenderPassEvent.AfterRenderingDeferredLights
|
|
};
|
|
}
|
|
|
|
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
|
{
|
|
// The gBuffers are only used in the Deferred rendering path
|
|
if (m_Material != null)
|
|
{
|
|
m_GBufferRenderPass.Setup(m_Material);
|
|
renderer.EnqueuePass(m_GBufferRenderPass);
|
|
}
|
|
}
|
|
}
|