Streamingle_URP/Assets/NiloToonURP/Runtime/RendererFeatures/NiloToonAllInOneRendererFeature.cs
user 010beaea75 Chore: NiloToonURP 업데이트 및 배경 썸네일 갱신
- NiloToonURP 외부 에셋 업데이트
- 배경 씬 썸네일 16:9 해상도로 갱신
- 렌더 파이프라인 설정 업데이트
- 외부 셰이더 그래프 업데이트 (LEDScreen, PIDI Planar Reflections)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:28:02 +09:00

366 lines
20 KiB
C#

// An all in one high-level RendererFeature that contains all NiloToon related passes, user only need to add this RendererFeature in their renderer
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Serialization;
namespace NiloToon.NiloToonURP
{
[Serializable]
public class NiloToonRendererFeatureSettings
{
[Header("Outline settings")]
public NiloToonToonOutlinePass.Settings outlineSettings = new NiloToonToonOutlinePass.Settings();
[Header("Misc settings")]
public NiloToonSetToonParamPass.Settings MiscSettings = new NiloToonSetToonParamPass.Settings();
[Header("Average URP shadow map")]
public NiloToonAverageShadowTestRTPass.Settings sphereShadowTestSettings = new NiloToonAverageShadowTestRTPass.Settings();
[Header("Anime PostProcess")]
public NiloToonAnimePostProcessPass.Settings animePostProcessSettings = new NiloToonAnimePostProcessPass.Settings();
[Header("Bloom & Tonemap")]
public NiloToonUberPostProcessPass.Settings uberPostProcessSettings = new NiloToonUberPostProcessPass.Settings();
[Header("Char SelfShadow")]
public NiloToonCharSelfShadowMapRTPass.Settings charSelfShadowSettings = new NiloToonCharSelfShadowMapRTPass.Settings();
[Header("Motion Blur")]
public NiloToonMotionBlurPass.Settings motionBlurSettings = new NiloToonMotionBlurPass.Settings();
[Header("Prepass Buffer")]
[Revertible]
public bool forceRenderPrepassBuffer = false;
[Header("Override Shader stripping")]
[OverrideDisplayName("Shader Stripping Settings")]
[Tooltip("This slot is useful when you are in the following situation:\n" +
"- Want to reduce build time/build size/runtime shader memory usage\n" +
"- Found that a feature is missing in build, and want to include that feature in build\n\n" +
"If you haven't configured this slot, NiloToon will default to the platform-specific stripping setting. This may not provide the optimal configuration for your project." +
"\n\n" +
"To create a custom stripping setting for enhanced shader stripping control, follow these steps:\n" +
"1.Right click in the project window\n" +
"2.Navigate to 'Create > NiloToon > NiloToonShaderStrippingSettingSO', click it to create a custom stripping setting\n" +
"3.Assign that setting to this slot.\n\n" +
"Please note:\n" +
"* You only need one instance of NiloToonShaderStrippingSettingSO in your project.\n" +
"* Ensure that all NiloToon renderer features share the same NiloToonShaderStrippingSettingSO for consistency.")]
public NiloToonShaderStrippingSettingSO shaderStrippingSettingSO;
}
[DisallowMultipleRendererFeature]
public class NiloToonAllInOneRendererFeature : ScriptableRendererFeature
{
public NiloToonRendererFeatureSettings settings = new NiloToonRendererFeatureSettings();
NiloToonSetToonParamPass SetToonParamPass;
NiloToonAverageShadowTestRTPass SphereShadowTestRTPass;
NiloToonCharSelfShadowMapRTPass CharSelfShadowMapRTRenderPass;
NiloToonDrawSkyboxPass SkyboxRedrawBeforeOpaquePass;
NiloToonToonOutlinePass ToonOutlinePass;
NiloToonToonOutlinePass ToonOutlinePass_RightAfterTransparent;
NiloToonScreenSpaceOutlinePass ScreenSpaceOutlinePass;
NiloToonExtraThickOutlinePass ExtraThickOutlinePass;
NiloToonAnimePostProcessPass AnimePostProcessPass;
NiloToonPrepassBufferRTPass PrepassBufferRTPass;
NiloToonUberPostProcessPass UberPostProcessPass;
#if UNITY_2022_3_OR_NEWER
NiloToonMotionBlurPass MotionBlurPass;
#endif
// This method is used to initialize the ScriptableRenderPass and any required resources.
// Unity calls this method in OnEnable/OnValidate -
// so every time the project loads, enter/exit play mode, scripts recompile, or serialisation changes.
// You should create all passes and set their renderPassEvent inside Create()
public override void Create()
{
// this can only ensure the last used(Created) NiloToonAllInOneRendererFeature can be accessed by NiloToonAllInOneRendererFeature.Instance
// (safe to use when :1 Universal Renderer asset per 1 quality setting)
// (unsafe to use when :N Universal Renderer asset per 1 quality setting)
// TODO: we should remove Singleton of ScriptableRendererFeature/ScriptableRenderPass completely, since it is not a safe design for ScriptableRendererFeature/ScriptableRenderPass due to URP's multi-renderer per URP asset design
// TODO: how about we turn Instance(Singleton) to Instances? where "Instances" is a list containing all Created and not yet deleted instances?
_instance = this;
ReInitPassesIfNeeded();
}
private void ReInitPassesIfNeeded()
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Create all passes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (SetToonParamPass == null)
SetToonParamPass = new NiloToonSetToonParamPass(settings);
if (SphereShadowTestRTPass == null)
SphereShadowTestRTPass = new NiloToonAverageShadowTestRTPass(settings);
if (CharSelfShadowMapRTRenderPass == null)
CharSelfShadowMapRTRenderPass = new NiloToonCharSelfShadowMapRTPass(settings);
if (SkyboxRedrawBeforeOpaquePass == null)
SkyboxRedrawBeforeOpaquePass = new NiloToonDrawSkyboxPass(RenderPassEvent.BeforeRenderingOpaques);
// RenderQueueRange.opaque = render queue(0-2500) materials
if (ToonOutlinePass == null)
ToonOutlinePass = new NiloToonToonOutlinePass(settings, RenderQueueRange.opaque, "NiloToonToonOutlinePass(Classic outline - OpaqueQueue)");
// RenderQueueRange.transparent = render queue(2501-5000) materials
if (ToonOutlinePass_RightAfterTransparent == null)
ToonOutlinePass_RightAfterTransparent = new NiloToonToonOutlinePass(settings, RenderQueueRange.transparent, "NiloToonToonOutlinePass(Classic outline - TransparentQueue)");
if (ScreenSpaceOutlinePass == null)
ScreenSpaceOutlinePass = new NiloToonScreenSpaceOutlinePass(settings);
if (ExtraThickOutlinePass == null)
ExtraThickOutlinePass = new NiloToonExtraThickOutlinePass(settings);
if (AnimePostProcessPass == null)
AnimePostProcessPass = new NiloToonAnimePostProcessPass(settings);
if (PrepassBufferRTPass == null)
PrepassBufferRTPass = new NiloToonPrepassBufferRTPass(settings);
if (UberPostProcessPass == null)
UberPostProcessPass = new NiloToonUberPostProcessPass(settings);
#if UNITY_2022_3_OR_NEWER
if (MotionBlurPass == null)
MotionBlurPass = new NiloToonMotionBlurPass(settings);
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Configures where the render pass should be injected.
// sorted by RenderPassEvent order here
// (*Be aware that camera matrices and stereo rendering is not set up until the BeforeRenderingPrePasses event (value of 150))
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// this pass must be in front of all other NiloToon passes
// DO NOT set this at AfterRenderingPrePasses or later!
SetToonParamPass.renderPassEvent = RenderPassEvent.BeforeRenderingPrePasses;
// require after _CameraDepthTexture is ready, since character shader's NiloToonPrepassBuffer pass needs _CameraDepthTexture. (AfterRenderingPrePasses is still too early because _CameraDepthTexture is not ready at this timing)
PrepassBufferRTPass.renderPassEvent = RenderPassEvent.BeforeRenderingOpaques - 2;
SphereShadowTestRTPass.renderPassEvent = RenderPassEvent.BeforeRenderingOpaques - 1;
CharSelfShadowMapRTRenderPass.renderPassEvent = RenderPassEvent.BeforeRenderingOpaques - 1;
ScreenSpaceOutlinePass.renderPassEvent = RenderPassEvent.BeforeRenderingOpaques - 1; // use BeforeRenderingOpaques because we need depth and normal texture, URP's SSAO is BeforeRenderingOpaques also
// SkyboxRedrawBeforeOpaquePass's renderPassEvent is BeforeRenderingOpaques - 0 (defined when calling the pass's constructor, so we didn't write it here)
// ...(X)
// [The chart below shows all NiloToon's skybox and outline draw timing & order]
//---------------------------------------
// BeforeRenderingOpaques (= Before RenderQueue 0) -> NiloToon's SkyboxRedrawBeforeOpaquePass
// URP RenderingOpaques, draw RenderQueue 0~2500's color pass "UniversalForwardOnly"
// AfterRenderingOpaque (= After RenderQueue 2500) -> (X)
//---------------------------------------
// BeforeRenderingSkybox (= Before Skybox) -> (X)
// URP draw skybox
// (ToonOutlinePass) AfterRenderingSkybox (= After Skybox)
//---------------------------------------
// (X) Before Transparent (= before RenderQueue 2501)
// URP draw RenderQueue 2501~5000's color pass "UniversalForwardOnly"
// (ToonOutlinePass_RightAfterTransparent) After Transparent (= After RenderQueue 5000)
//---------------------------------------
ToonOutlinePass.renderPassEvent = RenderPassEvent.AfterRenderingSkybox; // use AfterRenderingSkybox instead of BeforeRenderingSkybox, to make "semi-transparent(ZWrite) + outline" blend with skybox correctly
ToonOutlinePass_RightAfterTransparent.renderPassEvent = RenderPassEvent.AfterRenderingTransparents + 0; // right after transparent materials finish drawing, draw this outline pass
ExtraThickOutlinePass.renderPassEvent = settings.outlineSettings.extraThickOutlineRenderTiming; // default use AfterRenderingTransparents, because we want this outline not being blocked by transparent effects
// AnimePostProcessPass's renderPassEvent will be decided by the pass itself
UberPostProcessPass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing + UberPostProcessPass.settings.renderPassEventTimingOffset;
}
// Here you can inject one or multiple render passes in the renderer.
// This method is called when setting up the renderer once per-camera.
// *be aware this is called every frame/update, so avoid creating/instantiating anything in here (can use the Create method for that).
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
// a fix to any possible missing/null pass
ReInitPassesIfNeeded();
//----------------------------------------------------------------
// temp fix for supporting SetToonParamPass+RenderGraph
// since we can't call ConfigureInput() inside RecordRenderGraph()
//----------------------------------------------------------------
SetToonParamPass.ConfigureInputs(renderer);
ScreenSpaceOutlinePass.ConfigureInput(renderingData.cameraData.cameraType);
#if UNITY_2022_3_OR_NEWER
MotionBlurPass.ConfigureInputs(renderingData.cameraData.camera);
#endif
// Also note that by default it would enqueue the pass for all cameras -
// including ones used by the Unity Editor.
// In order to avoid this, we can test the camera type and return before enqueueing
// (or can check later during Execute to prevent that function running, if you prefer).
// Example code:
//if (renderingData.cameraData.isPreviewCamera) return;
//if (renderingData.cameraData.isSceneViewCamera) return;
#if !UNITY_2022_2_OR_NEWER
RenderTextureDescriptor cameraTargetDescriptor = renderingData.cameraData.cameraTargetDescriptor;
RenderTargetHandle cameraTarget = new RenderTargetHandle();
cameraTarget.Init("_CameraColorAttachmentA");
UberPostProcessPass.Setup(cameraTargetDescriptor, cameraTarget);
#endif
// the order of Enqueue matters if they have the same renderPassEvent
renderer.EnqueuePass(SetToonParamPass);
renderer.EnqueuePass(SphereShadowTestRTPass);
renderer.EnqueuePass(CharSelfShadowMapRTRenderPass);
renderer.EnqueuePass(ToonOutlinePass);
renderer.EnqueuePass(ToonOutlinePass_RightAfterTransparent);
renderer.EnqueuePass(ScreenSpaceOutlinePass);
renderer.EnqueuePass(ExtraThickOutlinePass);
renderer.EnqueuePass(AnimePostProcessPass);
if (settings.MiscSettings.EnableSkyboxDrawBeforeOpaque && renderingData.cameraData.camera.clearFlags == CameraClearFlags.Skybox)
renderer.EnqueuePass(SkyboxRedrawBeforeOpaquePass);
//--------------------------------------------------------------------------
// skip prepass buffer when all condition meet:
// - no NiloToonBloom
// - no NiloToonTonemapping
// - no character is using [Color Fill] feature
// - no force render
var tonemappingEffect = VolumeManager.instance.stack.GetComponent<NiloToonTonemappingVolume>();
var bloomEffect = VolumeManager.instance.stack.GetComponent<NiloToonBloomVolume>();
bool isAnyNiloPostEnabled = false;
isAnyNiloPostEnabled |= tonemappingEffect.IsActive();
isAnyNiloPostEnabled |= bloomEffect.IsActive() && settings.uberPostProcessSettings.allowRenderNiloToonBloom;
bool isAnyNiloToonPerCharacterScriptRequiresPrepass = false;
foreach (var characterRenderController in characterList)
{
if (!characterRenderController) continue;
if (characterRenderController.isActiveAndEnabled &&
characterRenderController.gameObject.activeInHierarchy &&
characterRenderController.shouldRenderCharacterAreaColorFill)
{
isAnyNiloToonPerCharacterScriptRequiresPrepass = true;
break;
}
}
bool shouldDrawPrepass = renderingData.cameraData.postProcessEnabled && isAnyNiloPostEnabled;
shouldDrawPrepass |= isAnyNiloToonPerCharacterScriptRequiresPrepass;
shouldDrawPrepass |= settings.forceRenderPrepassBuffer;
if (shouldDrawPrepass)
{
renderer.EnqueuePass(PrepassBufferRTPass);
}
//--------------------------------------------------------------------------
renderer.EnqueuePass(UberPostProcessPass);
#if UNITY_2022_3_OR_NEWER
if (renderingData.cameraData.postProcessEnabled &&
renderingData.cameraData.cameraType == CameraType.Game) // only apply to game window
{
renderer.EnqueuePass(MotionBlurPass);
}
#endif
}
#if !UNITY_6000_4_OR_NEWER // TODO: it seems not correct to remove UberPostProcessPass.Setup(...) in Unity6.4?
#if UNITY_2022_2_OR_NEWER
// If you want to pass cameraColorTargetHandle or cameraDepthTargetHandle RTHandle into a pass
// You must do it here instead of AddRenderPasses(...), since these RTHandle are not yet init in AddRenderPasses(...)!
// see https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@14.0/manual/upgrade-guide-2022-2.html
public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
{
UberPostProcessPass.Setup(renderingData.cameraData.cameraTargetDescriptor);
}
#endif
#endif
#if UNITY_2022_2_OR_NEWER
// The method can be useful for releasing any resources that have been allocated.
// In editor, the method is called when removing features, recompiling scripts, entering/exiting play mode.
// (Not too sure when it gets called in builds, probably when changing scenes?)
// You should call Dispose() of every pass here to avoid memory leak
// Usually each pass's Dispose() will call:
// - RTHandle?.Release();
// - Destroy materials/textures created
protected override void Dispose(bool disposing)
{
// call Dispose to passes that alloc RT
PrepassBufferRTPass?.Dispose();
CharSelfShadowMapRTRenderPass?.Dispose();
UberPostProcessPass?.Dispose();
SphereShadowTestRTPass?.Dispose();
MotionBlurPass?.Dispose();
// null all passes
SetToonParamPass = null;
SphereShadowTestRTPass = null;
CharSelfShadowMapRTRenderPass = null;
SkyboxRedrawBeforeOpaquePass = null;
ToonOutlinePass = null;
ToonOutlinePass_RightAfterTransparent = null;
ScreenSpaceOutlinePass = null;
ExtraThickOutlinePass = null;
AnimePostProcessPass = null;
PrepassBufferRTPass = null;
UberPostProcessPass = null;
MotionBlurPass = null;
}
#endif
#region [Singleton to store list of active char (no matter visible by camera or not)]
public NiloToonAllInOneRendererFeature()
{
CheckInit();
}
// TODO: should we enable this?
//[Obsolete("We may remove Singleton(.Instance) API from all NiloToon ScriptableRendererFeature/ScriptableRenderPass in the future, since multiple Universal Renderer(s) can be added to 1 URP asset, Singleton is not possible.", false)]
public static NiloToonAllInOneRendererFeature Instance
{
get => _instance;
}
static NiloToonAllInOneRendererFeature _instance;
public static List<NiloToonPerCharacterRenderController> characterList;
public static HashSet<NiloToonPerCharacterRenderController> characterHashSet;
public static void AddCharIfNotExist(NiloToonPerCharacterRenderController controller)
{
CheckInit();
// optimize .Contains() call, now use HashSet instead of List: https://stackoverflow.com/questions/823860/listt-contains-is-very-slow
if (!characterHashSet.Contains(controller))
{
characterHashSet.Add(controller);
characterList.Add(controller);
UpdateCharacterControllerIndex();
}
}
public static void Remove(NiloToonPerCharacterRenderController controller)
{
CheckInit();
// optimize .Contains() call, now use HashSet instead of List: https://stackoverflow.com/questions/823860/listt-contains-is-very-slow
if (characterHashSet.Contains(controller))
{
characterHashSet.Remove(controller);
characterList.Remove(controller);
UpdateCharacterControllerIndex();
}
}
static void UpdateCharacterControllerIndex()
{
for (int i = 0; i < characterList.Count; i++)
{
var c = characterList[i];
if (!c)
continue;
c.characterID = i;
}
}
internal static void CheckInit()
{
if (characterList == null)
characterList = new List<NiloToonPerCharacterRenderController>(); // for UpdateCharacterControllerIndex()
if (characterHashSet == null)
characterHashSet = new HashSet<NiloToonPerCharacterRenderController>(); // for .Contains() checks
}
#endregion
}
}