// SPDX-License-Identifier: (Not available for this version, you are only allowed to use this software if you have express permission from the copyright holder and agreed to the latest NiloToonURP EULA) // Copyright (c) 2021 Kuroneko ShaderLab Limited // Fur rendering pass — draws lilToon-style barycentric fur shells. // Two ShaderTagIds: depth prepass first, then color pass. // Standalone/Editor only, and only when the current graphics device supports geometry shaders. // Renderer feature toggle defaults OFF. using System; using UnityEngine; using UnityEngine.Rendering; #if UNITY_6000_0_OR_NEWER using UnityEngine.Rendering.RenderGraphModule; #endif using UnityEngine.Rendering.Universal; namespace NiloToon.NiloToonURP { public class NiloToonFurPass : ScriptableRenderPass { [Serializable] public class Settings { [Tooltip("Enable fur shell rendering for NiloToonCharacter materials that have Fur enabled.\n" + "Standalone only. Has no effect on mobile platforms.\n\n" + "Default: OFF")] [OverrideDisplayName("Allow Render?")] [Revertible] public bool supportFur = false; [Tooltip("Render fur shells into the prepass buffer so fur silhouette edges are included in bloom/tonemapping character area detection and face mask.\n" + "Without this, fur edges may be excluded from NiloToon post-processing effects.\n\n" + "Default: ON")] [DisableIf("supportFur", false)] [OverrideDisplayName(" Draw in Nilo Prepass")] [Revertible] public bool furPrepassBuffer = true; } static readonly ShaderTagId furDepthPrepassShaderTagId = new ShaderTagId("NiloToonFurDepthPrepass"); static readonly ShaderTagId furColorShaderTagId = new ShaderTagId("NiloToonFur"); NiloToonRendererFeatureSettings allSettings; Settings settings; ProfilingSampler m_ProfilingSamplerFur; public NiloToonFurPass(NiloToonRendererFeatureSettings allSettings) { this.allSettings = allSettings; this.settings = allSettings.furSettings; m_ProfilingSamplerFur = new ProfilingSampler("NiloToonFurPass"); } /// /// Returns true when fur is active and fur prepass buffer rendering is enabled. /// public bool ShouldRenderFurPrepassBuffer() { return ShouldEnqueue() && settings.furPrepassBuffer; } /// /// Returns true only on standalone/editor platforms when fur support is enabled /// and the current graphics device supports geometry shaders. /// public bool ShouldEnqueue() { if (!settings.supportFur) return false; #if UNITY_STANDALONE || UNITY_EDITOR return SystemInfo.supportsGeometryShaders; #else return false; #endif } //===================================================================== // Legacy (pre-RenderGraph) path //===================================================================== #if !UNITY_6000_4_OR_NEWER #if UNITY_6000_0_OR_NEWER [Obsolete] #endif public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { // do nothing } #if UNITY_6000_0_OR_NEWER [Obsolete] #endif public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if (!ShouldEnqueue()) return; CommandBuffer cmd = CommandBufferPool.Get(); using (new ProfilingScope(cmd, m_ProfilingSamplerFur)) { context.ExecuteCommandBuffer(cmd); cmd.Clear(); // Draw fur depth prepass first DrawingSettings drawingSettingsDepth = CreateDrawingSettings(furDepthPrepassShaderTagId, ref renderingData, SortingCriteria.CommonOpaque); FilteringSettings filteringSettings = new FilteringSettings(RenderQueueRange.opaque); NiloToonRenderingUtils.DrawRendererListOrRenderers(context, cmd, renderingData.cullResults, ref drawingSettingsDepth, ref filteringSettings); // Execute depth prepass before color pass context.ExecuteCommandBuffer(cmd); cmd.Clear(); // Draw fur color pass DrawingSettings drawingSettingsColor = CreateDrawingSettings(furColorShaderTagId, ref renderingData, SortingCriteria.CommonOpaque); NiloToonRenderingUtils.DrawRendererListOrRenderers(context, cmd, renderingData.cullResults, ref drawingSettingsColor, ref filteringSettings); } context.ExecuteCommandBuffer(cmd); cmd.Clear(); CommandBufferPool.Release(cmd); } #endif public override void OnCameraCleanup(CommandBuffer cmd) { // do nothing } //===================================================================== // RenderGraph path (Unity 6+) //===================================================================== #if UNITY_6000_0_OR_NEWER class PassData { public RendererListHandle depthPrepassRendererListHandle; public RendererListHandle colorRendererListHandle; public bool shouldRender; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { string passName = "NiloToonFur(RG)"; using (var builder = renderGraph.AddRasterRenderPass(passName, out var passData)) { UniversalResourceData resourceData = frameData.Get(); UniversalCameraData cameraData = frameData.Get(); UniversalRenderingData renderingData = frameData.Get(); UniversalLightData lightData = frameData.Get(); SortingCriteria sortFlags = SortingCriteria.CommonOpaque; FilteringSettings filterSettings = new FilteringSettings(RenderQueueRange.opaque, ~0); // Depth prepass renderer list DrawingSettings drawSettingsDepth = RenderingUtils.CreateDrawingSettings(furDepthPrepassShaderTagId, renderingData, cameraData, lightData, sortFlags); var depthRendererListParams = new RendererListParams(renderingData.cullResults, drawSettingsDepth, filterSettings); passData.depthPrepassRendererListHandle = renderGraph.CreateRendererList(depthRendererListParams); // Color pass renderer list DrawingSettings drawSettingsColor = RenderingUtils.CreateDrawingSettings(furColorShaderTagId, renderingData, cameraData, lightData, sortFlags); var colorRendererListParams = new RendererListParams(renderingData.cullResults, drawSettingsColor, filterSettings); passData.colorRendererListHandle = renderGraph.CreateRendererList(colorRendererListParams); passData.shouldRender = ShouldEnqueue(); builder.UseRendererList(passData.depthPrepassRendererListHandle); builder.UseRendererList(passData.colorRendererListHandle); if (resourceData.activeColorTexture.IsValid()) builder.SetRenderAttachment(resourceData.activeColorTexture, 0); if (resourceData.activeDepthTexture.IsValid()) builder.SetRenderAttachmentDepth(resourceData.activeDepthTexture, AccessFlags.Write); builder.AllowGlobalStateModification(true); builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecutePassRG(data, context)); } } static void ExecutePassRG(PassData data, RasterGraphContext context) { if (!data.shouldRender) return; var cmd = context.cmd; // Draw depth prepass first, then color pass cmd.DrawRendererList(data.depthPrepassRendererListHandle); cmd.DrawRendererList(data.colorRendererListHandle); } #endif } }