312 lines
9.0 KiB
C#
312 lines
9.0 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
namespace BadDog.Rendering.AreaLight
|
|
{
|
|
/// <summary>
|
|
/// Attach this component to a Light to enable area light calculations.
|
|
/// </summary>
|
|
[ExecuteAlways]
|
|
[RequireComponent(typeof(Light))]
|
|
[DisallowMultipleComponent]
|
|
[AddComponentMenu("BadDog/Rendering/BGAreaLight")]
|
|
public class BGAreaLight : MonoBehaviour
|
|
{
|
|
private Light m_Light;
|
|
|
|
[SerializeField]
|
|
[Tooltip("Enable shadow casting for this area light.")]
|
|
private bool m_CastShadows = true;
|
|
|
|
[SerializeField]
|
|
[Tooltip("Enable custom shadow settings for this area light.")]
|
|
private bool m_UseCustomShadow = false;
|
|
[SerializeField]
|
|
[Range(0, 8192)]
|
|
[Tooltip("Custom shadow map resolution for this area light. Set to 0 to use pipeline defaults.")]
|
|
private int m_CustomShadowResolution = 0;
|
|
[SerializeField]
|
|
[Range(AreaShadowUtils.k_MinAreaLightShadowCone, AreaShadowUtils.k_MaxAreaLightShadowCone)]
|
|
[Tooltip("Aperture of the cone used for shadowing the area light.")]
|
|
private float m_ShadowCone = AreaShadowUtils.k_DefaultAreaLightShadowCone;
|
|
[SerializeField]
|
|
[Range(0f, 1f)]
|
|
[Tooltip("Shadow strength for this area light. Applied to Light.shadowStrength.")]
|
|
private float m_ShadowStrength = 1f;
|
|
[SerializeField]
|
|
[Range(0f, 10f)]
|
|
[Tooltip("Normal bias used for area light shadowing.")]
|
|
private float m_ShadowNormalBias = 0.05f;
|
|
[SerializeField]
|
|
[Range(0f, 10f)]
|
|
[Tooltip("Depth bias used for area light shadowing. Acts like Light.shadowBias for rectangle lights.")]
|
|
private float m_ShadowDepthBias = 0.05f;
|
|
[SerializeField]
|
|
[Range(AreaShadowUtils.k_MinShadowNearPlane, AreaShadowUtils.k_MaxShadowNearPlane)]
|
|
[Tooltip("Shadow near plane for area light. Controls the starting distance of the shadow frustum.")]
|
|
private float m_ShadowNearPlane = 0.1f;
|
|
|
|
[SerializeField]
|
|
[Tooltip("Rendering Layer Mask used for area light lighting/shadow filtering.")]
|
|
private RenderingLayerMask m_RenderingLayerMask = (RenderingLayerMask)1;
|
|
|
|
[SerializeField, HideInInspector]
|
|
private bool m_PreBakeEnabled;
|
|
|
|
[SerializeField, HideInInspector]
|
|
private Vector2 m_CachedAreaSize = new Vector2(1.0f, 0.5f);
|
|
|
|
private void Awake()
|
|
{
|
|
OnValidate();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
OnValidate();
|
|
BGAreaLightManager.RegisterAreaLight(this);
|
|
|
|
#if UNITY_EDITOR
|
|
UnityEditor.Lightmapping.bakeStarted += OnBakeStarted;
|
|
UnityEditor.Lightmapping.bakeCompleted += OnBakeCompleted;
|
|
#endif
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
BGAreaLightManager.UnRegisterAreaLight(this);
|
|
|
|
#if UNITY_EDITOR
|
|
UnityEditor.Lightmapping.bakeStarted -= OnBakeStarted;
|
|
UnityEditor.Lightmapping.bakeCompleted -= OnBakeCompleted;
|
|
#endif
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
BGAreaLightManager.UnRegisterAreaLight(this);
|
|
}
|
|
|
|
private void OnValidate()
|
|
{
|
|
EnsureLight();
|
|
EnsureRectangleType();
|
|
ForceRealtimeMode();
|
|
SyncCastShadowsToLight();
|
|
SetShadowCone(m_ShadowCone);
|
|
SetShadowDepthBias(m_ShadowDepthBias);
|
|
SetShadowNormalBias(m_ShadowNormalBias);
|
|
SetShadowNearPlane(m_ShadowNearPlane);
|
|
ApplyShadowStrength();
|
|
SyncRenderingLayerMaskToLight();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
OnValidate();
|
|
}
|
|
|
|
private void ForceRealtimeMode()
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (m_Light.lightmapBakeType != LightmapBakeType.Realtime)
|
|
{
|
|
m_Light.lightmapBakeType = LightmapBakeType.Realtime;
|
|
UnityEditor.EditorUtility.SetDirty(m_Light);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private void EnsureLight()
|
|
{
|
|
if (m_Light == null)
|
|
{
|
|
m_Light = GetComponent<Light>();
|
|
}
|
|
}
|
|
|
|
private void EnsureRectangleType()
|
|
{
|
|
if (m_Light.type != LightType.Rectangle)
|
|
{
|
|
m_Light.type = LightType.Rectangle;
|
|
#if UNITY_EDITOR
|
|
UnityEditor.EditorUtility.SetDirty(m_Light);
|
|
#endif
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
if (m_Light.areaSize == Vector2.zero)
|
|
{
|
|
m_Light.areaSize = new Vector2(1.0f, 0.5f);
|
|
UnityEditor.EditorUtility.SetDirty(m_Light);
|
|
}
|
|
#endif
|
|
|
|
// Keep the runtime cache in sync with the Light's current area size.
|
|
m_CachedAreaSize = m_Light.areaSize;
|
|
}
|
|
|
|
private void ApplyShadowStrength()
|
|
{
|
|
float clamped = Mathf.Clamp01(m_ShadowStrength);
|
|
m_Light.shadowStrength = clamped;
|
|
|
|
#if UNITY_EDITOR
|
|
UnityEditor.EditorUtility.SetDirty(m_Light);
|
|
#endif
|
|
}
|
|
|
|
private void SyncCastShadowsToLight()
|
|
{
|
|
if (m_Light == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LightShadows targetShadows = m_CastShadows ? LightShadows.Soft : LightShadows.None;
|
|
|
|
if (m_Light.shadows != targetShadows)
|
|
{
|
|
m_Light.shadows = targetShadows;
|
|
#if UNITY_EDITOR
|
|
UnityEditor.EditorUtility.SetDirty(m_Light);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private void SyncRenderingLayerMaskToLight()
|
|
{
|
|
if (m_Light == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_Light.renderingLayerMask != m_RenderingLayerMask)
|
|
{
|
|
m_Light.renderingLayerMask = m_RenderingLayerMask;
|
|
#if UNITY_EDITOR
|
|
UnityEditor.EditorUtility.SetDirty(m_Light);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public Light GetLight()
|
|
{
|
|
EnsureLight();
|
|
return m_Light;
|
|
}
|
|
|
|
public int GetCustomShadowResolution()
|
|
{
|
|
return m_CustomShadowResolution;
|
|
}
|
|
|
|
public Vector2 GetSize()
|
|
{
|
|
return m_CachedAreaSize;
|
|
}
|
|
|
|
public float GetShadowCone()
|
|
{
|
|
return Mathf.Clamp(m_ShadowCone, AreaShadowUtils.k_MinAreaLightShadowCone, AreaShadowUtils.k_MaxAreaLightShadowCone);
|
|
}
|
|
|
|
public float GetShadowDepthBias()
|
|
{
|
|
return Mathf.Max(0f, m_ShadowDepthBias);
|
|
}
|
|
|
|
public void SetShadowDepthBias(float value)
|
|
{
|
|
float clamped = Mathf.Max(0f, value);
|
|
m_ShadowDepthBias = clamped;
|
|
}
|
|
|
|
public float GetShadowNormalBias()
|
|
{
|
|
return Mathf.Max(0f, m_ShadowNormalBias);
|
|
}
|
|
|
|
public void SetShadowNormalBias(float value)
|
|
{
|
|
float clamped = Mathf.Max(0f, value);
|
|
m_ShadowNormalBias = clamped;
|
|
}
|
|
|
|
public float GetShadowNearPlane()
|
|
{
|
|
return Mathf.Clamp(m_ShadowNearPlane, AreaShadowUtils.k_MinShadowNearPlane, AreaShadowUtils.k_MaxShadowNearPlane);
|
|
}
|
|
|
|
public void SetShadowNearPlane(float value)
|
|
{
|
|
float clamped = Mathf.Clamp(value, AreaShadowUtils.k_MinShadowNearPlane, AreaShadowUtils.k_MaxShadowNearPlane);
|
|
m_ShadowNearPlane = clamped;
|
|
}
|
|
|
|
public void SetShadowCone(float value)
|
|
{
|
|
float clamped = Mathf.Clamp(value, AreaShadowUtils.k_MinAreaLightShadowCone, AreaShadowUtils.k_MaxAreaLightShadowCone);
|
|
m_ShadowCone = clamped;
|
|
}
|
|
|
|
public bool GetIsRectLight()
|
|
{
|
|
Vector2 size = GetSize();
|
|
return size.y > 0.001f;
|
|
}
|
|
|
|
public float GetRangeAttenuationScale()
|
|
{
|
|
if (m_Light == null)
|
|
{
|
|
return 1.0f;
|
|
}
|
|
|
|
float range = m_Light.range;
|
|
if (range <= 0f)
|
|
{
|
|
return 1.0f;
|
|
}
|
|
|
|
return 1.0f / (range * range);
|
|
}
|
|
|
|
public float GetRangeAttenuationBias()
|
|
{
|
|
return 1.0f;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnBakeStarted()
|
|
{
|
|
EnsureLight();
|
|
if (m_Light == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_PreBakeEnabled = m_Light.enabled;
|
|
m_Light.enabled = false;
|
|
}
|
|
|
|
private void OnBakeCompleted()
|
|
{
|
|
EnsureLight();
|
|
if (m_Light == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_Light.enabled = m_PreBakeEnabled;
|
|
m_Light.lightmapBakeType = LightmapBakeType.Realtime;
|
|
UnityEditor.EditorUtility.SetDirty(m_Light);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|