暫無描述
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.

TextureGradient.cs 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using UnityEngine.Experimental.Rendering;
  4. namespace UnityEngine.Rendering
  5. {
  6. // Due to limitations in the builtin Gradient we need this custom wrapper.
  7. /// <summary>
  8. /// A wrapper around <c>Gradient</c> to automatically bake it into a texture.
  9. /// </summary>
  10. [Serializable]
  11. public class TextureGradient : IDisposable
  12. {
  13. /// <summary>
  14. /// Texture Size computed.
  15. /// </summary>
  16. [field: SerializeField, HideInInspector]
  17. public int textureSize { get; private set; }
  18. /// <summary>
  19. /// Internal Gradient used to generate the Texture
  20. /// </summary>
  21. [SerializeField]
  22. Gradient m_Gradient;
  23. Texture2D m_Texture = null;
  24. int m_RequestedTextureSize = -1;
  25. bool m_IsTextureDirty;
  26. bool m_Precise;
  27. /// <summary>All color keys defined in the gradient.</summary>
  28. [HideInInspector]
  29. public GradientColorKey[] colorKeys => m_Gradient?.colorKeys;
  30. /// <summary>All alpha keys defined in the gradient.</summary>
  31. [HideInInspector]
  32. public GradientAlphaKey[] alphaKeys => m_Gradient?.alphaKeys;
  33. /// <summary>Controls how the gradient colors are interpolated.</summary>
  34. [SerializeField, HideInInspector]
  35. public GradientMode mode = GradientMode.PerceptualBlend;
  36. /// <summary>Indicates the color space that the gradient color keys are using.</summary>
  37. [SerializeField, HideInInspector]
  38. public ColorSpace colorSpace = ColorSpace.Uninitialized;
  39. /// <summary>
  40. /// Creates a new <see cref="TextureGradient"/> from an existing <c>Gradient</c>.
  41. /// </summary>
  42. /// <param name="baseCurve">The source <c>Gradient</c>.</param>
  43. public TextureGradient(Gradient baseCurve)
  44. : this(baseCurve.colorKeys, baseCurve.alphaKeys)
  45. {
  46. mode = baseCurve.mode;
  47. colorSpace = baseCurve.colorSpace;
  48. m_Gradient.mode = baseCurve.mode;
  49. m_Gradient.colorSpace = baseCurve.colorSpace;
  50. }
  51. /// <summary>
  52. /// Creates a new <see cref="TextureCurve"/> from an arbitrary number of keyframes.
  53. /// </summary>
  54. /// <param name="colorKeys">An array of keyframes used to define the color of gradient.</param>
  55. /// <param name="alphaKeys">An array of keyframes used to define the alpha of gradient.</param>
  56. /// <param name="mode">Indicates the color space that the gradient color keys are using.</param>
  57. /// <param name="colorSpace">Controls how the gradient colors are interpolated.</param>
  58. /// <param name="requestedTextureSize">Texture Size used internally, if '-1' using Nyquist-Shannon limits.</param>
  59. /// <param name="precise">if precise uses 4*Nyquist-Shannon limits, 2* otherwise.</param>
  60. public TextureGradient(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys, GradientMode mode = GradientMode.PerceptualBlend, ColorSpace colorSpace = ColorSpace.Uninitialized, int requestedTextureSize = -1, bool precise = false)
  61. {
  62. Rebuild(colorKeys, alphaKeys, mode, colorSpace, requestedTextureSize, precise);
  63. }
  64. void Rebuild(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys, GradientMode mode, ColorSpace colorSpace, int requestedTextureSize, bool precise)
  65. {
  66. m_Gradient = new Gradient();
  67. m_Gradient.mode = mode;
  68. m_Gradient.colorSpace = colorSpace;
  69. m_Gradient.SetKeys(colorKeys, alphaKeys);
  70. m_Precise = precise;
  71. m_RequestedTextureSize = requestedTextureSize;
  72. if (requestedTextureSize > 0)
  73. {
  74. textureSize = requestedTextureSize;
  75. }
  76. else
  77. {
  78. float smallestDelta = 1.0f;
  79. float[] times = new float[colorKeys.Length + alphaKeys.Length];
  80. for (int i = 0; i < colorKeys.Length; ++i)
  81. {
  82. times[i] = colorKeys[i].time;
  83. }
  84. for (int i = 0; i < alphaKeys.Length; ++i)
  85. {
  86. times[colorKeys.Length + i] = alphaKeys[i].time;
  87. }
  88. Array.Sort(times);
  89. // Found the smallest increment between 2 keys
  90. for (int i = 1; i < times.Length; ++i)
  91. {
  92. int k0 = Math.Max(i - 1, 0);
  93. int k1 = Math.Min(i, times.Length - 1);
  94. float delta = Mathf.Abs(times[k0] - times[k1]);
  95. // Do not compare if time is duplicated
  96. if (delta > 0 && delta < smallestDelta)
  97. smallestDelta = delta;
  98. }
  99. // Nyquist-Shannon
  100. // smallestDelta: 1.00f => Sampling => 2
  101. // smallestDelta: 0.50f => Sampling => 3
  102. // smallestDelta: 0.33f => Sampling => 4
  103. // smallestDelta: 0.25f => Sampling => 5
  104. // 2x: Theoretical limits
  105. // 4x: Preserve original frequency
  106. // Round to the closest 4 * Nyquist-Shannon limits
  107. // 4x for Fixed to capture sharp discontinuity
  108. float scale;
  109. if (precise || mode == GradientMode.Fixed)
  110. scale = 4.0f;
  111. else
  112. scale = 2.0f;
  113. float sizef = scale * Mathf.Ceil(1.0f / smallestDelta + 1.0f);
  114. textureSize = Mathf.RoundToInt(sizef);
  115. // Arbitrary max (1024)
  116. textureSize = Math.Min(textureSize, 1024);
  117. }
  118. SetDirty();
  119. }
  120. /// <summary>
  121. /// Cleans up the internal texture resource.
  122. /// </summary>
  123. public void Dispose()
  124. {
  125. //Release();
  126. }
  127. /// <summary>
  128. /// Releases the internal texture resource.
  129. /// </summary>
  130. public void Release()
  131. {
  132. if (m_Texture != null)
  133. CoreUtils.Destroy(m_Texture);
  134. m_Texture = null;
  135. }
  136. /// <summary>
  137. /// Marks the curve as dirty to trigger a redraw of the texture the next time <see cref="GetTexture"/>
  138. /// is called.
  139. /// </summary>
  140. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  141. public void SetDirty()
  142. {
  143. m_IsTextureDirty = true;
  144. }
  145. static GraphicsFormat GetTextureFormat()
  146. {
  147. return GraphicsFormat.R8G8B8A8_UNorm;
  148. }
  149. /// <summary>
  150. /// Gets the texture representation of this Gradient.
  151. /// </summary>
  152. /// <returns>A texture.</returns>
  153. public Texture2D GetTexture()
  154. {
  155. float step = 1.0f / (float)(textureSize - 1);
  156. if (m_Texture != null && m_Texture.width != textureSize)
  157. {
  158. Object.DestroyImmediate(m_Texture);
  159. m_Texture = null;
  160. }
  161. if (m_Texture == null)
  162. {
  163. m_Texture = new Texture2D(textureSize, 1, GetTextureFormat(), TextureCreationFlags.None);
  164. m_Texture.name = "GradientTexture";
  165. m_Texture.hideFlags = HideFlags.HideAndDontSave;
  166. m_Texture.filterMode = FilterMode.Bilinear;
  167. m_Texture.wrapMode = TextureWrapMode.Clamp;
  168. m_Texture.anisoLevel = 0;
  169. m_IsTextureDirty = true;
  170. }
  171. if (m_IsTextureDirty)
  172. {
  173. var pixels = new Color[textureSize];
  174. for (int i = 0; i < textureSize; i++)
  175. pixels[i] = Evaluate(i * step);
  176. m_Texture.SetPixels(pixels);
  177. m_Texture.Apply(false, false);
  178. m_IsTextureDirty = false;
  179. m_Texture.IncrementUpdateCount();
  180. }
  181. return m_Texture;
  182. }
  183. /// <summary>
  184. /// Evaluate a time value on the Gradient.
  185. /// </summary>
  186. /// <param name="time">The time within the Gradient you want to evaluate.</param>
  187. /// <returns>The value of the Gradient, at the point in time specified.</returns>
  188. public Color Evaluate(float time)
  189. {
  190. if (textureSize <= 0)
  191. return Color.black;
  192. return m_Gradient.Evaluate(time);
  193. }
  194. /// <summary>
  195. /// Setup Gradient with an array of color keys and alpha keys.
  196. /// </summary>
  197. /// <param name="colorKeys">Color keys of the gradient (maximum 8 color keys).</param>
  198. /// <param name="alphaKeys">Alpha keys of the gradient (maximum 8 alpha keys).</param>
  199. /// <param name="mode">Indicates the color space that the gradient color keys are using.</param>
  200. /// <param name="colorSpace">Controls how the gradient colors are interpolated.</param>
  201. public void SetKeys(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys, GradientMode mode, ColorSpace colorSpace)
  202. {
  203. m_Gradient.SetKeys(colorKeys, alphaKeys);
  204. m_Gradient.mode = mode;
  205. m_Gradient.colorSpace = colorSpace;
  206. // Rebuild will make the TextureGradient Dirty.
  207. Rebuild(colorKeys, alphaKeys, mode, colorSpace, m_RequestedTextureSize, m_Precise);
  208. }
  209. }
  210. /// <summary>
  211. /// A <see cref="VolumeParameter"/> that holds a <see cref="TextureGradient"/> value.
  212. /// </summary>
  213. [Serializable]
  214. public class TextureGradientParameter : VolumeParameter<TextureGradient>
  215. {
  216. /// <summary>
  217. /// Creates a new <see cref="TextureGradientParameter"/> instance.
  218. /// </summary>
  219. /// <param name="value">The initial value to store in the parameter.</param>
  220. /// <param name="overrideState">The initial override state for the parameter.</param>
  221. public TextureGradientParameter(TextureGradient value, bool overrideState = false)
  222. : base(value, overrideState) { }
  223. /// <summary>
  224. /// Release implementation.
  225. /// </summary>
  226. public override void Release() => m_Value.Release();
  227. // TODO: TextureGradient interpolation
  228. }
  229. }