338 lines
12 KiB
C#
338 lines
12 KiB
C#
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_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_SHADOW_RT_PROP_NAME = "_HairShadowTex";
|
|
private const string _HAIR_BLENDING_PROP_NAME = "_HairBlendingTex";
|
|
|
|
private static readonly ProfilingSampler _hairShadowProfilingSampler = new("UTS Hair Shadow");
|
|
private static readonly ProfilingSampler _hairBlendingProfilingSampler = new("UTS Hair Blending Target");
|
|
|
|
private float _max;
|
|
private float _min;
|
|
private float[] _exposureArray;
|
|
|
|
private RTHandle _hairShadowRTHandle;
|
|
private bool _needReallocateHairShadow;
|
|
|
|
// TODO: Possible to avoid another RTHandle for depth?
|
|
private RTHandle _hairBlendingRTHandle;
|
|
private RTHandle _hairBlendingDepthRTHandle;
|
|
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
|
|
|
|
// Use R8 or R16 directly?
|
|
var format = _hairShadowQuality switch
|
|
{
|
|
BufferQuality.Low => GraphicsFormat.D16_UNorm,
|
|
BufferQuality.High => GraphicsFormat.D32_SFloat,
|
|
_ => GraphicsFormat.R16G16B16A16_SFloat
|
|
};
|
|
|
|
_hairShadowRTHandle?.Release();
|
|
_hairShadowRTHandle = RTHandles.Alloc(Vector2.one, useDynamicScale: true, format: format, isShadowMap: true, name: _HAIR_SHADOW_RT_PROP_NAME);
|
|
Shader.SetGlobalTexture(_HAIR_SHADOW_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.A2B10G10R10_UNormPack32,
|
|
BufferQuality.High => GraphicsFormat.R16G16B16A16_UNorm,
|
|
_ => GraphicsFormat.A2B10G10R10_UNormPack32
|
|
};
|
|
|
|
_hairBlendingRTHandle?.Release();
|
|
_hairBlendingRTHandle = RTHandles.Alloc(Vector2.one, useDynamicScale: true, dimension: TextureXR.dimension, colorFormat: format, name: _HAIR_BLENDING_PROP_NAME);
|
|
|
|
if (_hairBlendingDepthRTHandle == null || _hairBlendingDepthRTHandle.rt == null || !_hairBlendingDepthRTHandle.rt.IsCreated())
|
|
{
|
|
_hairBlendingDepthRTHandle?.Release();
|
|
_hairBlendingDepthRTHandle = RTHandles.Alloc(Vector2.one, useDynamicScale: true, dimension: TextureXR.dimension, colorFormat: GraphicsFormat.D16_UNorm, name: _HAIR_BLENDING_PROP_NAME + "_depth");
|
|
}
|
|
|
|
Shader.SetGlobalTexture(_HAIR_BLENDING_PROP_NAME, _hairBlendingRTHandle);
|
|
|
|
_needReallocateHairBlending = false;
|
|
}
|
|
|
|
protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
|
|
{
|
|
_exposureArray = new float[_ADJUSTMENT_CURVE_PRECISION];
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private void RenderHairShadow(ref CustomPassContext ctx, UTSRenderer utsRenderer)
|
|
{
|
|
if (!_enableHairShadow
|
|
|| utsRenderer == null
|
|
|| !utsRenderer.state.value
|
|
|| !utsRenderer.enableHairShadow.value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ShouldReallocateHairShadowBuffer())
|
|
{
|
|
ReallocateHairShadowBuffer();
|
|
return;
|
|
}
|
|
|
|
using (new ProfilingScope(ctx.cmd, _hairShadowProfilingSampler))
|
|
{
|
|
CoreUtils.SetRenderTarget(ctx.cmd, _hairShadowRTHandle, ClearFlag.Depth);
|
|
|
|
var result = new RendererListDesc(UtsShaderPassName.hairShadowCasterPassId, ctx.cullingResults, ctx.hdCamera.camera)
|
|
{
|
|
renderQueueRange = GetRenderQueueRange(RenderQueueType.AllOpaque),
|
|
sortingCriteria = SortingCriteria.CommonOpaque,
|
|
excludeObjectMotionVectors = false,
|
|
};
|
|
|
|
CoreUtils.DrawRendererList(ctx.renderContext, ctx.cmd, ctx.renderContext.CreateRendererList(result));
|
|
|
|
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;
|
|
}
|
|
|
|
using (new ProfilingScope(ctx.cmd, _hairBlendingProfilingSampler))
|
|
{
|
|
CoreUtils.SetRenderTarget(ctx.cmd, _hairBlendingRTHandle, _hairBlendingDepthRTHandle, ClearFlag.Color | ClearFlag.Depth);
|
|
|
|
var result = new RendererListDesc(UtsShaderPassName.hairBlendingTargetPassId, ctx.cullingResults, ctx.hdCamera.camera)
|
|
{
|
|
renderQueueRange = GetRenderQueueRange(RenderQueueType.AllOpaque),
|
|
sortingCriteria = SortingCriteria.CommonOpaque,
|
|
excludeObjectMotionVectors = false,
|
|
};
|
|
|
|
CoreUtils.DrawRendererList(ctx.renderContext, ctx.cmd, ctx.renderContext.CreateRendererList(result));
|
|
}
|
|
}
|
|
|
|
protected override void Execute(CustomPassContext ctx)
|
|
{
|
|
var utsRenderer = ctx.hdCamera.volumeStack.GetComponent<UTSRenderer>();
|
|
|
|
UpdateSceneEV(utsRenderer);
|
|
RenderHairShadow(ref ctx, utsRenderer);
|
|
RenderHairBlending(ref ctx);
|
|
}
|
|
|
|
protected override void Cleanup()
|
|
{
|
|
_exposureArray = null;
|
|
_hairShadowRTHandle?.Release();
|
|
_hairBlendingRTHandle?.Release();
|
|
_hairBlendingDepthRTHandle?.Release();
|
|
}
|
|
}
|
|
}
|