194 lines
8.2 KiB
C#

// 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");
}
/// <summary>
/// Returns true when fur is active and fur prepass buffer rendering is enabled.
/// </summary>
public bool ShouldRenderFurPrepassBuffer()
{
return ShouldEnqueue() && settings.furPrepassBuffer;
}
/// <summary>
/// Returns true only on standalone/editor platforms when fur support is enabled
/// and the current graphics device supports geometry shaders.
/// </summary>
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<PassData>(passName, out var passData))
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
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
}
}