using System; using UnityEditor; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; using UnityEngine.Rendering.RendererUtils; namespace Misaki.HdrpToon { [HideInInspector] internal class UTSPass : CustomPass { private const int Adjustment_Curve_Precision = 128; private const string Compensation_Prop_Name = "_ToonEvAdjustmentCompensation"; private const string Exposure_Adjustment_Prop_Name = "_ToonEvAdjustmentCurve"; private const string Exposure_Array_Prop_Name = "_ToonEvAdjustmentValueArray"; private const string Exposure_Min_Prop_Name = "_ToonEvAdjustmentValueMin"; private const string Exposure_Max_Prop_Name = "_ToonEvAdjustmentValueMax"; private const string Toon_Light_Filter_Prop_Name = "_ToonLightHiCutFilter"; private const string Ignore_Volume_Exposure_Prop_Name = "_ToonIgnoreExposureMultiplier"; private const string Hair_Shadow_RTHandle_Scale_Prop_Name = "_HairShadowRTHandleScale"; private const string Hair_Shadow_Distance_Prop_Name = "_HairShadowDistance"; private const string Hair_Shadow_Distance_Scale_Prop_Name = "_HairShadowDistanceScaleFactor"; private const string Hair_Shadow_Depth_Bias_Prop_Name = "_HairShadowDepthBias"; private const string Hair_Shadow_FadeIn_Prop_Name = "_HairShadowFadeInDistance"; private const string Hair_Shadow_Fade_Out_Prop_Name = "_HairShadowFadeOutDistance"; private const string Hair_Blending_RTHandle_Scale_Prop_Name = "_HairBlendingRTHandleScale"; private const string Output_RT_Prop_Name = "_HairShadowTex"; private const string Hair_Blending_Prop_Name = "_HairBlendingTex"; private float _max; private float _min; private float[] _exposureArray; private RTHandle _hairShadowRTHandle; private bool _needReallocateHairShadow; private RTHandle _hairBlendingRTHandle; private bool _needReallocateHairBlending; private bool _enableHairShadow; public bool EnableHairShadow { get => _enableHairShadow; set { if (_enableHairShadow == value) { return; } _enableHairShadow = value; if (_enableHairShadow) { Shader.EnableKeyword("ENABLE_UTS_HAIR_SHAOW"); } else { Shader.DisableKeyword("ENABLE_UTS_HAIR_SHAOW"); _hairShadowRTHandle?.Release(); } } } private bool _enableHairBlending; public bool EnableHairBlending { get => _enableHairBlending; set { if (_enableHairBlending == value) { return; } _enableHairBlending = value; if (_enableHairBlending) { Shader.EnableKeyword("ENABLE_UTS_HAIR_BLENDING"); } else { Shader.DisableKeyword("ENABLE_UTS_HAIR_BLENDING"); _hairBlendingRTHandle?.Release(); } } } private BufferQuality _hairShadowQuality = BufferQuality.High; internal BufferQuality HairShadowQuality { get => _hairShadowQuality; set { if (_hairShadowQuality == value) { return; } _hairShadowQuality = value; _needReallocateHairShadow = true; } } private BufferQuality _hairBlendingQuality = BufferQuality.High; internal BufferQuality HairBlendingQuality { get => _hairBlendingQuality; set { if (_hairBlendingQuality == value) { return; } _hairBlendingQuality = value; _needReallocateHairBlending = true; } } private bool ShouldReallocateHairShadowBuffer() { return _hairShadowRTHandle == null || _hairShadowRTHandle.rt == null || !_hairShadowRTHandle.rt.IsCreated() || _needReallocateHairShadow; } private void ReallocateHairShadowBuffer() { #if UNITY_EDITOR if (EditorApplication.isCompiling) { return; } #endif var scale = _hairShadowQuality switch { BufferQuality.Low => new Vector2(0.5f, 0.5f), BufferQuality.High => Vector2.one, _ => Vector2.zero }; var format = _hairShadowQuality switch { BufferQuality.Low => GraphicsFormat.D16_UNorm, BufferQuality.High => GraphicsFormat.D32_SFloat, _ => GraphicsFormat.D16_UNorm }; _hairShadowRTHandle?.Release(); _hairShadowRTHandle = RTHandles.Alloc(scale, colorFormat: format, isShadowMap: true, useDynamicScale: true, name: Output_RT_Prop_Name); Shader.SetGlobalTexture(Output_RT_Prop_Name, _hairShadowRTHandle); _needReallocateHairShadow = false; } private bool ShouldReallocateHairBlendingBuffer() { return _hairBlendingRTHandle == null || _hairBlendingRTHandle.rt == null || !_hairBlendingRTHandle.rt.IsCreated() || _needReallocateHairBlending; } private void ReallocateHairBlendingBuffer() { #if UNITY_EDITOR if (EditorApplication.isCompiling) { return; } #endif var format = _hairBlendingQuality switch { BufferQuality.Low => GraphicsFormat.R8G8B8A8_SNorm, BufferQuality.High => GraphicsFormat.R8G8B8A8_SRGB, _ => GraphicsFormat.R8G8B8A8_SRGB }; _hairBlendingRTHandle?.Release(); _hairBlendingRTHandle = RTHandles.Alloc(Vector2.one, colorFormat: format, useDynamicScale: true, name: Hair_Blending_Prop_Name); Shader.SetGlobalTexture(Hair_Blending_Prop_Name, _hairBlendingRTHandle); _needReallocateHairBlending = false; } protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { _exposureArray = new float[Adjustment_Curve_Precision]; ReallocateHairShadowBuffer(); ReallocateHairBlendingBuffer(); } protected override void Execute(CustomPassContext ctx) { var utsRenderer = ctx.hdCamera.volumeStack.GetComponent(); UpdateSceneEV(utsRenderer); RenderHairShadow(ref ctx, utsRenderer); RenderHairBlending(ref ctx); } private void RenderHairShadow(ref CustomPassContext ctx, UTSRenderer utsRenderer) { if (!_enableHairShadow) { return; } if (ShouldReallocateHairShadowBuffer()) { ReallocateHairShadowBuffer(); return; } CoreUtils.SetRenderTarget(ctx.cmd, _hairShadowRTHandle, ClearFlag.DepthStencil); var shouldRender = utsRenderer != null && utsRenderer.enableHairShadow.value && utsRenderer.state.value; if (!shouldRender) { return; } var result = new RendererListDesc(UtsShaderPassName.hairShadowCasterPassId, ctx.cullingResults, ctx.hdCamera.camera) { renderQueueRange = GetRenderQueueRange(RenderQueueType.All), sortingCriteria = SortingCriteria.CommonOpaque, excludeObjectMotionVectors = false, }; CoreUtils.DrawRendererList(ctx.renderContext, ctx.cmd, ctx.renderContext.CreateRendererList(result)); Shader.SetGlobalVector(Hair_Shadow_RTHandle_Scale_Prop_Name, _hairShadowRTHandle.rtHandleProperties.rtHandleScale); Shader.SetGlobalFloat(Hair_Shadow_Distance_Prop_Name, utsRenderer.shadowDistance.value); Shader.SetGlobalFloat(Hair_Shadow_Distance_Scale_Prop_Name, utsRenderer.shadowDistanceScale.value); Shader.SetGlobalFloat(Hair_Shadow_Depth_Bias_Prop_Name, utsRenderer.shadowDepthBias.value); Shader.SetGlobalFloat(Hair_Shadow_FadeIn_Prop_Name, utsRenderer.shadowFadeIn.value); Shader.SetGlobalFloat(Hair_Shadow_Fade_Out_Prop_Name, utsRenderer.shadowFadeOut.value); } private void RenderHairBlending(ref CustomPassContext ctx) { if (!_enableHairBlending) { return; } if (ShouldReallocateHairBlendingBuffer()) { ReallocateHairBlendingBuffer(); return; } CoreUtils.SetRenderTarget(ctx.cmd, _hairBlendingRTHandle, ctx.cameraDepthBuffer, ClearFlag.Color); var result = new RendererListDesc(UtsShaderPassName.hairBlendingTargetPassId, ctx.cullingResults, ctx.hdCamera.camera) { renderQueueRange = GetRenderQueueRange(RenderQueueType.All), sortingCriteria = SortingCriteria.CommonOpaque, excludeObjectMotionVectors = false, }; CoreUtils.DrawRendererList(ctx.renderContext, ctx.cmd, ctx.renderContext.CreateRendererList(result)); Shader.SetGlobalVector(Hair_Blending_RTHandle_Scale_Prop_Name, _hairBlendingRTHandle.rtHandleProperties.rtHandleScale); } private void UpdateSceneEV(UTSRenderer utsRenderer) { if (utsRenderer == null) { return; } var toonEVAdjustment = utsRenderer.toonEVAdjustment.value ? 1 : 0; var lightIntensityLimiter = utsRenderer.lightIntensityLimiter.value ? 1 : 0; var ignoreVolumeExposure = utsRenderer.ignoreVolumeExposure.value ? 1 : 0; var compensation = utsRenderer.compensation.value; if (!utsRenderer.state.value) { _min = 0; _max = 0; _exposureArray.AsSpan().Fill(0); toonEVAdjustment = 0; lightIntensityLimiter = 0; ignoreVolumeExposure = 0; compensation = 0; } else { // Fail safe in case the curve is deleted / has 0 point var curve = utsRenderer.adjustmentCurve.value; if (curve == null || curve.length == 0) { _min = 0f; _max = 0f; _exposureArray.AsSpan().Fill(0); } else { _min = curve[0].time; _max = curve[curve.length - 1].time; var step = (_max - _min) / (Adjustment_Curve_Precision - 1f); for (var i = 0; i < Adjustment_Curve_Precision; i++) { _exposureArray[i] = curve.Evaluate(_min + step * i); } } } Shader.SetGlobalFloatArray(Exposure_Array_Prop_Name, _exposureArray); Shader.SetGlobalFloat(Exposure_Min_Prop_Name, _min); Shader.SetGlobalFloat(Exposure_Max_Prop_Name, _max); Shader.SetGlobalInt(Exposure_Adjustment_Prop_Name, toonEVAdjustment); Shader.SetGlobalInt(Toon_Light_Filter_Prop_Name, lightIntensityLimiter); Shader.SetGlobalInt(Ignore_Volume_Exposure_Prop_Name, ignoreVolumeExposure); Shader.SetGlobalFloat(Compensation_Prop_Name, compensation); } protected override void Cleanup() { _exposureArray = null; _hairShadowRTHandle?.Release(); _hairBlendingRTHandle?.Release(); } } }