2025-12-15 01:52:59 +09:00

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
}
}