Bez popisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Light2D.cs 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. using System;
  2. using UnityEngine.Serialization;
  3. using UnityEngine.Scripting.APIUpdating;
  4. using UnityEngine.U2D;
  5. using UnityEngine.Rendering.RenderGraphModule;
  6. #if UNITY_EDITOR
  7. using System.Linq;
  8. #endif
  9. namespace UnityEngine.Rendering.Universal
  10. {
  11. /// <summary>
  12. /// Class <c>Light2D</c> is a 2D light which can be used with the 2D Renderer.
  13. /// </summary>
  14. ///
  15. [ExecuteAlways, DisallowMultipleComponent]
  16. [MovedFrom(true, "UnityEngine.Experimental.Rendering.Universal", "Unity.RenderPipelines.Universal.Runtime")]
  17. [AddComponentMenu("Rendering/2D/Light 2D")]
  18. [HelpURL("https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/index.html?subfolder=/manual/2DLightProperties.html")]
  19. public sealed partial class Light2D : Light2DBase, ISerializationCallbackReceiver
  20. {
  21. /// <summary>
  22. /// Deprecated Light types that are no supported. Please migrate to either Freeform or Point lights.
  23. /// </summary>
  24. public enum DeprecatedLightType
  25. {
  26. /// <summary>
  27. /// N-gon shaped lights.
  28. /// </summary>
  29. Parametric = 0,
  30. }
  31. /// <summary>
  32. /// An enumeration of the types of light
  33. /// </summary>
  34. public enum LightType
  35. {
  36. /// <summary>
  37. /// N-gon shaped lights. Deprecated.
  38. /// </summary>
  39. Parametric = 0,
  40. /// <summary>
  41. /// The shape of the light is based on a user defined closed shape with multiple points.
  42. /// </summary>
  43. Freeform = 1,
  44. /// <summary>
  45. /// The shape of the light is based on a Sprite.
  46. /// </summary>
  47. Sprite = 2,
  48. /// <summary>
  49. /// The shape of light is circular and can also be configured into a pizza shape.
  50. /// </summary>
  51. Point = 3,
  52. /// <summary>
  53. /// Shapeless light that affects the entire screen.
  54. /// </summary>
  55. Global = 4
  56. }
  57. /// <summary>
  58. /// The accuracy of how the normal map calculation.
  59. /// </summary>
  60. public enum NormalMapQuality
  61. {
  62. /// <summary>
  63. /// Normal map not used.
  64. /// </summary>
  65. Disabled = 2,
  66. /// <summary>
  67. /// Faster calculation with less accuracy suited for small shapes on screen.
  68. /// </summary>
  69. Fast = 0,
  70. /// <summary>
  71. /// Accurate calculation useful for better output on bigger shapes on screen.
  72. /// </summary>
  73. Accurate = 1
  74. }
  75. /// <summary>
  76. /// Determines how the final color is calculated when multiple lights overlap each other
  77. /// </summary>
  78. public enum OverlapOperation
  79. {
  80. /// <summary>
  81. /// Colors are added together
  82. /// </summary>
  83. Additive,
  84. /// <summary>
  85. /// Colors are blended using standard blending (alpha, 1-alpha)
  86. /// </summary>
  87. AlphaBlend
  88. }
  89. private enum ComponentVersions
  90. {
  91. Version_Unserialized = 0,
  92. Version_1 = 1,
  93. Version_2 = 2
  94. }
  95. const ComponentVersions k_CurrentComponentVersion = ComponentVersions.Version_2;
  96. [SerializeField] ComponentVersions m_ComponentVersion = ComponentVersions.Version_Unserialized;
  97. #if USING_ANIMATION_MODULE
  98. [UnityEngine.Animations.NotKeyable]
  99. #endif
  100. [SerializeField] LightType m_LightType = LightType.Point;
  101. [SerializeField, FormerlySerializedAs("m_LightOperationIndex")]
  102. int m_BlendStyleIndex = 0;
  103. [SerializeField] float m_FalloffIntensity = 0.5f;
  104. [ColorUsage(true)]
  105. [SerializeField] Color m_Color = Color.white;
  106. [SerializeField] float m_Intensity = 1;
  107. [FormerlySerializedAs("m_LightVolumeOpacity")]
  108. [SerializeField] float m_LightVolumeIntensity = 1.0f;
  109. [FormerlySerializedAs("m_LightVolumeIntensityEnabled")]
  110. [SerializeField] bool m_LightVolumeEnabled = false;
  111. [SerializeField] int[] m_ApplyToSortingLayers; // These are sorting layer IDs. If we need to update this at runtime make sure we add code to update global lights
  112. [Reload("Textures/2D/Sparkle.png")]
  113. [SerializeField] Sprite m_LightCookieSprite;
  114. [FormerlySerializedAs("m_LightCookieSprite")]
  115. [SerializeField] Sprite m_DeprecatedPointLightCookieSprite;
  116. [SerializeField] int m_LightOrder = 0;
  117. [SerializeField] bool m_AlphaBlendOnOverlap = false; // This is now deprecated. Keep it here for backwards compatibility.
  118. [SerializeField] OverlapOperation m_OverlapOperation = OverlapOperation.Additive;
  119. [FormerlySerializedAs("m_PointLightDistance")]
  120. [SerializeField] float m_NormalMapDistance = 3.0f;
  121. #if USING_ANIMATION_MODULE
  122. [UnityEngine.Animations.NotKeyable]
  123. #endif
  124. [FormerlySerializedAs("m_PointLightQuality")]
  125. [SerializeField] NormalMapQuality m_NormalMapQuality = NormalMapQuality.Disabled;
  126. [SerializeField] bool m_UseNormalMap = false; // This is now deprecated. Keep it here for backwards compatibility.
  127. [FormerlySerializedAs("m_ShadowIntensityEnabled")]
  128. [SerializeField] bool m_ShadowsEnabled = true;
  129. [Range(0, 1)]
  130. [SerializeField] float m_ShadowIntensity = 0.75f;
  131. [Range(0, 1)]
  132. [SerializeField] float m_ShadowSoftness = 0.3f;
  133. [Range(0, 1)]
  134. [SerializeField] float m_ShadowSoftnessFalloffIntensity = 0.5f;
  135. [SerializeField] bool m_ShadowVolumeIntensityEnabled = false;
  136. [Range(0, 1)]
  137. [SerializeField] float m_ShadowVolumeIntensity = 0.75f;
  138. Mesh m_Mesh;
  139. [NonSerialized]
  140. private LightUtility.LightMeshVertex[] m_Vertices = new LightUtility.LightMeshVertex[1];
  141. [NonSerialized]
  142. private ushort[] m_Triangles = new ushort[1];
  143. internal LightUtility.LightMeshVertex[] vertices { get { return m_Vertices; } set { m_Vertices = value; } }
  144. internal ushort[] indices { get { return m_Triangles; } set { m_Triangles = value; } }
  145. // Transients
  146. int m_PreviousLightCookieSprite;
  147. internal Vector3 m_CachedPosition;
  148. // We use Blue Channel of LightMesh's vertex color to indicate Slot Index.
  149. int m_BatchSlotIndex = 0;
  150. internal int batchSlotIndex { get { return m_BatchSlotIndex; } set { m_BatchSlotIndex = value; } }
  151. internal int[] affectedSortingLayers => m_ApplyToSortingLayers;
  152. private int lightCookieSpriteInstanceID => lightCookieSprite?.GetInstanceID() ?? 0;
  153. internal bool useCookieSprite => (lightType == LightType.Point || lightType == LightType.Sprite) && (lightCookieSprite != null && lightCookieSprite.texture != null);
  154. internal RTHandle m_CookieSpriteTexture = null;
  155. internal TextureHandle m_CookieSpriteTextureHandle;
  156. [SerializeField]
  157. Bounds m_LocalBounds;
  158. internal BoundingSphere boundingSphere { get; private set; }
  159. internal Mesh lightMesh
  160. {
  161. get
  162. {
  163. if (null == m_Mesh)
  164. m_Mesh = new Mesh();
  165. return m_Mesh;
  166. }
  167. }
  168. internal bool hasCachedMesh => (vertices.Length > 1 && indices.Length > 1);
  169. internal bool forceUpdate = false;
  170. /// <summary>
  171. /// The light's current type
  172. /// </summary>
  173. public LightType lightType
  174. {
  175. get => m_LightType;
  176. set
  177. {
  178. if (m_LightType != value)
  179. UpdateMesh();
  180. m_LightType = value;
  181. Light2DManager.ErrorIfDuplicateGlobalLight(this);
  182. }
  183. }
  184. /// <summary>
  185. /// The lights current operation index
  186. /// </summary>
  187. public int blendStyleIndex { get => m_BlendStyleIndex; set => m_BlendStyleIndex = value; }
  188. /// <summary>
  189. /// Specifies the darkness of the shadow
  190. /// </summary>
  191. public float shadowIntensity { get => m_ShadowIntensity; set => m_ShadowIntensity = Mathf.Clamp01(value); }
  192. /// <summary>
  193. /// Specifies the softness of the soft shadow
  194. /// </summary>
  195. public float shadowSoftness { get => m_ShadowSoftness; set => m_ShadowSoftness = value; }
  196. /// <summary>
  197. /// Specifies that the shadows are enabled
  198. /// </summary>
  199. public bool shadowsEnabled { get => m_ShadowsEnabled; set => m_ShadowsEnabled = value; }
  200. /// <summary>
  201. /// Specifies the darkness of the shadow
  202. /// </summary>
  203. public float shadowVolumeIntensity { get => m_ShadowVolumeIntensity; set => m_ShadowVolumeIntensity = Mathf.Clamp01(value); }
  204. /// <summary>
  205. /// Specifies that the volumetric shadows are enabled
  206. /// </summary>
  207. public bool volumetricShadowsEnabled { get => m_ShadowVolumeIntensityEnabled; set => m_ShadowVolumeIntensityEnabled = value; }
  208. /// <summary>
  209. /// The lights current color
  210. /// </summary>
  211. public Color color { get => m_Color; set => m_Color = value; }
  212. /// <summary>
  213. /// The lights current intensity
  214. /// </summary>
  215. public float intensity { get => m_Intensity; set => m_Intensity = value; }
  216. /// <summary>
  217. /// The lights current intensity
  218. /// </summary>
  219. ///
  220. [Obsolete]
  221. public float volumeOpacity => m_LightVolumeIntensity;
  222. /// <summary>
  223. /// Controls the visibility of the light's volume
  224. /// </summary>
  225. public float volumeIntensity { get => m_LightVolumeIntensity; set => m_LightVolumeIntensity = value; }
  226. /// <summary>
  227. /// Enables or disables the light's volume
  228. /// </summary>
  229. ///
  230. [Obsolete]
  231. public bool volumeIntensityEnabled { get => m_LightVolumeEnabled; set => m_LightVolumeEnabled = value; }
  232. /// <summary>
  233. /// Enables or disables the light's volume
  234. /// </summary>
  235. ///
  236. public bool volumetricEnabled { get => m_LightVolumeEnabled; set => m_LightVolumeEnabled = value; }
  237. /// <summary>
  238. /// The Sprite that's used by the Sprite Light type to control the shape light
  239. /// </summary>
  240. public Sprite lightCookieSprite { get { return m_LightType != LightType.Point ? m_LightCookieSprite : m_DeprecatedPointLightCookieSprite; } set => m_LightCookieSprite = value; }
  241. /// <summary>
  242. /// Controls the brightness and distance of the fall off (edge) of the light
  243. /// </summary>
  244. public float falloffIntensity { get => m_FalloffIntensity; set => m_FalloffIntensity = Mathf.Clamp(value, 0, 1); }
  245. /// <summary>
  246. /// Controls the falloff for soft shadows
  247. /// </summary>
  248. public float shadowSoftnessFalloffIntensity { get => m_ShadowSoftnessFalloffIntensity; set => m_ShadowSoftnessFalloffIntensity = Mathf.Clamp(value, 0, 1); }
  249. /// <summary>
  250. /// Checks if the alpha overlap operation is alpha blend.
  251. /// This is obsolete.
  252. /// </summary>
  253. [Obsolete]
  254. public bool alphaBlendOnOverlap { get { return m_OverlapOperation == OverlapOperation.AlphaBlend; } }
  255. /// <summary>
  256. /// Controls the overlap operation mode.
  257. /// </summary>
  258. public OverlapOperation overlapOperation { get => m_OverlapOperation; set => m_OverlapOperation = value; }
  259. /// <summary>
  260. /// Gets or sets the light order. The lightOrder determines the order in which the lights are rendered onto the light textures.
  261. /// </summary>
  262. public int lightOrder { get => m_LightOrder; set => m_LightOrder = value; }
  263. /// <summary>
  264. /// The simulated z distance of the light from the surface used in normal map calculation.
  265. /// </summary>
  266. public float normalMapDistance => m_NormalMapDistance;
  267. /// <summary>
  268. /// Returns the calculation quality for the normal map rendering. Please refer to NormalMapQuality.
  269. /// </summary>
  270. public NormalMapQuality normalMapQuality => m_NormalMapQuality;
  271. /// <summary>
  272. /// Returns if volumetric shadows should be rendered.
  273. /// </summary>
  274. public bool renderVolumetricShadows => volumetricShadowsEnabled && shadowVolumeIntensity > 0;
  275. internal void MarkForUpdate()
  276. {
  277. forceUpdate = true;
  278. }
  279. internal void CacheValues()
  280. {
  281. m_CachedPosition = transform.position;
  282. }
  283. internal int GetTopMostLitLayer()
  284. {
  285. var largestIndex = Int32.MinValue;
  286. var largestLayer = 0;
  287. var layers = Light2DManager.GetCachedSortingLayer();
  288. for (var i = 0; i < m_ApplyToSortingLayers.Length; ++i)
  289. {
  290. for (var layer = layers.Length - 1; layer >= largestLayer; --layer)
  291. {
  292. if (layers[layer].id == m_ApplyToSortingLayers[i])
  293. {
  294. largestIndex = layers[layer].value;
  295. largestLayer = layer;
  296. }
  297. }
  298. }
  299. return largestIndex;
  300. }
  301. internal Bounds UpdateSpriteMesh()
  302. {
  303. if (m_LightCookieSprite == null && (m_Vertices.Length != 1 || m_Triangles.Length != 1))
  304. {
  305. m_Vertices = new LightUtility.LightMeshVertex[1];
  306. m_Triangles = new ushort[1];
  307. }
  308. return LightUtility.GenerateSpriteMesh(this, m_LightCookieSprite, LightBatch.GetBatchColor());
  309. }
  310. internal void UpdateBatchSlotIndex()
  311. {
  312. if (lightMesh && lightMesh.colors != null && lightMesh.colors.Length != 0)
  313. m_BatchSlotIndex = LightBatch.GetBatchSlotIndex(lightMesh.colors[0].b);
  314. }
  315. internal bool NeedsColorIndexBaking()
  316. {
  317. if (lightMesh && LightBatch.isBatchingSupported)
  318. {
  319. if (lightMesh.colors.Length != 0)
  320. return lightMesh.colors[0].b == 0;
  321. }
  322. return false;
  323. }
  324. internal void UpdateCookieSpriteTexture()
  325. {
  326. m_CookieSpriteTexture?.Release();
  327. if (useCookieSprite)
  328. m_CookieSpriteTexture = RTHandles.Alloc(lightCookieSprite.texture);
  329. }
  330. internal void UpdateMesh(bool forceUpdate = false)
  331. {
  332. var shapePathHash = LightUtility.GetShapePathHash(shapePath);
  333. var fallOffSizeChanged = LightUtility.CheckForChange(m_ShapeLightFalloffSize, ref m_PreviousShapeLightFalloffSize);
  334. var parametricRadiusChanged = LightUtility.CheckForChange(m_ShapeLightParametricRadius, ref m_PreviousShapeLightParametricRadius);
  335. var parametricSidesChanged = LightUtility.CheckForChange(m_ShapeLightParametricSides, ref m_PreviousShapeLightParametricSides);
  336. var parametricAngleOffsetChanged = LightUtility.CheckForChange(m_ShapeLightParametricAngleOffset, ref m_PreviousShapeLightParametricAngleOffset);
  337. var spriteInstanceChanged = LightUtility.CheckForChange(lightCookieSpriteInstanceID, ref m_PreviousLightCookieSprite);
  338. var shapePathHashChanged = LightUtility.CheckForChange(shapePathHash, ref m_PreviousShapePathHash);
  339. var lightTypeChanged = LightUtility.CheckForChange(m_LightType, ref m_PreviousLightType);
  340. var hashChanged = fallOffSizeChanged || parametricRadiusChanged || parametricSidesChanged ||
  341. parametricAngleOffsetChanged || spriteInstanceChanged || shapePathHashChanged || lightTypeChanged || NeedsColorIndexBaking();
  342. // Mesh Rebuilding
  343. if (hashChanged || forceUpdate)
  344. {
  345. var batchChannelColor = LightBatch.GetBatchColor();
  346. switch (m_LightType)
  347. {
  348. case LightType.Freeform:
  349. m_LocalBounds = LightUtility.GenerateShapeMesh(this, m_ShapePath, m_ShapeLightFalloffSize, batchChannelColor);
  350. break;
  351. case LightType.Parametric:
  352. m_LocalBounds = LightUtility.GenerateParametricMesh(this, m_ShapeLightParametricRadius, m_ShapeLightFalloffSize, m_ShapeLightParametricAngleOffset, m_ShapeLightParametricSides, batchChannelColor);
  353. break;
  354. case LightType.Sprite:
  355. m_LocalBounds = UpdateSpriteMesh();
  356. break;
  357. case LightType.Point:
  358. m_LocalBounds = LightUtility.GenerateParametricMesh(this, 1.412135f, 0, 0, 4, batchChannelColor);
  359. break;
  360. }
  361. UpdateCookieSpriteTexture();
  362. UpdateBatchSlotIndex();
  363. }
  364. }
  365. internal void UpdateBoundingSphere()
  366. {
  367. if (isPointLight)
  368. {
  369. boundingSphere = new BoundingSphere(transform.position, m_PointLightOuterRadius);
  370. return;
  371. }
  372. var maxBound = transform.TransformPoint(Vector3.Max(m_LocalBounds.max, m_LocalBounds.max + (Vector3)m_ShapeLightFalloffOffset));
  373. var minBound = transform.TransformPoint(Vector3.Min(m_LocalBounds.min, m_LocalBounds.min + (Vector3)m_ShapeLightFalloffOffset));
  374. var center = 0.5f * (maxBound + minBound);
  375. var radius = Vector3.Magnitude(maxBound - center);
  376. boundingSphere = new BoundingSphere(center, radius);
  377. }
  378. internal bool IsLitLayer(int layer)
  379. {
  380. if (m_ApplyToSortingLayers == null)
  381. return false;
  382. for (var i = 0; i < m_ApplyToSortingLayers.Length; i++)
  383. if (m_ApplyToSortingLayers[i] == layer)
  384. return true;
  385. return false;
  386. }
  387. internal Matrix4x4 GetMatrix()
  388. {
  389. var matrix = transform.localToWorldMatrix;
  390. if (lightType == Light2D.LightType.Point)
  391. {
  392. var scale = new Vector3(pointLightOuterRadius, pointLightOuterRadius, pointLightOuterRadius);
  393. matrix = Matrix4x4.TRS(transform.position, transform.rotation, scale);
  394. }
  395. return matrix;
  396. }
  397. private void Awake()
  398. {
  399. #if UNITY_EDITOR
  400. // Default target sorting layers to "All"
  401. if (m_ApplyToSortingLayers == null)
  402. m_ApplyToSortingLayers = SortingLayer.layers.Select(x => x.id).ToArray();
  403. #endif
  404. }
  405. void OnEnable()
  406. {
  407. m_PreviousLightCookieSprite = lightCookieSpriteInstanceID;
  408. Light2DManager.RegisterLight(this);
  409. UpdateCookieSpriteTexture();
  410. #if UNITY_EDITOR
  411. SortingLayer.onLayerAdded += OnSortingLayerAdded;
  412. SortingLayer.onLayerRemoved += OnSortingLayerRemoved;
  413. #endif
  414. }
  415. private void OnDisable()
  416. {
  417. Light2DManager.DeregisterLight(this);
  418. m_CookieSpriteTexture?.Release();
  419. #if UNITY_EDITOR
  420. SortingLayer.onLayerAdded -= OnSortingLayerAdded;
  421. SortingLayer.onLayerRemoved -= OnSortingLayerRemoved;
  422. #endif
  423. }
  424. private void LateUpdate()
  425. {
  426. if (m_LightType == LightType.Global)
  427. return;
  428. UpdateMesh(forceUpdate);
  429. UpdateBoundingSphere();
  430. forceUpdate = false;
  431. }
  432. #if UNITY_EDITOR
  433. private void OnSortingLayerAdded(SortingLayer layer)
  434. {
  435. m_ApplyToSortingLayers = m_ApplyToSortingLayers.Append(layer.id).ToArray();
  436. }
  437. private void OnSortingLayerRemoved(SortingLayer layer)
  438. {
  439. m_ApplyToSortingLayers = m_ApplyToSortingLayers.Where(x => x != layer.id && SortingLayer.IsValid(x)).ToArray();
  440. }
  441. #endif
  442. /// <summary>
  443. /// OnBeforeSerialize implementation.
  444. /// </summary>
  445. public void OnBeforeSerialize()
  446. {
  447. m_ComponentVersion = k_CurrentComponentVersion;
  448. }
  449. /// <summary>
  450. /// OnAfterSerialize implementation.
  451. /// </summary>
  452. public void OnAfterDeserialize()
  453. {
  454. // Upgrade from no serialized version
  455. if (m_ComponentVersion == ComponentVersions.Version_Unserialized)
  456. {
  457. m_ShadowVolumeIntensityEnabled = m_ShadowVolumeIntensity > 0;
  458. m_ShadowsEnabled = m_ShadowIntensity > 0;
  459. m_LightVolumeEnabled = m_LightVolumeIntensity > 0;
  460. m_NormalMapQuality = !m_UseNormalMap ? NormalMapQuality.Disabled : m_NormalMapQuality;
  461. m_OverlapOperation = m_AlphaBlendOnOverlap ? OverlapOperation.AlphaBlend : m_OverlapOperation;
  462. m_ComponentVersion = ComponentVersions.Version_1;
  463. }
  464. if(m_ComponentVersion < ComponentVersions.Version_2)
  465. {
  466. m_ShadowSoftness = 0;
  467. }
  468. }
  469. }
  470. }