409 lines
14 KiB
C#
Raw Normal View History

2025-09-11 20:42:09 +08:00
using System.Collections.Generic;
using Unity.Collections;
using Unity.Mathematics;
2025-09-11 11:04:02 +08:00
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
2025-09-11 20:42:09 +08:00
namespace X.Rendering.Scene
2025-09-11 11:04:02 +08:00
{
2025-09-11 20:42:09 +08:00
[ExecuteAlways]
[DefaultExecutionOrder(100)]
public class SceneEffect : MonoBehaviour
2025-09-11 11:04:02 +08:00
{
2025-09-11 20:42:09 +08:00
const string MultiCapsuleShadowAndAO = "_MultiCapsule_Shadow_AO_ON";
const string MultiCapsuleAO = "_MultiCapsule_AO_ON";
[SerializeField]
private ShadowEdgeRemapSetupPass.ShadowEdgeRemapSettings shadowRemapSettings = new();
ShadowEdgeRemapSetupPass shadowRemapPass;
2025-09-11 11:04:02 +08:00
2025-09-11 20:42:09 +08:00
[SerializeField]
private CapsuleShadowPass.CapsuleShadowSettings capsuleAOSettings = new();
CapsuleShadowPass capsuleAOPass;
2025-09-11 11:04:02 +08:00
2025-09-11 20:42:09 +08:00
[SerializeField]
public bool EnableShadowOffset;
[SerializeField]
public Vector3 CascadeShadowOffset;
2025-09-11 11:04:02 +08:00
2025-09-11 20:42:09 +08:00
public int updateInterval = 1;
static SceneEffect instance;
public static SceneEffect Instance
2025-09-11 11:04:02 +08:00
{
2025-09-11 20:42:09 +08:00
get
{
if (!instance)
{
instance = GameObject.FindObjectOfType<SceneEffect>();
}
return instance;
}
private set
{
instance = value;
}
2025-09-11 11:04:02 +08:00
}
2025-09-11 20:42:09 +08:00
private bool active = false;
private void Awake()
{
Instance = this;
2025-09-11 11:04:02 +08:00
}
2025-09-11 20:42:09 +08:00
private void OnEnable()
{
SetEnable(true);
}
2025-09-11 11:04:02 +08:00
2025-09-11 20:42:09 +08:00
private void OnDisable()
{
SetEnable(false);
}
private void OnDestroy()
{
Shader.DisableKeyword(MultiCapsuleAO);
Shader.DisableKeyword(MultiCapsuleShadowAndAO);
shadowRemapPass?.Dispose();
capsuleAOPass?.Dispose();
Instance = null;
}
protected void SetEnable(bool enable)
{
active = enable;
if (enable)
{
shadowRemapPass ??= new(shadowRemapSettings, this);
capsuleAOPass ??= new(capsuleAOSettings, this);
RenderPipelineManager.beginCameraRendering += OnBeginCamera;
UpdateRenderAssetsSettings();
#if UNITY_EDITOR
UpdateCapsuleLights();
#endif
ApplySerializeData();
}
else
{
RenderPipelineManager.beginCameraRendering -= OnBeginCamera;
}
}
#if UNITY_EDITOR
internal void AddSceneAreaEffect(CapsuleShadowAreaEffect sceneAreaEffect)
{
sceneAreaEffects.Add(sceneAreaEffect);
UpdateCapsuleLights();
}
internal void RmSceneAreaEffect(CapsuleShadowAreaEffect sceneAreaEffect)
{
sceneAreaEffects.Remove(sceneAreaEffect);
UpdateCapsuleLights();
}
private void OnValidate()
{
UpdateRenderAssetsSettings();
UpdateCapsuleLights();
}
internal void AreaEffectValidate()
{
UpdateCapsuleLights();
}
private void UpdateCapsuleLights()
{
List<Vector4> lightDirs = new List<Vector4>();
CapsuleLightsDir = new Vector4[32];
capsuleAOAreaSettings.Clear();
for (int i = 0; i < capsuleLights?.Length; i++)
{
var light = capsuleLights[i];
lightDirs.Add(new Vector4()
{
x = light.x,
y = light.y,
z = light.z,
w = 1
});
}
capsuleAOSetting.lightStartID = 0;
capsuleAOSetting.lightEndID = lightDirs.Count;
capsuleAOAreaSettings.Add(capsuleAOSetting);
for (int i = 0; i < sceneAreaEffects.Count; i++)
{
var sceneAreaEffect = sceneAreaEffects[i];
ref var aoSetting = ref sceneAreaEffect.capsuleAOSetting;
aoSetting.lightStartID = lightDirs.Count;
for (int j = 0; j < sceneAreaEffect.capsuleLights.Length; j++)
{
var light = sceneAreaEffect.capsuleLights[j];
lightDirs.Add(new Vector4()
{
x = light.x,
y = light.y,
z = light.z,
w = 1
});
}
aoSetting.lightEndID = lightDirs.Count - 1;
capsuleAOAreaSettings.Add(aoSetting);
}
CapsuleLightsDirCount = lightDirs.Count;
for (int i = 0; i < CapsuleLightsDirCount; i++)
{
CapsuleLightsDir[i] = lightDirs[i];
}
if(CapsuleAOSettingArray.IsCreated)
{
CapsuleAOSettingArray.Dispose();
}
CapsuleAOSettingArray = new NativeArray<CapsuleShadowAreaSetting>(capsuleAOAreaSettings.Count, Allocator.Persistent);
CapsuleAOSettingArray.CopyFrom(capsuleAOAreaSettings.ToArray());
}
#endif
2025-09-11 11:04:02 +08:00
2025-09-11 20:42:09 +08:00
private void ApplySerializeData()
2025-09-11 11:04:02 +08:00
{
2025-09-11 20:42:09 +08:00
if (CapsuleAOSettingArray.IsCreated)
{
CapsuleAOSettingArray.Dispose();
}
CapsuleAOSettingArray = new NativeArray<CapsuleShadowAreaSetting>(capsuleAOAreaSettings.Count, Allocator.Persistent);
CapsuleAOSettingArray.CopyFrom(capsuleAOAreaSettings.ToArray());
Shader.EnableKeyword(MultiCapsuleAO);
if (CapsuleLightsDir.Length > 0)
{
Shader.EnableKeyword(MultiCapsuleShadowAndAO);
}
else
{
Shader.DisableKeyword(MultiCapsuleShadowAndAO);
}
2025-09-11 11:04:02 +08:00
}
2025-09-11 20:42:09 +08:00
private void UpdateRenderAssetsSettings()
2025-09-11 11:04:02 +08:00
{
2025-09-11 20:42:09 +08:00
var asset = (GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset);
asset.EnableShadowOffset = EnableShadowOffset;
asset.cascadeShadowOffset = CascadeShadowOffset;
2025-09-11 11:04:02 +08:00
}
2025-09-11 20:42:09 +08:00
private void OnBeginCamera(ScriptableRenderContext context, Camera cam)
2025-09-11 11:04:02 +08:00
{
2025-09-11 20:42:09 +08:00
if (cam.cameraType != CameraType.Game && cam.cameraType != CameraType.SceneView)
{
return;
}
var rdr = cam.GetUniversalAdditionalCameraData().scriptableRenderer;
if (shadowRemapSettings.Enable && shadowRemapSettings.ShadowRemapTex)
{
rdr.EnqueuePass(shadowRemapPass);
}
if (capsuleAOSettings.Enable)
{
rdr.EnqueuePass(capsuleAOPass);
}
}
private void Update()
{
if (active && Time.frameCount % updateInterval == 0)
{
UpdateCapsuleShadow();
}
if(a && b)
{
//CapsuleLightsDir[0] = new float4(/*(a.position - b.position).normalized*/a.rotation.eulerAngles, 1);
//Debug.Log(a.rotation.eulerAngles);
}
}
#region CapsuleAO
[SerializeField]
public Transform a;
[SerializeField]
public Transform b;
[SerializeField ]//, HideInInspector]
private Vector4[] capsuleLights;
[SerializeField]
private CapsuleShadowAreaSetting capsuleAOSetting = CapsuleShadowAreaSetting.GetDefault();
[HideInInspector]
public NativeArray<Character> CharacterArray;
[HideInInspector]
public NativeArray<Capsule> CapsuleArray;
[HideInInspector, SerializeField]
public Vector4[] CapsuleLightsDir = new Vector4[32];
[HideInInspector, SerializeField]
public int CapsuleLightsDirCount = 0;
[HideInInspector, SerializeField]
List<CapsuleShadowAreaSetting> capsuleAOAreaSettings = new();
[HideInInspector]
public NativeArray<CapsuleShadowAreaSetting> CapsuleAOSettingArray;
[SerializeField]
private List<CapsuleShadowAreaEffect> sceneAreaEffects = new List<CapsuleShadowAreaEffect>(2);
private List<SceneEffectCharacter> sceneEffectCharacters = new();
private Dictionary<SceneEffectCharacter, CapsuleCollider[]> character2CapsuleCollider = new();
private int capsuleColliderCount = 0;
public void AddSceneEffectCharacter(SceneEffectCharacter character)
{
sceneEffectCharacters.Add(character);
AddCapsuleAOCharacter(character);
}
public void RmSceneEffectCharacter(SceneEffectCharacter character)
{
sceneEffectCharacters.Remove(character);
RemoveCapsuleAOCharacter(character);
}
private void AddCapsuleAOCharacter(SceneEffectCharacter character)
{
if (character2CapsuleCollider.ContainsKey(character))
{
return;
}
var oldCapsuleArray = CapsuleArray;
var oldCharacterArray = CharacterArray;
var capsules = character.GetComponentsInChildren<CapsuleCollider>(true);
character2CapsuleCollider.Add(character, capsules);
capsuleColliderCount += capsules.Length;
CapsuleArray = new(capsuleColliderCount, Allocator.Persistent);
CharacterArray = new(character2CapsuleCollider.Count, Allocator.Persistent);
if (oldCharacterArray.IsCreated)
{
oldCharacterArray.Dispose();
oldCapsuleArray.Dispose();
}
}
private void RemoveCapsuleAOCharacter(SceneEffectCharacter character)
{
if(!character2CapsuleCollider.ContainsKey(character))
{
return;
}
capsuleColliderCount -= character2CapsuleCollider[character].Length;
character2CapsuleCollider.Remove(character);
var oldCapsuleArray = CapsuleArray;
var oldCharacterArray = CharacterArray;
CapsuleArray = new(capsuleColliderCount, Allocator.Persistent);
CharacterArray = new(character2CapsuleCollider.Count, Allocator.Persistent);
if (oldCharacterArray.IsCreated)
{
oldCharacterArray.Dispose();
oldCapsuleArray.Dispose();
}
}
private void UpdateCapsuleShadow()
{
if (!CapsuleArray.IsCreated || CapsuleArray.Length == 0)
{
capsuleAOSettings.Enable = false;
return;
}
SceneEffectCharacter sceneEffectCharacter = null;
for (int j = 0; j < sceneEffectCharacters.Count; j++)
{
sceneEffectCharacter = sceneEffectCharacters[j];
sceneEffectCharacter.capsuleAOSettingMask = 1;
for (int i = 0; i < sceneAreaEffects.Count; i++)
{
var sceneAreaEffect = sceneAreaEffects[i];
if (sceneAreaEffect.Bounds.Contains(sceneEffectCharacter.transform.position))
{
sceneEffectCharacter.capsuleAOSettingMask |= (uint)1 << i;
}
}
}
int capsuleArrayIndex = 0;
int characterArrayIndex = 0;
int startID = 0;
sceneEffectCharacter = null;
foreach (var item in character2CapsuleCollider)
{
for (int i = 0; i < item.Value.Length; i++)
{
var capsule = item.Value[i];
//capsule.direction
Vector3 dir = Vector3.zero;
switch (capsule.direction)
{
case 0:
dir = new Vector3(capsule.height / 2 - capsule.radius, 0);
break;
case 1:
dir = new Vector3(0, capsule.height / 2 - capsule.radius, 0);
break;
case 2:
dir = new Vector3(0, 0, capsule.height / 2 - capsule.radius);
break;
}
var a = capsule.center - dir;
var b = capsule.center + dir;
//var a = capsule.center - new Vector3(0, capsule.height / 2, 0);
//var b = capsule.center + new Vector3(0, capsule.height / 2, 0);
a = capsule.transform.TransformPoint(a);
b = capsule.transform.TransformPoint(b);
CapsuleArray[capsuleArrayIndex++] = new()
{
a = a,
b = b,
radius = capsule.radius,
};
}
if (sceneEffectCharacter != item.Key)
{
sceneEffectCharacter = item.Key;
CharacterArray[characterArrayIndex++] = new()
{
position = sceneEffectCharacter.transform.position,
radius = sceneEffectCharacter.transform.lossyScale.x,
capsuleAOSettingMask = sceneEffectCharacter.capsuleAOSettingMask,
capsuleStartID = startID,
capsuleEndID = capsuleArrayIndex,
};
startID = capsuleArrayIndex;
}
}
2025-09-11 11:04:02 +08:00
}
2025-09-11 20:42:09 +08:00
#endregion
2025-09-11 11:04:02 +08:00
}
}