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

SpeedTree8MaterialUpgrader.cs 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using System;
  4. using UnityEngine.Experimental.Rendering;
  5. namespace UnityEditor.Rendering
  6. {
  7. /// <summary>
  8. /// Material upgrader and relevant utilities for SpeedTree 8.
  9. /// </summary>
  10. public class SpeedTree8MaterialUpgrader : MaterialUpgrader
  11. {
  12. private enum WindQuality
  13. {
  14. None = 0,
  15. Fastest,
  16. Fast,
  17. Better,
  18. Best,
  19. Palm,
  20. Count
  21. }
  22. private static string[] WindQualityString =
  23. {
  24. "_WINDQUALITY_NONE",
  25. "_WINDQUALITY_FASTEST",
  26. "_WINDQUALITY_FAST",
  27. "_WINDQUALITY_BETTER",
  28. "_WINDQUALITY_BEST",
  29. "_WINDQUALITY_PALM"
  30. };
  31. static private class Uniforms
  32. {
  33. internal static int _WINDQUALITY = Shader.PropertyToID("_WINDQUALITY");
  34. internal static int EFFECT_BILLBOARD = Shader.PropertyToID("EFFECT_BILLBOARD");
  35. internal static int EFFECT_EXTRA_TEX = Shader.PropertyToID("EFFECT_EXTRA_TEX");
  36. internal static int _TwoSided = Shader.PropertyToID("_TwoSided");
  37. internal static int _WindQuality = Shader.PropertyToID("_WindQuality");
  38. }
  39. /// <summary>
  40. /// Returns true if the material contains a SpeedTree Wind keyword.
  41. /// </summary>
  42. /// <param name="material">Material to check</param>
  43. /// <returns> true if the material has a SpeedTree wind keyword that enables Vertex Shader wind animation </returns>
  44. public static bool DoesMaterialHaveSpeedTreeWindKeyword(Material material)
  45. {
  46. foreach(string keyword in WindQualityString)
  47. if(material.IsKeywordEnabled(keyword))
  48. return true;
  49. return false;
  50. }
  51. /// <summary>
  52. /// Checks the material for SpeedTree keywords to determine if the wind is enabled.
  53. /// </summary>
  54. /// <param name="material">Material to check</param>
  55. /// <returns> true if the material has a SpeedTree wind keyword that enables Vertex Shader wind animation and WindQuality other than None (0) </returns>
  56. public static bool IsWindEnabled(Material material)
  57. {
  58. return HasWindEnabledKeyword(material) && HasWindQualityPropertyEnabled(material);
  59. }
  60. private static bool HasWindEnabledKeyword(Material material)
  61. {
  62. for(int i=1/*skip NONE*/; i<WindQualityString.Length; ++i)
  63. {
  64. if(material.IsKeywordEnabled(WindQualityString[i]))
  65. return true;
  66. }
  67. return false;
  68. }
  69. private static bool HasWindQualityPropertyEnabled(Material material)
  70. {
  71. return material.HasProperty("_WindQuality") && material.GetFloat(Uniforms._WindQuality) > 0.0f;
  72. }
  73. /// <summary>
  74. /// Creates a material upgrader that handles the property renames that HD and Universal have in common when upgrading
  75. /// from the built-in SpeedTree 8 shader.
  76. /// </summary>
  77. /// <param name="sourceShaderName">Original SpeedTree8 shader name.</param>
  78. /// <param name="destShaderName">New SpeedTree 8 shader name.</param>
  79. /// <param name="finalizer">A delegate that postprocesses the material for the render pipeline in use.</param>
  80. public SpeedTree8MaterialUpgrader(string sourceShaderName, string destShaderName, MaterialFinalizer finalizer = null)
  81. {
  82. RenameShader(sourceShaderName, destShaderName, finalizer);
  83. RenameFloat("_WindQuality", "_WINDQUALITY");
  84. RenameFloat("_BillboardKwToggle", "EFFECT_BILLBOARD");
  85. RenameKeywordToFloat("EFFECT_EXTRA_TEX", "EFFECT_EXTRA_TEX", 1, 0);
  86. RenameKeywordToFloat("EFFECT_SUBSURFACE", "_SubsurfaceKwToggle", 1, 0);
  87. RenameKeywordToFloat("EFFECT_BUMP", "_NormalMapKwToggle", 1, 0);
  88. RenameKeywordToFloat("EFFECT_HUE_VARIATION", "_HueVariationKwToggle", 1, 0);
  89. }
  90. /// <summary>
  91. /// Postprocesses materials while you are importing a SpeedTree 8 asset. Call from OnPostprocessSpeedTree in a MaterialPostprocessor.
  92. /// </summary>
  93. /// <param name="speedtree">The GameObject Unity creates from this imported SpeedTree.</param>
  94. /// <param name="stImporter">The asset importer used to import this SpeedTree asset.</param>
  95. /// <param name="finalizer">Render pipeline-specific material finalizer.</param>
  96. public static void PostprocessSpeedTree8Materials(GameObject speedtree, SpeedTreeImporter stImporter, MaterialFinalizer finalizer = null)
  97. {
  98. LODGroup lg = speedtree.GetComponent<LODGroup>();
  99. LOD[] lods = lg.GetLODs();
  100. for (int l = 0; l < lods.Length; l++)
  101. {
  102. LOD lod = lods[l];
  103. bool isBillboard = stImporter.hasBillboard && (l == lods.Length - 1);
  104. int wq = Mathf.Min(stImporter.windQualities[l], stImporter.bestWindQuality);
  105. foreach (Renderer r in lod.renderers)
  106. {
  107. foreach (Material m in r.sharedMaterials)
  108. {
  109. if (m == null)
  110. continue;
  111. float cutoff = stImporter.alphaTestRef;
  112. int cullmode = isBillboard ? 2 : 0;
  113. m.SetFloat(Uniforms._WINDQUALITY, wq);
  114. if (isBillboard)
  115. {
  116. m.SetFloat(Uniforms.EFFECT_BILLBOARD, 1.0f);
  117. }
  118. m.SetFloat(Uniforms._TwoSided, cullmode); // Temporary; Finalizer should read from this and apply the value to a pipeline-specific cull property
  119. if (m.IsKeywordEnabled("EFFECT_EXTRA_TEX"))
  120. m.SetFloat(Uniforms.EFFECT_EXTRA_TEX, 1.0f);
  121. if (finalizer != null)
  122. finalizer(m);
  123. }
  124. }
  125. }
  126. }
  127. /// <summary>
  128. /// Preserves wind quality and billboard settings while you are upgrading a SpeedTree 8 material from previous versions of SpeedTree 8.
  129. /// Wind priority order is _WindQuality float value > enabled keyword.
  130. /// Should work for upgrading versions within a pipeline and from standard to current pipeline.
  131. /// </summary>
  132. /// <param name="material">SpeedTree 8 material to upgrade.</param>
  133. public static void SpeedTree8MaterialFinalizer(Material material)
  134. {
  135. UpgradeWindQuality(material);
  136. }
  137. private static void UpgradeWindQuality(Material material, int windQuality = -1)
  138. {
  139. int wq = GetWindQuality(material, windQuality);
  140. SetWindQuality(material, wq);
  141. }
  142. private static int GetWindQuality(Material material, int windQuality = -1)
  143. {
  144. // Conservative wind quality priority:
  145. // input WindQuality > enabled keyword > _WindQuality float value
  146. if (!WindIntValid(windQuality))
  147. {
  148. windQuality = material.HasProperty(Uniforms._WindQuality) ? (int)material.GetFloat(Uniforms._WindQuality) : 0;
  149. if (!WindIntValid(windQuality))
  150. {
  151. windQuality = GetWindQualityFromKeywords(material.shaderKeywords);
  152. if (!WindIntValid(windQuality))
  153. windQuality = 0;
  154. }
  155. }
  156. return windQuality;
  157. }
  158. private static void ClearWindKeywords(Material material)
  159. {
  160. if (material == null)
  161. return;
  162. for (int i = 0; i < (int)WindQuality.Count; i++)
  163. {
  164. material.DisableKeyword(WindQualityString[i]);
  165. }
  166. }
  167. private static void SetWindQuality(Material material, int windQuality)
  168. {
  169. Debug.Assert(WindIntValid(windQuality), "Attempting to set invalid wind quality on material " + material.name);
  170. if (material == null)
  171. return;
  172. if (windQuality != GetWindQualityFromKeywords(material.shaderKeywords))
  173. {
  174. ClearWindKeywords(material);
  175. }
  176. material.EnableKeyword(WindQualityString[windQuality]);
  177. material.SetFloat(Uniforms._WindQuality, windQuality); // A legacy float used in native code to apply wind data
  178. if (material.HasProperty("_WINDQUALITY"))
  179. material.SetFloat(Uniforms._WINDQUALITY, windQuality); // The actual name of the keyword enum for the shadergraph
  180. }
  181. private static int GetWindQualityFromKeywords(string[] matKws)
  182. {
  183. foreach (string kw in matKws)
  184. {
  185. if (kw.StartsWith("_WINDQUALITY_"))
  186. {
  187. for (int i = 0; i < (int)WindQuality.Count; i++)
  188. {
  189. if (kw.EndsWith(WindQualityString[i]))
  190. return i;
  191. }
  192. }
  193. }
  194. return -1;
  195. }
  196. private static bool WindIntValid(int windInt)
  197. {
  198. return ((int)WindQuality.None <= windInt) && (windInt < (int)WindQuality.Count);
  199. }
  200. }
  201. }