UIEffect.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. using UnityEngine.Serialization;
  6. #if UNITY_EDITOR
  7. using System.IO;
  8. using System.Linq;
  9. using UnityEditor;
  10. #endif
  11. namespace Coffee.UIEffects
  12. {
  13. /// <summary>
  14. /// UIEffect.
  15. /// </summary>
  16. [ExecuteInEditMode]
  17. [RequireComponent(typeof(Graphic))]
  18. [DisallowMultipleComponent]
  19. [AddComponentMenu("UI/UIEffects/UIEffect", 1)]
  20. public class UIEffect : BaseMaterialEffect, IMaterialModifier
  21. {
  22. private const uint k_ShaderId = 2 << 3;
  23. private static readonly ParameterTexture s_ParamTex = new ParameterTexture(4, 1024, "_ParamTex");
  24. [FormerlySerializedAs("m_ToneLevel")]
  25. [Tooltip("Effect factor between 0(no effect) and 1(complete effect).")]
  26. [SerializeField]
  27. [Range(0, 1)]
  28. float m_EffectFactor = 1;
  29. [Tooltip("Color effect factor between 0(no effect) and 1(complete effect).")] [SerializeField] [Range(0, 1)]
  30. float m_ColorFactor = 1;
  31. [FormerlySerializedAs("m_Blur")]
  32. [Tooltip("How far is the blurring from the graphic.")]
  33. [SerializeField]
  34. [Range(0, 1)]
  35. float m_BlurFactor = 1;
  36. [FormerlySerializedAs("m_ToneMode")] [Tooltip("Effect mode")] [SerializeField]
  37. EffectMode m_EffectMode = EffectMode.None;
  38. [Tooltip("Color effect mode")] [SerializeField]
  39. ColorMode m_ColorMode = ColorMode.Multiply;
  40. [Tooltip("Blur effect mode")] [SerializeField]
  41. BlurMode m_BlurMode = BlurMode.None;
  42. [Tooltip("Advanced blurring remove common artifacts in the blur effect for uGUI.")] [SerializeField]
  43. bool m_AdvancedBlur = false;
  44. private enum BlurEx
  45. {
  46. None = 0,
  47. Ex = 1,
  48. }
  49. /// <summary>
  50. /// Additional canvas shader channels to use this component.
  51. /// </summary>
  52. public AdditionalCanvasShaderChannels uvMaskChannel
  53. {
  54. get { return connector.extraChannel; }
  55. }
  56. /// <summary>
  57. /// Effect factor between 0(no effect) and 1(complete effect).
  58. /// </summary>
  59. public float effectFactor
  60. {
  61. get { return m_EffectFactor; }
  62. set
  63. {
  64. value = Mathf.Clamp(value, 0, 1);
  65. if (Mathf.Approximately(m_EffectFactor, value)) return;
  66. m_EffectFactor = value;
  67. SetEffectParamsDirty();
  68. }
  69. }
  70. /// <summary>
  71. /// Color effect factor between 0(no effect) and 1(complete effect).
  72. /// </summary>
  73. public float colorFactor
  74. {
  75. get { return m_ColorFactor; }
  76. set
  77. {
  78. value = Mathf.Clamp(value, 0, 1);
  79. if (Mathf.Approximately(m_ColorFactor, value)) return;
  80. m_ColorFactor = value;
  81. SetEffectParamsDirty();
  82. }
  83. }
  84. /// <summary>
  85. /// How far is the blurring from the graphic.
  86. /// </summary>
  87. public float blurFactor
  88. {
  89. get { return m_BlurFactor; }
  90. set
  91. {
  92. value = Mathf.Clamp(value, 0, 1);
  93. if (Mathf.Approximately(m_BlurFactor, value)) return;
  94. m_BlurFactor = value;
  95. SetEffectParamsDirty();
  96. }
  97. }
  98. /// <summary>
  99. /// Effect mode.
  100. /// </summary>
  101. public EffectMode effectMode
  102. {
  103. get { return m_EffectMode; }
  104. set
  105. {
  106. if (m_EffectMode == value) return;
  107. m_EffectMode = value;
  108. SetMaterialDirty();
  109. }
  110. }
  111. /// <summary>
  112. /// Color effect mode.
  113. /// </summary>
  114. public ColorMode colorMode
  115. {
  116. get { return m_ColorMode; }
  117. set
  118. {
  119. if (m_ColorMode == value) return;
  120. m_ColorMode = value;
  121. SetMaterialDirty();
  122. }
  123. }
  124. /// <summary>
  125. /// Blur effect mode(readonly).
  126. /// </summary>
  127. public BlurMode blurMode
  128. {
  129. get { return m_BlurMode; }
  130. set
  131. {
  132. if (m_BlurMode == value) return;
  133. m_BlurMode = value;
  134. SetMaterialDirty();
  135. }
  136. }
  137. /// <summary>
  138. /// Gets the parameter texture.
  139. /// </summary>
  140. public override ParameterTexture paramTex
  141. {
  142. get { return s_ParamTex; }
  143. }
  144. /// <summary>
  145. /// Advanced blurring remove common artifacts in the blur effect for uGUI.
  146. /// </summary>
  147. public bool advancedBlur
  148. {
  149. get { return m_AdvancedBlur; }
  150. set
  151. {
  152. if (m_AdvancedBlur == value) return;
  153. m_AdvancedBlur = value;
  154. SetVerticesDirty();
  155. SetMaterialDirty();
  156. }
  157. }
  158. public override Hash128 GetMaterialHash(Material material)
  159. {
  160. if (!isActiveAndEnabled || !material || !material.shader)
  161. return k_InvalidHash;
  162. var shaderVariantId = (uint) (((int) m_EffectMode << 6) + ((int) m_ColorMode << 9) +
  163. ((int) m_BlurMode << 11) + ((m_AdvancedBlur ? 1 : 0) << 13));
  164. return new Hash128(
  165. (uint) material.GetInstanceID(),
  166. k_ShaderId + shaderVariantId,
  167. 0,
  168. 0
  169. );
  170. }
  171. public override void ModifyMaterial(Material newMaterial, Graphic graphic)
  172. {
  173. var connector = GraphicConnector.FindConnector(graphic);
  174. newMaterial.shader = Shader.Find(string.Format("Hidden/{0} (UIEffect)", newMaterial.shader.name));
  175. SetShaderVariants(newMaterial, m_EffectMode, m_ColorMode, m_BlurMode,
  176. m_AdvancedBlur ? BlurEx.Ex : BlurEx.None);
  177. paramTex.RegisterMaterial(newMaterial);
  178. }
  179. /// <summary>
  180. /// Modifies the mesh.
  181. /// </summary>
  182. public override void ModifyMesh(VertexHelper vh, Graphic graphic)
  183. {
  184. if (!isActiveAndEnabled)
  185. {
  186. return;
  187. }
  188. var normalizedIndex = paramTex.GetNormalizedIndex(this);
  189. if (m_BlurMode != BlurMode.None && advancedBlur)
  190. {
  191. vh.GetUIVertexStream(s_TempVerts);
  192. vh.Clear();
  193. var count = s_TempVerts.Count;
  194. // Bundle
  195. int bundleSize = connector.IsText(graphic) ? 6 : count;
  196. Rect posBounds = default(Rect);
  197. Rect uvBounds = default(Rect);
  198. Vector3 size = default(Vector3);
  199. Vector3 tPos = default(Vector3);
  200. Vector3 tUV = default(Vector3);
  201. float expand = (float) blurMode * 6 * 2;
  202. for (int i = 0; i < count; i += bundleSize)
  203. {
  204. // min/max for bundled-quad
  205. GetBounds(s_TempVerts, i, bundleSize, ref posBounds, ref uvBounds, true);
  206. // Pack uv mask.
  207. Vector2 uvMask = new Vector2(Packer.ToFloat(uvBounds.xMin, uvBounds.yMin),
  208. Packer.ToFloat(uvBounds.xMax, uvBounds.yMax));
  209. // Quad
  210. for (int j = 0; j < bundleSize; j += 6)
  211. {
  212. Vector3 cornerPos1 = s_TempVerts[i + j + 1].position;
  213. Vector3 cornerPos2 = s_TempVerts[i + j + 4].position;
  214. // Is outer quad?
  215. bool hasOuterEdge = (bundleSize == 6)
  216. || !posBounds.Contains(cornerPos1)
  217. || !posBounds.Contains(cornerPos2);
  218. if (hasOuterEdge)
  219. {
  220. Vector3 cornerUv1 = s_TempVerts[i + j + 1].uv0;
  221. Vector3 cornerUv2 = s_TempVerts[i + j + 4].uv0;
  222. Vector3 centerPos = (cornerPos1 + cornerPos2) / 2;
  223. Vector3 centerUV = (cornerUv1 + cornerUv2) / 2;
  224. size = (cornerPos1 - cornerPos2);
  225. size.x = 1 + expand / Mathf.Abs(size.x);
  226. size.y = 1 + expand / Mathf.Abs(size.y);
  227. size.z = 1 + expand / Mathf.Abs(size.z);
  228. tPos = centerPos - Vector3.Scale(size, centerPos);
  229. tUV = centerUV - Vector3.Scale(size, centerUV);
  230. }
  231. // Vertex
  232. for (int k = 0; k < 6; k++)
  233. {
  234. UIVertex vt = s_TempVerts[i + j + k];
  235. Vector3 pos = vt.position;
  236. Vector2 uv0 = vt.uv0;
  237. if (hasOuterEdge && (pos.x < posBounds.xMin || posBounds.xMax < pos.x))
  238. {
  239. pos.x = pos.x * size.x + tPos.x;
  240. uv0.x = uv0.x * size.x + tUV.x;
  241. }
  242. if (hasOuterEdge && (pos.y < posBounds.yMin || posBounds.yMax < pos.y))
  243. {
  244. pos.y = pos.y * size.y + tPos.y;
  245. uv0.y = uv0.y * size.y + tUV.y;
  246. }
  247. vt.uv0 = new Vector2(Packer.ToFloat((uv0.x + 0.5f) / 2f, (uv0.y + 0.5f) / 2f),
  248. normalizedIndex);
  249. vt.position = pos;
  250. connector.SetExtraChannel(ref vt, uvMask);
  251. s_TempVerts[i + j + k] = vt;
  252. }
  253. }
  254. }
  255. vh.AddUIVertexTriangleStream(s_TempVerts);
  256. s_TempVerts.Clear();
  257. }
  258. else
  259. {
  260. int count = vh.currentVertCount;
  261. UIVertex vt = default(UIVertex);
  262. for (int i = 0; i < count; i++)
  263. {
  264. vh.PopulateUIVertex(ref vt, i);
  265. Vector2 uv0 = vt.uv0;
  266. vt.uv0 = new Vector2(
  267. Packer.ToFloat((uv0.x + 0.5f) / 2f, (uv0.y + 0.5f) / 2f),
  268. normalizedIndex
  269. );
  270. vh.SetUIVertex(vt, i);
  271. }
  272. }
  273. }
  274. protected override void SetEffectParamsDirty()
  275. {
  276. paramTex.SetData(this, 0, m_EffectFactor); // param.x : effect factor
  277. paramTex.SetData(this, 1, m_ColorFactor); // param.y : color factor
  278. paramTex.SetData(this, 2, m_BlurFactor); // param.z : blur factor
  279. }
  280. static void GetBounds(List<UIVertex> verts, int start, int count, ref Rect posBounds, ref Rect uvBounds,
  281. bool global)
  282. {
  283. Vector2 minPos = new Vector2(float.MaxValue, float.MaxValue);
  284. Vector2 maxPos = new Vector2(float.MinValue, float.MinValue);
  285. Vector2 minUV = new Vector2(float.MaxValue, float.MaxValue);
  286. Vector2 maxUV = new Vector2(float.MinValue, float.MinValue);
  287. for (int i = start; i < start + count; i++)
  288. {
  289. UIVertex vt = verts[i];
  290. Vector2 uv = vt.uv0;
  291. Vector3 pos = vt.position;
  292. // Left-Bottom
  293. if (minPos.x >= pos.x && minPos.y >= pos.y)
  294. {
  295. minPos = pos;
  296. }
  297. // Right-Top
  298. else if (maxPos.x <= pos.x && maxPos.y <= pos.y)
  299. {
  300. maxPos = pos;
  301. }
  302. // Left-Bottom
  303. if (minUV.x >= uv.x && minUV.y >= uv.y)
  304. {
  305. minUV = uv;
  306. }
  307. // Right-Top
  308. else if (maxUV.x <= uv.x && maxUV.y <= uv.y)
  309. {
  310. maxUV = uv;
  311. }
  312. }
  313. // Shrink coordinate for detect edge
  314. posBounds.Set(minPos.x + 0.001f, minPos.y + 0.001f, maxPos.x - minPos.x - 0.002f,
  315. maxPos.y - minPos.y - 0.002f);
  316. uvBounds.Set(minUV.x, minUV.y, maxUV.x - minUV.x, maxUV.y - minUV.y);
  317. }
  318. }
  319. }