Lutify.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. // Lutify - Unity Asset
  2. // Copyright (c) 2015 - Thomas Hourdel
  3. // http://www.thomashourdel.com
  4. using UnityEngine;
  5. [DisallowMultipleComponent]
  6. [RequireComponent(typeof(Camera)), ExecuteInEditMode]
  7. [AddComponentMenu("Image Effects/Lutify")]
  8. public class Lutify : MonoBehaviour
  9. {
  10. public enum SplitMode
  11. {
  12. None,
  13. Horizontal,
  14. Vertical
  15. }
  16. [Tooltip("The texture to use as a lookup table. Size should be: height = sqrt(width)")]
  17. public Texture2D LookupTexture;
  18. [Tooltip("Shows a before/after comparison by splitting the screen in half.")]
  19. public SplitMode Split = SplitMode.None;
  20. [Tooltip("Lutify will automatically detect the correct shader to use for the device but you can force it to only use the compatibility shader.")]
  21. public bool ForceCompatibility = false;
  22. [Tooltip("Sets the filter mode for the LUT texture. You'll want to set this to Point when using palette reduction LUTs.")]
  23. public FilterMode LutFiltering = FilterMode.Bilinear;
  24. [Range(0f, 10f), Tooltip("Blending factor.")]
  25. public float Blend = 1f;
  26. #region Editor-only stuff
  27. public int _LastSelectedCategory = 0;
  28. public int _ThumbWidth = 110;
  29. public int _ThumbHeight;
  30. int cache_ThumbWidth;
  31. int cache_ThumbHeight;
  32. bool cache_IsLinear;
  33. public RenderTexture _PreviewRT;
  34. #endregion
  35. protected Texture3D m_Lut3D;
  36. protected int m_BaseTextureIntanceID;
  37. protected bool m_Use2DLut = false;
  38. public bool IsLinear { get { return QualitySettings.activeColorSpace == ColorSpace.Linear; } }
  39. public Shader Shader2D;
  40. public Shader Shader3D;
  41. protected Material m_Material2D;
  42. protected Material m_Material3D;
  43. public Material Material
  44. {
  45. get
  46. {
  47. if (m_Use2DLut || ForceCompatibility)
  48. {
  49. if (m_Material2D == null)
  50. {
  51. m_Material2D = new Material(Shader2D);
  52. m_Material2D.hideFlags = HideFlags.HideAndDontSave;
  53. }
  54. return m_Material2D;
  55. }
  56. else
  57. {
  58. if (m_Material3D == null)
  59. {
  60. m_Material3D = new Material(Shader3D);
  61. m_Material3D.hideFlags = HideFlags.HideAndDontSave;
  62. }
  63. return m_Material3D;
  64. }
  65. }
  66. }
  67. protected virtual void Start()
  68. {
  69. // Disable if we don't support image effects
  70. if (!SystemInfo.supportsImageEffects)
  71. {
  72. Debug.LogWarning("Image effects aren't supported on this device");
  73. enabled = false;
  74. return;
  75. }
  76. // Switch to the 2D lut if the platform doesn't support 3D textures
  77. if (!SystemInfo.supports3DTextures)
  78. m_Use2DLut = true;
  79. // Disable the image effect if the shader can't run on the users graphics card
  80. if ((!m_Use2DLut && (!Shader3D || !Shader3D.isSupported)) ||
  81. ( m_Use2DLut && (!Shader2D || !Shader2D.isSupported)))
  82. {
  83. Debug.LogWarning("The shader is null or unsupported on this device");
  84. enabled = false;
  85. }
  86. }
  87. protected virtual void OnEnable()
  88. {
  89. if (LookupTexture != null && LookupTexture.GetInstanceID() != m_BaseTextureIntanceID)
  90. ConvertBaseTexture3D();
  91. }
  92. protected virtual void OnDisable()
  93. {
  94. if (m_Material2D)
  95. DestroyImmediate(m_Material2D);
  96. if (m_Material3D)
  97. DestroyImmediate(m_Material3D);
  98. if (m_Lut3D)
  99. DestroyImmediate(m_Lut3D);
  100. #if UNITY_EDITOR
  101. if (_PreviewRT)
  102. DestroyImmediate(_PreviewRT);
  103. #endif
  104. m_BaseTextureIntanceID = -1;
  105. }
  106. protected void SetIdentityLut3D()
  107. {
  108. int dim = 16;
  109. Color[] newC = new Color[dim * dim * dim];
  110. float oneOverDim = 1f / (1f * dim - 1f);
  111. for (int i = 0; i < dim; i++)
  112. {
  113. for (int j = 0; j < dim; j++)
  114. {
  115. for (int k = 0; k < dim; k++)
  116. {
  117. newC[i + (j * dim) + (k * dim * dim)] = new Color((float)i * oneOverDim, (float)j * oneOverDim, (float)k * oneOverDim, 1f);
  118. }
  119. }
  120. }
  121. if (m_Lut3D)
  122. DestroyImmediate(m_Lut3D);
  123. m_Lut3D = new Texture3D(dim, dim, dim, TextureFormat.ARGB32, false);
  124. m_Lut3D.hideFlags = HideFlags.HideAndDontSave;
  125. m_Lut3D.SetPixels(newC);
  126. m_Lut3D.Apply();
  127. m_BaseTextureIntanceID = -1;
  128. }
  129. public bool ValidDimensions(Texture2D tex2D)
  130. {
  131. if (tex2D == null || tex2D.height != Mathf.FloorToInt(Mathf.Sqrt(tex2D.width)))
  132. return false;
  133. return true;
  134. }
  135. protected void ConvertBaseTexture3D()
  136. {
  137. if (!ValidDimensions(LookupTexture))
  138. {
  139. Debug.LogWarning("The given 2D texture " + LookupTexture.name + " cannot be used as a LUT. Pick another texture or adjust dimension to e.g. 256x16.");
  140. return;
  141. }
  142. m_BaseTextureIntanceID = LookupTexture.GetInstanceID();
  143. int dim = LookupTexture.height;
  144. Color[] c = LookupTexture.GetPixels();
  145. Color[] newC = new Color[c.Length];
  146. for (int i = 0; i < dim; i++)
  147. {
  148. for (int j = 0; j < dim; j++)
  149. {
  150. for (int k = 0; k < dim; k++)
  151. {
  152. int j_ = dim - j - 1;
  153. newC[i + (j * dim) + (k * dim * dim)] = c[k * dim + i + j_ * dim * dim];
  154. }
  155. }
  156. }
  157. if (m_Lut3D)
  158. DestroyImmediate(m_Lut3D);
  159. m_Lut3D = new Texture3D(dim, dim, dim, TextureFormat.ARGB32, false);
  160. m_Lut3D.hideFlags = HideFlags.HideAndDontSave;
  161. m_Lut3D.wrapMode = TextureWrapMode.Clamp;
  162. m_Lut3D.SetPixels(newC);
  163. m_Lut3D.Apply();
  164. }
  165. #if UNITY_EDITOR
  166. bool RebuildPreview(int w, int h)
  167. {
  168. _ThumbHeight = Mathf.FloorToInt((float)_ThumbWidth / ((float)w / (float)h));
  169. if (_ThumbWidth != cache_ThumbWidth || cache_ThumbHeight != _ThumbHeight || cache_IsLinear != IsLinear)
  170. {
  171. cache_ThumbWidth = _ThumbWidth;
  172. cache_ThumbHeight = _ThumbHeight;
  173. cache_IsLinear = IsLinear;
  174. return true;
  175. }
  176. if (_PreviewRT == null)
  177. return true;
  178. return false;
  179. }
  180. #endif
  181. protected virtual void OnRenderImage(RenderTexture source, RenderTexture destination)
  182. {
  183. #if UNITY_EDITOR
  184. if (RebuildPreview(source.width, source.height))
  185. {
  186. if (_PreviewRT != null)
  187. {
  188. DestroyImmediate(_PreviewRT);
  189. _PreviewRT = null;
  190. }
  191. _PreviewRT = new RenderTexture(_ThumbWidth, _ThumbHeight, 0, RenderTextureFormat.ARGB32, IsLinear ? RenderTextureReadWrite.sRGB : RenderTextureReadWrite.Linear);
  192. _PreviewRT.hideFlags = HideFlags.HideAndDontSave;
  193. _PreviewRT.Create();
  194. }
  195. Graphics.Blit(source, _PreviewRT);
  196. #endif
  197. if (LookupTexture == null || Blend <= 0f)
  198. {
  199. Graphics.Blit(source, destination);
  200. return;
  201. }
  202. int pass = 0;
  203. if (Split == SplitMode.Horizontal) pass = 2;
  204. else if (Split == SplitMode.Vertical) pass = 4;
  205. if (IsLinear) pass++;
  206. if (m_Use2DLut || ForceCompatibility)
  207. RenderLut2D(source, destination, pass);
  208. else
  209. RenderLut3D(source, destination, pass);
  210. }
  211. void RenderLut3D(RenderTexture source, RenderTexture destination, int pass)
  212. {
  213. if (LookupTexture.GetInstanceID() != m_BaseTextureIntanceID)
  214. ConvertBaseTexture3D();
  215. if (m_Lut3D == null)
  216. SetIdentityLut3D();
  217. m_Lut3D.filterMode = LutFiltering;
  218. // Uniforms
  219. float lutSize = (float)m_Lut3D.width;
  220. Material.SetTexture("_LookupTex3D", m_Lut3D);
  221. Material.SetVector("_Params", new Vector3((lutSize - 1f) / lutSize, 1f / (2f * lutSize), Blend));
  222. Graphics.Blit(source, destination, Material, pass);
  223. }
  224. void RenderLut2D(RenderTexture source, RenderTexture destination, int pass)
  225. {
  226. LookupTexture.filterMode = LutFiltering;
  227. // Uniforms
  228. float tileSize = Mathf.Sqrt((float)LookupTexture.width);
  229. Material.SetTexture("_LookupTex2D", LookupTexture);
  230. Material.SetVector("_Params", new Vector4(1f / (float)LookupTexture.width, 1f / (float)LookupTexture.height, tileSize - 1f, Blend));
  231. Graphics.Blit(source, destination, Material, pass + (LutFiltering == FilterMode.Point ? 6 : 0));
  232. }
  233. }