221 lines
8.8 KiB
C#
221 lines
8.8 KiB
C#
// NiloToon Fur Mask Pass
|
|
// Renders fur shells to a dedicated mask buffer for fur area detection
|
|
// Separate from main fur rendering for cleaner architecture
|
|
// Based on NiloToonPrepassBufferRTPass pattern
|
|
|
|
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.RendererUtils;
|
|
#if UNITY_6000_0_OR_NEWER
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
#endif
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
namespace NiloToon.NiloToonURP
|
|
{
|
|
public class NiloToonFurMaskPass : ScriptableRenderPass
|
|
{
|
|
static readonly ShaderTagId furShellMaskLightModeShaderTagId = new ShaderTagId("NiloToonFurShellMask");
|
|
static readonly int FurMaskTexId = Shader.PropertyToID("_NiloToonFurMaskTex");
|
|
|
|
[Serializable]
|
|
public class Settings
|
|
{
|
|
[Tooltip("Create a mask buffer for fur area detection.\n" +
|
|
"This creates _NiloToonFurMaskTex which can be used for post-processing.\n\nDefault: ON")]
|
|
public bool Enabled = true;
|
|
}
|
|
|
|
Settings settings;
|
|
RenderQueueRange renderQueueRange;
|
|
|
|
#if UNITY_2022_2_OR_NEWER
|
|
RTHandle furMaskRTHColor;
|
|
RTHandle furMaskRTHDepth;
|
|
#else
|
|
RenderTargetHandle furMaskRTHandle;
|
|
#endif
|
|
|
|
public NiloToonFurMaskPass(Settings settings)
|
|
{
|
|
this.settings = settings;
|
|
this.renderQueueRange = RenderQueueRange.all;
|
|
|
|
#if !UNITY_2022_2_OR_NEWER
|
|
furMaskRTHandle.Init("_NiloToonFurMaskRT");
|
|
#endif
|
|
|
|
base.profilingSampler = new ProfilingSampler(nameof(NiloToonFurMaskPass));
|
|
}
|
|
|
|
#if UNITY_6000_0_OR_NEWER
|
|
[Obsolete]
|
|
#endif
|
|
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
|
{
|
|
if (!settings.Enabled)
|
|
return;
|
|
|
|
var cameraData = renderingData.cameraData;
|
|
|
|
#if UNITY_2022_2_OR_NEWER
|
|
// [color RTHandle's Descriptor] - same pattern as NiloToonPrepassBufferRTPass
|
|
var colorDesc = cameraData.cameraTargetDescriptor;
|
|
colorDesc.graphicsFormat = GraphicsFormat.R8G8B8A8_UNorm;
|
|
colorDesc.depthBufferBits = 0;
|
|
colorDesc.msaaSamples = 1;
|
|
|
|
// [depth RTHandle's Descriptor] - create own depth buffer like PrepassBufferRTPass
|
|
var depthDesc = cameraData.cameraTargetDescriptor;
|
|
depthDesc.graphicsFormat = GraphicsFormat.None; // Depth only rendering
|
|
depthDesc.depthStencilFormat = cameraData.cameraTargetDescriptor.depthStencilFormat;
|
|
depthDesc.msaaSamples = 1;
|
|
|
|
RenderingUtils.ReAllocateIfNeeded(ref furMaskRTHColor, colorDesc, name: "_NiloToonFurMaskColor");
|
|
RenderingUtils.ReAllocateIfNeeded(ref furMaskRTHDepth, depthDesc, name: "_NiloToonFurMaskDepth");
|
|
|
|
// Set global RT
|
|
cmd.SetGlobalTexture(FurMaskTexId, furMaskRTHColor);
|
|
|
|
// Configure target with own color and depth buffers
|
|
ConfigureTarget(furMaskRTHColor, furMaskRTHDepth);
|
|
ConfigureClear(ClearFlag.All, Color.black);
|
|
#else
|
|
var furMaskDesc = cameraData.cameraTargetDescriptor;
|
|
furMaskDesc.colorFormat = RenderTextureFormat.ARGB32;
|
|
furMaskDesc.depthStencilFormat = cameraData.cameraTargetDescriptor.depthStencilFormat;
|
|
furMaskDesc.msaaSamples = 1;
|
|
|
|
cmd.GetTemporaryRT(furMaskRTHandle.id, furMaskDesc);
|
|
cmd.SetGlobalTexture(FurMaskTexId, furMaskRTHandle.Identifier());
|
|
|
|
ConfigureTarget(new RenderTargetIdentifier(furMaskRTHandle.Identifier(), 0, CubemapFace.Unknown, -1));
|
|
ConfigureClear(ClearFlag.All, Color.black);
|
|
#endif
|
|
}
|
|
|
|
#if UNITY_6000_0_OR_NEWER
|
|
[Obsolete]
|
|
#endif
|
|
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
|
{
|
|
if (!settings.Enabled)
|
|
return;
|
|
|
|
// Never draw in Preview
|
|
Camera camera = renderingData.cameraData.camera;
|
|
if (camera.cameraType == CameraType.Preview)
|
|
return;
|
|
|
|
var cmd = CommandBufferPool.Get();
|
|
using (new ProfilingScope(cmd, base.profilingSampler))
|
|
{
|
|
// Execute command buffer before DrawRenderers (frame debugger requirement)
|
|
context.ExecuteCommandBuffer(cmd);
|
|
cmd.Clear();
|
|
|
|
// Draw all fur shells with NiloToonFurShellMask pass
|
|
var filterSetting = new FilteringSettings(renderQueueRange);
|
|
var sortFlags = SortingCriteria.CommonTransparent;
|
|
var drawSettings = CreateDrawingSettings(furShellMaskLightModeShaderTagId, ref renderingData, sortFlags);
|
|
drawSettings.perObjectData = PerObjectData.None;
|
|
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSetting);
|
|
}
|
|
|
|
context.ExecuteCommandBuffer(cmd);
|
|
cmd.Clear();
|
|
CommandBufferPool.Release(cmd);
|
|
}
|
|
|
|
public override void OnCameraCleanup(CommandBuffer cmd)
|
|
{
|
|
// To Release a RTHandle, do it in ScriptableRendererFeature's Dispose(), don't do it in OnCameraCleanup(...)
|
|
#if !UNITY_2022_2_OR_NEWER
|
|
if (settings.Enabled)
|
|
{
|
|
cmd.ReleaseTemporaryRT(furMaskRTHandle.id);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if UNITY_2022_2_OR_NEWER
|
|
public void Dispose()
|
|
{
|
|
furMaskRTHColor?.Release();
|
|
furMaskRTHDepth?.Release();
|
|
}
|
|
#endif
|
|
|
|
#if UNITY_6000_0_OR_NEWER
|
|
private class FurMaskPassData
|
|
{
|
|
public UniversalCameraData cameraData;
|
|
public TextureHandle colorTarget;
|
|
public TextureHandle depthTarget;
|
|
public RendererListHandle rendererList;
|
|
}
|
|
|
|
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
|
{
|
|
if (!settings.Enabled)
|
|
return;
|
|
|
|
var cameraData = frameData.Get<UniversalCameraData>();
|
|
var resourceData = frameData.Get<UniversalResourceData>();
|
|
var renderingData = frameData.Get<UniversalRenderingData>();
|
|
|
|
// Skip Preview cameras - same check as Execute
|
|
if (cameraData.camera.cameraType == CameraType.Preview)
|
|
return;
|
|
|
|
using (var builder = renderGraph.AddRasterRenderPass<FurMaskPassData>("NiloToon Fur Mask Buffer", out var passData, base.profilingSampler))
|
|
{
|
|
passData.cameraData = cameraData;
|
|
|
|
// Create render textures for RG - EXACT same descriptors as OnCameraSetup
|
|
var colorDesc = cameraData.cameraTargetDescriptor;
|
|
colorDesc.graphicsFormat = GraphicsFormat.R8G8B8A8_UNorm;
|
|
colorDesc.depthBufferBits = 0;
|
|
colorDesc.msaaSamples = 1;
|
|
|
|
var depthDesc = cameraData.cameraTargetDescriptor;
|
|
depthDesc.graphicsFormat = GraphicsFormat.None; // Depth only rendering
|
|
depthDesc.depthStencilFormat = cameraData.cameraTargetDescriptor.depthStencilFormat;
|
|
depthDesc.msaaSamples = 1;
|
|
|
|
passData.colorTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorDesc, "_NiloToonFurMaskColor", false);
|
|
passData.depthTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, depthDesc, "_NiloToonFurMaskDepth", false);
|
|
|
|
// Create renderer list - EXACT same settings as Execute
|
|
var renderListDesc = new RendererListDesc(furShellMaskLightModeShaderTagId, renderingData.cullResults, cameraData.camera)
|
|
{
|
|
sortingCriteria = SortingCriteria.CommonTransparent,
|
|
renderQueueRange = renderQueueRange,
|
|
};
|
|
passData.rendererList = renderGraph.CreateRendererList(renderListDesc);
|
|
|
|
// Set up render targets - Match ConfigureTarget behavior
|
|
builder.SetRenderAttachment(passData.colorTarget, 0, AccessFlags.Write);
|
|
builder.SetRenderAttachmentDepth(passData.depthTarget, AccessFlags.Write);
|
|
|
|
builder.UseRendererList(passData.rendererList);
|
|
|
|
// Set global textures - Match cmd.SetGlobalTexture behavior
|
|
builder.SetGlobalTextureAfterPass(passData.colorTarget, FurMaskTexId);
|
|
|
|
builder.SetRenderFunc((FurMaskPassData data, RasterGraphContext context) =>
|
|
{
|
|
// Match ConfigureClear(ClearFlag.All, Color.black) behavior
|
|
context.cmd.ClearRenderTarget(RTClearFlags.All, Color.black, 1.0f, 0);
|
|
|
|
// Match context.DrawRenderers behavior
|
|
context.cmd.DrawRendererList(data.rendererList);
|
|
});
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|