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
|
|
|
|
}
|
|
|
|
|
|
}
|