using System; using System.Collections.Generic; using UnityEngine.Experimental.Rendering; using UnityEngine.Experimental.Rendering.RenderGraphModule; using UnityEngine.Profiling; namespace UnityEngine.Rendering.Universal.Internal { /// /// Draw objects into the given color and depth target /// /// You can use this pass to render objects that have a material and/or shader /// with the pass names UniversalForward or SRPDefaultUnlit. /// public class DrawObjectsPass : ScriptableRenderPass { FilteringSettings m_FilteringSettings; RenderStateBlock m_RenderStateBlock; List m_ShaderTagIdList = new List(); string m_ProfilerTag; /// /// Profiling sampler /// protected ProfilingSampler m_ProfilingSampler; bool m_IsOpaque; /// /// Used to indicate whether transparent objects should receive shadows or not. /// public bool m_ShouldTransparentsReceiveShadows; PassData m_PassData; static readonly int s_DrawObjectPassDataPropID = Shader.PropertyToID("_DrawObjectPassData"); /// /// Creates a new DrawObjectsPass instance. /// /// The profiler tag used with the pass. /// /// Marks whether the objects are opaque or transparent. /// The RenderPassEvent to use. /// The RenderQueueRange to use for creating filtering settings that control what objects get rendered. /// The layer mask to use for creating filtering settings that control what objects get rendered. /// The stencil settings to use with this poss. /// The stencil reference value to use with this pass. /// /// /// /// /// public DrawObjectsPass(string profilerTag, ShaderTagId[] shaderTagIds, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference) { base.profilingSampler = new ProfilingSampler(nameof(DrawObjectsPass)); m_PassData = new PassData(); m_ProfilerTag = profilerTag; m_ProfilingSampler = new ProfilingSampler(profilerTag); foreach (ShaderTagId sid in shaderTagIds) m_ShaderTagIdList.Add(sid); renderPassEvent = evt; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); m_IsOpaque = opaque; m_ShouldTransparentsReceiveShadows = false; if (stencilState.enabled) { m_RenderStateBlock.stencilReference = stencilReference; m_RenderStateBlock.mask = RenderStateMask.Stencil; m_RenderStateBlock.stencilState = stencilState; } } /// /// Creates a new DrawObjectsPass instance. /// /// The profiler tag used with the pass. /// Marks whether the objects are opaque or transparent. /// The RenderPassEvent to use. /// The RenderQueueRange to use for creating filtering settings that control what objects get rendered. /// The layer mask to use for creating filtering settings that control what objects get rendered. /// The stencil settings to use with this poss. /// The stencil reference value to use with this pass. /// /// /// /// public DrawObjectsPass(string profilerTag, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference) : this(profilerTag, new ShaderTagId[] { new ShaderTagId("SRPDefaultUnlit"), new ShaderTagId("UniversalForward"), new ShaderTagId("UniversalForwardOnly") }, opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference) { } internal DrawObjectsPass(URPProfileId profileId, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference) : this(profileId.GetType().Name, opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference) { m_ProfilingSampler = ProfilingSampler.Get(profileId); } /// public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { InitPassData(ref renderingData, ref m_PassData); InitRendererLists(ref renderingData, ref m_PassData, context, default(RenderGraph), false); using (new ProfilingScope(renderingData.commandBuffer, m_ProfilingSampler)) { ExecutePass(CommandBufferHelpers.GetRasterCommandBuffer(renderingData.commandBuffer), m_PassData, m_PassData.rendererList, m_PassData.objectsWithErrorRendererList, ref renderingData, renderingData.cameraData.IsCameraProjectionMatrixFlipped()); } } internal static void ExecutePass(RasterCommandBuffer cmd, PassData data, RendererList rendererList, RendererList objectsWithErrorRendererList, ref RenderingData renderingData, bool yFlip) { // Global render pass data containing various settings. // x,y,z are currently unused // w is used for knowing whether the object is opaque(1) or alpha blended(0) Vector4 drawObjectPassData = new Vector4(0.0f, 0.0f, 0.0f, (data.isOpaque) ? 1.0f : 0.0f); cmd.SetGlobalVector(s_DrawObjectPassDataPropID, drawObjectPassData); // scaleBias.x = flipSign // scaleBias.y = scale // scaleBias.z = bias // scaleBias.w = unused float flipSign = yFlip ? -1.0f : 1.0f; Vector4 scaleBias = (flipSign < 0.0f) ? new Vector4(flipSign, 1.0f, -1.0f, 1.0f) : new Vector4(flipSign, 0.0f, 1.0f, 1.0f); cmd.SetGlobalVector(ShaderPropertyId.scaleBiasRt, scaleBias); // Set a value that can be used by shaders to identify when AlphaToMask functionality may be active // The material shader alpha clipping logic requires this value in order to function correctly in all cases. float alphaToMaskAvailable = ((renderingData.cameraData.cameraTargetDescriptor.msaaSamples > 1) && data.isOpaque) ? 1.0f : 0.0f; cmd.SetGlobalFloat(ShaderPropertyId.alphaToMaskAvailable, alphaToMaskAvailable); var activeDebugHandler = GetActiveDebugHandler(ref renderingData); if (activeDebugHandler != null) { data.debugRendererLists.DrawWithRendererList(cmd); } else { cmd.DrawRendererList(rendererList); // Render objects that did not match any shader pass with error shader RenderingUtils.DrawRendererListObjectsWithError(cmd, ref objectsWithErrorRendererList); } } /// /// Shared pass data /// internal class PassData { internal TextureHandle albedoHdl; internal TextureHandle depthHdl; internal RenderingData renderingData; internal bool isOpaque; internal bool shouldTransparentsReceiveShadows; internal RendererListHandle rendererListHdl; internal RendererListHandle objectsWithErrorRendererListHdl; internal DebugRendererLists debugRendererLists; // Required for code sharing purpose between RG and non-RG. internal RendererList rendererList; internal RendererList objectsWithErrorRendererList; } /// /// Initialize the shared pass data. /// /// internal void InitPassData(ref RenderingData renderingData, ref PassData passData) { passData.isOpaque = m_IsOpaque; passData.shouldTransparentsReceiveShadows = m_ShouldTransparentsReceiveShadows; } private void InitRendererLists(ref RenderingData renderingData, ref PassData passData, ScriptableRenderContext context, RenderGraph renderGraph, bool useRenderGraph) { ref Camera camera = ref renderingData.cameraData.camera; var sortFlags = (m_IsOpaque) ? renderingData.cameraData.defaultOpaqueSortFlags : SortingCriteria.CommonTransparent; if (renderingData.cameraData.renderer.useDepthPriming && m_IsOpaque && (renderingData.cameraData.renderType == CameraRenderType.Base || renderingData.cameraData.clearDepth)) sortFlags = SortingCriteria.SortingLayer | SortingCriteria.RenderQueue | SortingCriteria.OptimizeStateChanges | SortingCriteria.CanvasOrder; var filterSettings = m_FilteringSettings; #if UNITY_EDITOR // When rendering the preview camera, we want the layer mask to be forced to Everything if (renderingData.cameraData.isPreviewCamera) { filterSettings.layerMask = -1; } #endif DrawingSettings drawSettings = RenderingUtils.CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, sortFlags); if (renderingData.cameraData.renderer.useDepthPriming && m_IsOpaque && (renderingData.cameraData.renderType == CameraRenderType.Base || renderingData.cameraData.clearDepth)) { m_RenderStateBlock.depthState = new DepthState(false, CompareFunction.Equal); m_RenderStateBlock.mask |= RenderStateMask.Depth; } else if (m_RenderStateBlock.depthState.compareFunction == CompareFunction.Equal) { m_RenderStateBlock.depthState = new DepthState(true, CompareFunction.LessEqual); m_RenderStateBlock.mask |= RenderStateMask.Depth; } var activeDebugHandler = GetActiveDebugHandler(ref renderingData); if (useRenderGraph) { if (activeDebugHandler != null) { passData.debugRendererLists = activeDebugHandler.CreateRendererListsWithDebugRenderState(renderGraph, ref renderingData, ref drawSettings, ref filterSettings, ref m_RenderStateBlock); } else { RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, renderingData, drawSettings, filterSettings, m_RenderStateBlock, ref passData.rendererListHdl); RenderingUtils.CreateRendererListObjectsWithError(renderGraph, ref renderingData.cullResults, camera, filterSettings, sortFlags, ref passData.objectsWithErrorRendererListHdl); } } else { if (activeDebugHandler != null) { passData.debugRendererLists = activeDebugHandler.CreateRendererListsWithDebugRenderState(context, ref renderingData, ref drawSettings, ref filterSettings, ref m_RenderStateBlock); } else { RenderingUtils.CreateRendererListWithRenderStateBlock(context, renderingData, drawSettings, filterSettings, m_RenderStateBlock, ref passData.rendererList); RenderingUtils.CreateRendererListObjectsWithError(context, ref renderingData.cullResults, camera, filterSettings, sortFlags, ref passData.objectsWithErrorRendererList); } } } internal void Render(RenderGraph renderGraph, TextureHandle colorTarget, TextureHandle depthTarget, TextureHandle mainShadowsTexture, TextureHandle additionalShadowsTexture, ref RenderingData renderingData) { using (var builder = renderGraph.AddRasterRenderPass("Draw Objects Pass", out var passData, m_ProfilingSampler)) { InitPassData(ref renderingData, ref passData); passData.renderingData = renderingData; if (colorTarget.IsValid()) passData.albedoHdl = builder.UseTextureFragment(colorTarget, 0, IBaseRenderGraphBuilder.AccessFlags.Write); if (depthTarget.IsValid()) passData.depthHdl = builder.UseTextureFragmentDepth(depthTarget, IBaseRenderGraphBuilder.AccessFlags.Write); if (mainShadowsTexture.IsValid()) builder.UseTexture(mainShadowsTexture, IBaseRenderGraphBuilder.AccessFlags.Read); if (additionalShadowsTexture.IsValid()) builder.UseTexture(additionalShadowsTexture, IBaseRenderGraphBuilder.AccessFlags.Read); UniversalRenderer renderer = renderingData.cameraData.renderer as UniversalRenderer; if (renderer != null) { TextureHandle ssaoTexture = renderer.resources.GetTexture(UniversalResource.SSAOTexture); if (ssaoTexture.IsValid()) builder.UseTexture(ssaoTexture, IBaseRenderGraphBuilder.AccessFlags.Read); } InitRendererLists(ref renderingData, ref passData, default(ScriptableRenderContext), renderGraph, true); var activeDebugHandler = GetActiveDebugHandler(ref renderingData); if (activeDebugHandler != null) { passData.debugRendererLists.PrepareRendererListForRasterPass(builder); } else { builder.UseRendererList(passData.rendererListHdl); builder.UseRendererList(passData.objectsWithErrorRendererListHdl); } builder.AllowPassCulling(false); builder.AllowGlobalStateModification(true); builder.SetRenderFunc((PassData data, RasterGraphContext context) => { ref var renderingData = ref data.renderingData; // Currently we only need to call this additional pass when the user // doesn't want transparent objects to receive shadows if (!data.isOpaque && !data.shouldTransparentsReceiveShadows) TransparentSettingsPass.ExecutePass(context.cmd, data.shouldTransparentsReceiveShadows); bool yFlip = renderingData.cameraData.IsRenderTargetProjectionMatrixFlipped(data.albedoHdl, data.depthHdl); ExecutePass(context.cmd, data, data.rendererListHdl, data.objectsWithErrorRendererListHdl, ref renderingData, yFlip); }); } } } /// /// Extension of DrawObjectPass that also output Rendering Layers Texture as second render target. /// internal class DrawObjectsWithRenderingLayersPass : DrawObjectsPass { RTHandle[] m_ColorTargetIndentifiers; RTHandle m_DepthTargetIndentifiers; /// /// Creates a new DrawObjectsWithRenderingLayersPass instance. /// /// The profiler tag used with the pass. /// Marks whether the objects are opaque or transparent. /// The RenderPassEvent to use. /// The RenderQueueRange to use for creating filtering settings that control what objects get rendered. /// The layer mask to use for creating filtering settings that control what objects get rendered. /// The stencil settings to use with this poss. /// The stencil reference value to use with this pass. public DrawObjectsWithRenderingLayersPass(URPProfileId profilerTag, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference) : base(profilerTag, opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference) { m_ColorTargetIndentifiers = new RTHandle[2]; } /// /// Sets up the pass. /// /// Color attachment handle. /// Texture used with rendering layers. /// Depth attachment handle. /// public void Setup(RTHandle colorAttachment, RTHandle renderingLayersTexture, RTHandle depthAttachment) { if (colorAttachment == null) throw new ArgumentException("Color attachment can not be null", "colorAttachment"); if (renderingLayersTexture == null) throw new ArgumentException("Rendering layers attachment can not be null", "renderingLayersTexture"); if (depthAttachment == null) throw new ArgumentException("Depth attachment can not be null", "depthAttachment"); m_ColorTargetIndentifiers[0] = colorAttachment; m_ColorTargetIndentifiers[1] = renderingLayersTexture; m_DepthTargetIndentifiers = depthAttachment; } /// public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { ConfigureTarget(m_ColorTargetIndentifiers, m_DepthTargetIndentifiers); } /// public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = renderingData.commandBuffer; // Enable Rendering Layers CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.WriteRenderingLayers, true); // Execute base.Execute(context, ref renderingData); // Clean up CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.WriteRenderingLayers, false); } private class RenderingLayersPassData { internal PassData basePassData; internal RenderingLayerUtils.MaskSize maskSize; public RenderingLayersPassData() { basePassData = new PassData(); } } internal void Render(RenderGraph renderGraph, TextureHandle colorTarget, TextureHandle renderingLayersTexture, TextureHandle depthTarget, TextureHandle mainShadowsTexture, TextureHandle additionalShadowsTexture, RenderingLayerUtils.MaskSize maskSize, ref RenderingData renderingData) { using (var builder = renderGraph.AddRasterRenderPass("Draw Objects With Rendering Layers Pass", out var passData, m_ProfilingSampler)) { InitPassData(ref renderingData, ref passData.basePassData); passData.basePassData.renderingData = renderingData; passData.maskSize = maskSize; passData.basePassData.albedoHdl = builder.UseTextureFragment(colorTarget, 0, IBaseRenderGraphBuilder.AccessFlags.Write); builder.UseTextureFragment(renderingLayersTexture, 1, IBaseRenderGraphBuilder.AccessFlags.Write); passData.basePassData.depthHdl = builder.UseTextureFragmentDepth(depthTarget, IBaseRenderGraphBuilder.AccessFlags.Write); if (mainShadowsTexture.IsValid()) builder.UseTexture(mainShadowsTexture, IBaseRenderGraphBuilder.AccessFlags.Read); if (additionalShadowsTexture.IsValid()) builder.UseTexture(additionalShadowsTexture, IBaseRenderGraphBuilder.AccessFlags.Read); UniversalRenderer renderer = renderingData.cameraData.renderer as UniversalRenderer; if (renderer != null) { TextureHandle ssaoTexture = renderer.resources.GetTexture(UniversalResource.SSAOTexture); if (ssaoTexture.IsValid()) builder.UseTexture(ssaoTexture, IBaseRenderGraphBuilder.AccessFlags.Read); } builder.AllowPassCulling(false); // Required here because of RenderingLayerUtils.SetupProperties builder.AllowGlobalStateModification(true); builder.SetRenderFunc((RenderingLayersPassData data, RasterGraphContext context) => { // Enable Rendering Layers CoreUtils.SetKeyword(context.cmd, ShaderKeywordStrings.WriteRenderingLayers, true); RenderingLayerUtils.SetupProperties(context.cmd, data.maskSize); // Currently we only need to call this additional pass when the user // doesn't want transparent objects to receive shadows if (!data.basePassData.isOpaque && !data.basePassData.shouldTransparentsReceiveShadows) TransparentSettingsPass.ExecutePass(context.cmd, data.basePassData.shouldTransparentsReceiveShadows); ref var renderingData = ref data.basePassData.renderingData; bool yFlip = renderingData.cameraData.IsRenderTargetProjectionMatrixFlipped(data.basePassData.albedoHdl, data.basePassData.depthHdl); // Execute ExecutePass(context.cmd, data.basePassData, data.basePassData.rendererListHdl, data.basePassData.objectsWithErrorRendererListHdl, ref renderingData, yFlip); // Clean up CoreUtils.SetKeyword(context.cmd, ShaderKeywordStrings.WriteRenderingLayers, false); }); } } } }