using System; using System.Runtime.InteropServices; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; namespace X.Rendering.Feature { [StructLayout(LayoutKind.Sequential)] public struct Capsule { public Vector3 a, b; public float radius; } [StructLayout(LayoutKind.Sequential)] public struct Character { public Vector3 position; public float radius; public Vector4 lightDir; //xyz:lightDir, w:根据lightColor 算个系数 public int startID, endID; } public class CapsuleAO : ScriptableRendererFeature { [Serializable] public class Settings { public RenderPassEvent RenderPassEvent = RenderPassEvent.BeforeRenderingTransparents; public float AmbientIntensity = 0.2f; public float ShadowIntensity = 0.4f; public float ShadowSharpness = 20; [Range(1, 90)] public float ConeAngle = 1; public Material CapsuleAOMat; } [SerializeField] Settings settings; CapsuleAOPass capsuleAOPass; public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { { renderer.EnqueuePass(capsuleAOPass); } } public override void Create() { capsuleAOPass = new(settings); } class CapsuleAOPass : ScriptableRenderPass, IDisposable { private Settings settings; private GraphicsBuffer capsuleDataBuffer; private GraphicsBuffer characterDataBuffer; private ProfilingSampler profiler; public CapsuleAOPass(Settings settings) { this.settings = settings; profiler = new (nameof(CapsuleAOPass)); renderPassEvent = settings.RenderPassEvent; } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { base.Configure(cmd, cameraTextureDescriptor); ConfigureInput(ScriptableRenderPassInput.Depth | ScriptableRenderPassInput.Normal); } public void Dispose() { capsuleDataBuffer?.Dispose(); capsuleDataBuffer = null; characterDataBuffer?.Dispose(); characterDataBuffer = null; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if (!XRenderFeatureManager.Instance) { return; } ref var characterArray = ref XRenderFeatureManager.Instance.CharacterArray; ref var capsuleArray = ref XRenderFeatureManager.Instance.CapsuleArray; if (!capsuleArray.IsCreated) { return; } if(capsuleDataBuffer == null || capsuleDataBuffer.count != capsuleArray.Length) { capsuleDataBuffer?.Release(); capsuleDataBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capsuleArray.Length, UnsafeUtility.SizeOf()); } if (characterDataBuffer == null || characterDataBuffer.count != characterArray.Length) { characterDataBuffer?.Release(); characterDataBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, characterArray.Length, UnsafeUtility.SizeOf()); } capsuleDataBuffer.SetData(capsuleArray); characterDataBuffer.SetData(characterArray); var cmd = renderingData.commandBuffer; using var scp = new ProfilingScope(cmd, profiler); cmd.SetGlobalBuffer("_CapsuleData", capsuleDataBuffer); cmd.SetGlobalBuffer("_CharacterData", characterDataBuffer); cmd.SetGlobalInt("_CapsulesCount", capsuleArray.Length); cmd.SetGlobalInt("_CharactersCount", characterArray.Length); cmd.SetGlobalFloat("_AmbientIntensity", settings.AmbientIntensity); cmd.SetGlobalFloat("_ShadowIntensity", settings.ShadowIntensity); cmd.SetGlobalFloat("_ShadowSharpness", settings.ShadowSharpness); cmd.SetGlobalFloat("_ConeAngle", settings.ConeAngle); var renderer = renderingData.cameraData.renderer; cmd.SetRenderTarget(renderer.cameraColorTargetHandle, loadAction: RenderBufferLoadAction.Load, storeAction: RenderBufferStoreAction.Store); cmd.DrawProcedural(Matrix4x4.identity, settings.CapsuleAOMat, 0, MeshTopology.Triangles, 3); } } } }