2025-09-19 18:13:23 +08:00

141 lines
5.2 KiB
C#

using System;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace X.Rendering.Scene
{
[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 capsuleStartID, capsuleEndID;
public uint capsuleAOSettingMask;
}
[StructLayout(LayoutKind.Sequential), Serializable]
public struct CapsuleShadowAreaSetting
{
public float AmbientIntensity;
//[Range(1, 90)]
public float ConeAngle;
public float ShadowIntensity;
public float ShadowSharpness;
public int lightStartID, lightEndID;
public static CapsuleShadowAreaSetting GetDefault()
{
return new CapsuleShadowAreaSetting()
{
AmbientIntensity = 0.2f,
ShadowIntensity = 0.4f,
ShadowSharpness = 20,
ConeAngle = 1
};
}
}
internal class CapsuleShadowPass : ScriptableRenderPass, IDisposable
{
[Serializable]
public class CapsuleShadowSettings
{
public bool Enable = true;
public RenderPassEvent RenderPassEvent = RenderPassEvent.BeforeRenderingTransparents;
}
private CapsuleShadowSettings settings;
private readonly SceneEffect sceneEffect;
private GraphicsBuffer capsuleDataBuffer;
private GraphicsBuffer characterDataBuffer;
private GraphicsBuffer capsuleShadowDataBuffer;
private ProfilingSampler profiler;
private CommandBuffer commandBuffer;
public CapsuleShadowPass(CapsuleShadowSettings settings, SceneEffect sceneEffect)
{
this.settings = settings;
this.sceneEffect = sceneEffect;
profiler = new(nameof(CapsuleShadowPass));
renderPassEvent = settings.RenderPassEvent;
commandBuffer = CommandBufferPool.Get(nameof(CapsuleShadowPass) + sceneEffect.name);
}
public void Dispose()
{
capsuleDataBuffer?.Dispose();
capsuleDataBuffer = null;
characterDataBuffer?.Dispose();
characterDataBuffer = null;
capsuleShadowDataBuffer?.Dispose();
capsuleShadowDataBuffer = null;
commandBuffer.Dispose();
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
ref var characterArray = ref sceneEffect.CharacterArray;
ref var capsuleArray = ref sceneEffect.CapsuleArray;
ref var capsuleAOSettingArray = ref sceneEffect.CapsuleAOSettingArray;
if (!capsuleArray.IsCreated || !capsuleAOSettingArray.IsCreated)
{
return;
}
if (capsuleDataBuffer == null || capsuleDataBuffer.count != capsuleArray.Length)
{
capsuleDataBuffer?.Release();
capsuleDataBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capsuleArray.Length, UnsafeUtility.SizeOf<Capsule>());
}
if (characterDataBuffer == null || characterDataBuffer.count != characterArray.Length)
{
characterDataBuffer?.Release();
characterDataBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, characterArray.Length, UnsafeUtility.SizeOf<Character>());
}
if (capsuleShadowDataBuffer == null || capsuleShadowDataBuffer.count != capsuleAOSettingArray.Length)
{
capsuleShadowDataBuffer?.Release();
capsuleShadowDataBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capsuleAOSettingArray.Length, UnsafeUtility.SizeOf<CapsuleShadowAreaSetting>());
}
capsuleDataBuffer.SetData(capsuleArray);
characterDataBuffer.SetData(characterArray);
var cmd = commandBuffer;
using var scp = new ProfilingScope(cmd, profiler);
cmd.SetGlobalBuffer("_CapsuleData", capsuleDataBuffer);
cmd.SetGlobalBuffer("_CharacterData", characterDataBuffer);
capsuleShadowDataBuffer.SetData(capsuleAOSettingArray);
cmd.SetGlobalBuffer("_CapsuleShadowData", capsuleShadowDataBuffer);
cmd.SetGlobalInt("_CapsuleShadowDataCount", capsuleAOSettingArray.Length);
cmd.SetGlobalInt("_CapsulesCount", capsuleArray.Length);
cmd.SetGlobalInt("_CharactersCount", characterArray.Length);
cmd.SetGlobalInt("_CapsuleLightsCount", sceneEffect.CapsuleLightsDirCount);
if(sceneEffect.CapsuleLightsDirCount > 0)
{
cmd.SetGlobalVectorArray("_CapsuleLightsDir", sceneEffect.CapsuleLightsDir);
}
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
}
}
}