using System; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; #if UNITY_6000_0_OR_NEWER using UnityEngine.Rendering.RenderGraphModule; #endif namespace PotaToon { public class CharacterShadowPass : ScriptableRenderPass { /* ID */ private static readonly ShaderTagId k_ShaderTagId = new ShaderTagId("CharacterDepth"); /* Member Variables */ private RTHandle m_CharShadowRT; private ProfilingSampler m_ProfilingSampler; private PassData m_PassData; private static int[] s_TextureSize = new int[2] { 1, 1 }; private ToonCharacterShadow m_CBuffer; public CharacterShadowPass(string featureName) { m_PassData = new PassData(); renderPassEvent = RenderPassEvent.BeforeRenderingPrePasses; m_ProfilingSampler = new ProfilingSampler(featureName); } public void Dispose() { m_CharShadowRT?.Release(); #if UNITY_2021_3 ConstantBuffer.ReleaseAll(); #endif } public void Setup(in RenderingData renderingData, PotaToon volume) { m_CBuffer = new ToonCharacterShadow(); m_PassData.enabled = true; m_PassData.bias = new Vector3(volume.bias.value * PotaToon.k_BiasScale, volume.normalBias.value * 0.01f, 0f); var scale = (int)volume.textureScale.value; m_PassData.cascadeResolutionScale = 1.0f; m_PassData.cascadeMaxDistance = CharacterShadowUtils.GetCullingDistance(renderingData.cameraData.camera, volume.shadowCullingDistance.value); s_TextureSize[0] = 1024 * scale; s_TextureSize[1] = 1024 * scale; m_PassData.useBrightestLight = volume.mode.value == PotaToonMode.Concert; m_PassData.followLightLayer = volume.followLayerMask.value; m_PassData.maxToonBrightness = volume.maxToonBrightness.value; CharacterShadowUtils.shadowCamera.lightDirectionOffset = volume.mode.value == PotaToonMode.Normal ? volume.charShadowDirOffset.value : Vector3.zero; CharacterShadowUtils.shadowCamera.overrideLightDirection = volume.mode.value == PotaToonMode.Normal && volume.charShadowDirOffsetMode.value == LightOffsetMode.World; } private static void UpdateConstantBuffer_Internal(ref ToonCharacterShadow cb, PassData passData, in CharacterShadowUtils.BrightestLightData brightestLightData) { var shadowCamera = CharacterShadowUtils.shadowCamera; var textureScale = s_TextureSize[0] / 1024; float softShadowSamples = Mathf.Clamp((int)Mathf.Log(textureScale, 2) + 1, 2, 4); if (shadowCamera != null) { passData.projectM = shadowCamera.projectionMatrix; passData.viewM = shadowCamera.GetViewMatrix(); if (shadowCamera.distanceCameraToNearestRenderer > 0.5f) softShadowSamples = Mathf.Max(softShadowSamples - 1f, 2f); } float invShadowMapWidth = 1.0f / s_TextureSize[0]; float invShadowMapHeight = 1.0f / s_TextureSize[1]; float invHalfShadowMapWidth = 0.5f * invShadowMapWidth; float invHalfShadowMapHeight = 0.5f * invShadowMapHeight; cb._BrightestLightDirection = brightestLightData.lightDirection; cb._BrightestLightIndex = (uint)brightestLightData.lightIndex; cb._CharShadowParams = new Vector4(passData.bias.x, passData.bias.y, 0, 0); cb._CharShadowViewProjM = passData.projectM * passData.viewM; cb._CharShadowOffset0 = new Vector4(-invHalfShadowMapWidth, -invHalfShadowMapHeight, invHalfShadowMapWidth, -invHalfShadowMapHeight); cb._CharShadowOffset1 = new Vector4(-invHalfShadowMapWidth, invHalfShadowMapHeight, invHalfShadowMapWidth, invHalfShadowMapHeight); cb._CharShadowmapSize = new Vector4(invShadowMapWidth, invShadowMapHeight, s_TextureSize[0], s_TextureSize[1]); invShadowMapWidth = 1.0f / TransparentShadowPass.s_TextureSize[0]; invShadowMapHeight = 1.0f / TransparentShadowPass.s_TextureSize[1]; cb._CharTransparentShadowmapSize = new Vector4(invShadowMapWidth, invShadowMapHeight, TransparentShadowPass.s_TextureSize[0], TransparentShadowPass.s_TextureSize[1]); var rcpMaxBoundSize = 1.0f / CharacterShadowUtils.shadowCamera.maxBoundSize; cb._CharShadowCascadeParams = new Vector4(passData.cascadeMaxDistance, passData.cascadeResolutionScale, softShadowSamples, rcpMaxBoundSize); cb._UseBrightestLight = passData.useBrightestLight ? 1u : 0u; cb._IsBrightestLightMain = brightestLightData.isMainLight ? 1u : 0u; cb._MaxToonBrightness = passData.maxToonBrightness; } private static void UpdateConstantBuffer(ref ToonCharacterShadow cb, PassData passData, ref RenderingData renderingData) { CharacterShadowUtils.GetBrightestLightData(ref renderingData, passData.useBrightestLight, passData.followLightLayer, out var brightestLightData); UpdateConstantBuffer_Internal(ref cb, passData, brightestLightData); } private RenderTextureDescriptor GetRenderTextureDescriptor() { var descriptor = new RenderTextureDescriptor(s_TextureSize[0], s_TextureSize[1], RenderTextureFormat.RG32, 0); descriptor.dimension = TextureDimension.Tex2D; descriptor.sRGB = false; descriptor.depthStencilFormat = GraphicsFormat.None; descriptor.msaaSamples = 1; return descriptor; } private class PassData { public bool enabled; public Matrix4x4 viewM; public Matrix4x4 projectM; public float maxToonBrightness; public Vector3 bias; public bool useBrightestLight; public LayerMask followLightLayer; public float cascadeMaxDistance; public float cascadeResolutionScale; #if UNITY_6000_0_OR_NEWER // RenderGraph Path public RendererListHandle rendererList; public ToonCharacterShadow cb; #endif } #if UNITY_6000_0_OR_NEWER [Obsolete] #endif public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { // Allocate Char Shadowmap RenderingUtils.ReAllocateIfNeeded(ref m_CharShadowRT, GetRenderTextureDescriptor(), FilterMode.Bilinear, name:"CharShadowMap"); cmd.SetGlobalTexture(ShaderIDs._CharShadowMap, m_CharShadowRT); } #if !UNITY_2021_3 private static void ExecutePass(CommandBuffer cmd, RendererList rendererList) { cmd.DrawRendererList(rendererList); } #endif #if UNITY_6000_0_OR_NEWER [Obsolete] #endif public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { var cmd = CommandBufferPool.Get(); using (new ProfilingScope(cmd, m_ProfilingSampler)) { var filteringSettings = new FilteringSettings(RenderQueueRange.opaque); var drawSettings = RenderingUtils.CreateDrawingSettings(k_ShaderTagId, ref renderingData, SortingCriteria.CommonOpaque); UpdateConstantBuffer(ref m_CBuffer, m_PassData, ref renderingData); ConstantBuffer.PushGlobal(cmd, m_CBuffer, ShaderIDs._ToonCharacterShadow); CoreUtils.SetRenderTarget(cmd, m_CharShadowRT, ClearFlag.Color, 0, CubemapFace.Unknown, 0); context.ExecuteCommandBuffer(cmd); cmd.Clear(); #if UNITY_2021_3 var rendererListDesc = new UnityEngine.Rendering.RendererUtils.RendererListDesc(k_ShaderTagId, renderingData.cullResults, renderingData.cameraData.camera); rendererListDesc.sortingCriteria = SortingCriteria.CommonOpaque; rendererListDesc.renderQueueRange = RenderQueueRange.opaque; cmd.DrawRendererList(context.CreateRendererList(rendererListDesc)); #else var param = new RendererListParams(renderingData.cullResults, drawSettings, filteringSettings); ExecutePass(cmd, context.CreateRendererList(ref param)); #endif } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } #if UNITY_6000_0_OR_NEWER #region RenderGraph private static void UpdateConstantBuffer(ref ToonCharacterShadow cb, PassData passData, UniversalLightData lightData) { CharacterShadowUtils.GetBrightestLightData(lightData, passData.useBrightestLight, passData.followLightLayer, out var brightestLightData); UpdateConstantBuffer_Internal(ref cb, passData, brightestLightData); } private static void ExecutePass(RasterCommandBuffer cmd, RendererList rendererList) { cmd.DrawRendererList(rendererList); } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { var renderingData = frameData.Get(); var lightData = frameData.Get(); var cameraData = frameData.Get(); var drawSettings = RenderingUtils.CreateDrawingSettings(k_ShaderTagId, renderingData, cameraData, lightData, SortingCriteria.CommonOpaque); TextureHandle output = UniversalRenderer.CreateRenderGraphTexture(renderGraph, GetRenderTextureDescriptor(), "CharShadowMap", true, FilterMode.Bilinear); using (var builder = renderGraph.AddRasterRenderPass("[PotaToon] Character Shadow", out var passData, m_ProfilingSampler)) { builder.AllowGlobalStateModification(true); builder.SetRenderAttachment(output, 0); var param = new RendererListParams(renderingData.cullResults, drawSettings, new FilteringSettings(RenderQueueRange.opaque)); passData.rendererList = renderGraph.CreateRendererList(param); builder.UseRendererList(passData.rendererList); if (output.IsValid()) builder.SetGlobalTextureAfterPass(output, ShaderIDs._CharShadowMap); passData.viewM = m_PassData.viewM; passData.projectM = m_PassData.projectM; passData.bias = m_PassData.bias; passData.useBrightestLight = m_PassData.useBrightestLight; passData.followLightLayer = m_PassData.followLightLayer; passData.enabled = m_PassData.enabled; passData.cascadeMaxDistance = m_PassData.cascadeMaxDistance; passData.cascadeResolutionScale = m_PassData.cascadeResolutionScale; passData.maxToonBrightness = m_PassData.maxToonBrightness; passData.cb = new ToonCharacterShadow(); UpdateConstantBuffer(ref passData.cb, passData, lightData); builder.SetRenderFunc((PassData data, RasterGraphContext context) => { ConstantBuffer.PushGlobal(data.cb, ShaderIDs._ToonCharacterShadow); ExecutePass(context.cmd, data.rendererList); }); } } #endregion #endif } }