No Description
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.

TriplanarNode.cs 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. using System.Linq;
  2. using UnityEngine;
  3. using UnityEditor.Graphing;
  4. using UnityEditor.ShaderGraph.Drawing.Controls;
  5. using UnityEditor.ShaderGraph.Internal;
  6. namespace UnityEditor.ShaderGraph
  7. {
  8. [Title("UV", "Triplanar")]
  9. class TriplanarNode : AbstractMaterialNode, IGeneratesBodyCode, IMayRequirePosition, IMayRequireNormal, IMayRequireTangent, IMayRequireBitangent
  10. {
  11. public const int OutputSlotId = 0;
  12. public const int TextureInputId = 1;
  13. public const int SamplerInputId = 2;
  14. public const int PositionInputId = 3;
  15. public const int NormalInputId = 4;
  16. public const int TileInputId = 5;
  17. public const int BlendInputId = 6;
  18. const string kOutputSlotName = "Out";
  19. const string kTextureInputName = "Texture";
  20. const string kSamplerInputName = "Sampler";
  21. const string kPositionInputName = "Position";
  22. const string kNormalInputName = "Normal";
  23. const string kTileInputName = "Tile";
  24. const string kBlendInputName = "Blend";
  25. PositionMaterialSlot positionSlot => FindInputSlot<PositionMaterialSlot>(PositionInputId);
  26. NormalMaterialSlot normalSlot => FindInputSlot<NormalMaterialSlot>(NormalInputId);
  27. public override bool hasPreview { get { return true; } }
  28. public TriplanarNode()
  29. {
  30. name = "Triplanar";
  31. synonyms = new string[] { "project" };
  32. m_PreviewMode = PreviewMode.Preview3D;
  33. UpdateNodeAfterDeserialization();
  34. }
  35. [SerializeField]
  36. private TextureType m_TextureType = TextureType.Default;
  37. [EnumControl("Type")]
  38. public TextureType textureType
  39. {
  40. get { return m_TextureType; }
  41. set
  42. {
  43. if (m_TextureType == value)
  44. return;
  45. m_TextureType = value;
  46. Dirty(ModificationScope.Graph);
  47. ValidateNode();
  48. }
  49. }
  50. [SerializeField]
  51. private CoordinateSpace m_InputSpace = CoordinateSpace.AbsoluteWorld;
  52. public CoordinateSpace inputSpace
  53. {
  54. get { return m_InputSpace; }
  55. set
  56. {
  57. if (m_InputSpace == value)
  58. return;
  59. m_InputSpace = value;
  60. Setup();
  61. Dirty(ModificationScope.Topological); // needed to update slot views
  62. ValidateNode();
  63. }
  64. }
  65. [SerializeField]
  66. private CoordinateSpace m_NormalOutputSpace = CoordinateSpace.Tangent;
  67. public CoordinateSpace normalOutputSpace
  68. {
  69. get { return m_NormalOutputSpace; }
  70. set
  71. {
  72. if (m_NormalOutputSpace == value)
  73. return;
  74. m_NormalOutputSpace = value;
  75. Dirty(ModificationScope.Graph);
  76. ValidateNode();
  77. }
  78. }
  79. internal SpaceTransform normalTransform =>
  80. new SpaceTransform(inputSpace, normalOutputSpace, ConversionType.Normal, normalize: true);
  81. public sealed override void UpdateNodeAfterDeserialization()
  82. {
  83. AddSlot(new Vector4MaterialSlot(OutputSlotId, kOutputSlotName, kOutputSlotName, SlotType.Output, Vector4.zero, ShaderStageCapability.Fragment));
  84. AddSlot(new Texture2DInputMaterialSlot(TextureInputId, kTextureInputName, kTextureInputName));
  85. AddSlot(new SamplerStateMaterialSlot(SamplerInputId, kSamplerInputName, kSamplerInputName, SlotType.Input));
  86. AddSlot(new PositionMaterialSlot(PositionInputId, kPositionInputName, kPositionInputName, inputSpace));
  87. AddSlot(new NormalMaterialSlot(NormalInputId, kNormalInputName, kNormalInputName, inputSpace));
  88. AddSlot(new Vector1MaterialSlot(TileInputId, kTileInputName, kTileInputName, SlotType.Input, 1));
  89. AddSlot(new Vector1MaterialSlot(BlendInputId, kBlendInputName, kBlendInputName, SlotType.Input, 1));
  90. RemoveSlotsNameNotMatching(new[] { OutputSlotId, TextureInputId, SamplerInputId, PositionInputId, NormalInputId, TileInputId, BlendInputId });
  91. }
  92. public override void Setup()
  93. {
  94. base.Setup();
  95. var textureSlot = FindInputSlot<Texture2DInputMaterialSlot>(TextureInputId);
  96. textureSlot.defaultType = (textureType == TextureType.Normal ? Texture2DShaderProperty.DefaultType.NormalMap : Texture2DShaderProperty.DefaultType.White);
  97. positionSlot.space = inputSpace;
  98. normalSlot.space = inputSpace;
  99. }
  100. // Node generations
  101. public virtual void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
  102. {
  103. sb.AppendLine("$precision3 {0}_UV = {1} * {2};", GetVariableNameForNode(),
  104. GetSlotValue(PositionInputId, generationMode), GetSlotValue(TileInputId, generationMode));
  105. //Sampler input slot
  106. var samplerSlot = FindInputSlot<MaterialSlot>(SamplerInputId);
  107. var edgesSampler = owner.GetEdges(samplerSlot.slotReference);
  108. var id = GetSlotValue(TextureInputId, generationMode);
  109. switch (textureType)
  110. {
  111. // Whiteout blend method
  112. // https://medium.com/@bgolus/normal-mapping-for-a-triplanar-shader-10bf39dca05a
  113. case TextureType.Normal:
  114. // See comment for default case.
  115. sb.AppendLine("$precision3 {0}_Blend = SafePositivePow_$precision({1}, min({2}, floor(log2(Min_$precision())/log2(1/sqrt(3)))) );"
  116. , GetVariableNameForNode()
  117. , GetSlotValue(NormalInputId, generationMode)
  118. , GetSlotValue(BlendInputId, generationMode));
  119. sb.AppendLine("{0}_Blend /= ({0}_Blend.x + {0}_Blend.y + {0}_Blend.z ).xxx;", GetVariableNameForNode());
  120. sb.AppendLine("$precision3 {0}_X = UnpackNormal(SAMPLE_TEXTURE2D({1}.tex, {2}.samplerstate, {0}_UV.zy));"
  121. , GetVariableNameForNode()
  122. , id
  123. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : id);
  124. sb.AppendLine("$precision3 {0}_Y = UnpackNormal(SAMPLE_TEXTURE2D({1}.tex, {2}.samplerstate, {0}_UV.xz));"
  125. , GetVariableNameForNode()
  126. , id
  127. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : id);
  128. sb.AppendLine("$precision3 {0}_Z = UnpackNormal(SAMPLE_TEXTURE2D({1}.tex, {2}.samplerstate, {0}_UV.xy));"
  129. , GetVariableNameForNode()
  130. , id
  131. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : id);
  132. sb.AppendLine("{0}_X = $precision3({0}_X.xy + {1}.zy, abs({0}_X.z) * {1}.x);"
  133. , GetVariableNameForNode()
  134. , GetSlotValue(NormalInputId, generationMode));
  135. sb.AppendLine("{0}_Y = $precision3({0}_Y.xy + {1}.xz, abs({0}_Y.z) * {1}.y);"
  136. , GetVariableNameForNode()
  137. , GetSlotValue(NormalInputId, generationMode));
  138. sb.AppendLine("{0}_Z = $precision3({0}_Z.xy + {1}.xy, abs({0}_Z.z) * {1}.z);"
  139. , GetVariableNameForNode()
  140. , GetSlotValue(NormalInputId, generationMode));
  141. var outputVariable = GetVariableNameForSlot(OutputSlotId);
  142. sb.AppendLine("$precision4 {0} = $precision4({1}_X.zyx * {1}_Blend.x + {1}_Y.xzy * {1}_Blend.y + {1}_Z.xyz * {1}_Blend.z, 1);"
  143. , outputVariable
  144. , GetVariableNameForNode());
  145. // transform the normal from input to output space, and normalize
  146. outputVariable = $"{outputVariable}.rgb";
  147. SpaceTransformUtil.GenerateTransformCodeStatement(normalTransform, outputVariable, outputVariable, sb);
  148. break;
  149. default:
  150. // We want the sum of the 3 blend weights (by which we normalize them) to be > 0.
  151. // Max safe exponent is log2(REAL_MIN)/log2(1/sqrt(3)):
  152. // Take the set of all possible normalized vectors, make a set from selecting the maximum component of each 3-vectors from the previous set,
  153. // the minimum (:= min_of_max) of that new set is 1/sqrt(3) (by the fact vectors are normalized).
  154. // We then want a maximum exponent such that
  155. // precision_min < min_of_max^exponent_max
  156. // where exponent_max is blend,
  157. // log(precision_min) < log(min_of_max) * exponent_max
  158. // log(precision_min) / log(min_of_max) > exponent_max
  159. sb.AppendLine("$precision3 {0}_Blend = SafePositivePow_$precision({1}, min({2}, floor(log2(Min_$precision())/log2(1/sqrt(3)))) );"
  160. , GetVariableNameForNode()
  161. , GetSlotValue(NormalInputId, generationMode)
  162. , GetSlotValue(BlendInputId, generationMode));
  163. sb.AppendLine("{0}_Blend /= dot({0}_Blend, 1.0);", GetVariableNameForNode());
  164. sb.AppendLine("$precision4 {0}_X = SAMPLE_TEXTURE2D({1}.tex, {2}.samplerstate, {0}_UV.zy);"
  165. , GetVariableNameForNode()
  166. , id
  167. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : id);
  168. sb.AppendLine("$precision4 {0}_Y = SAMPLE_TEXTURE2D({1}.tex, {2}.samplerstate, {0}_UV.xz);"
  169. , GetVariableNameForNode()
  170. , id
  171. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : id);
  172. sb.AppendLine("$precision4 {0}_Z = SAMPLE_TEXTURE2D({1}.tex, {2}.samplerstate, {0}_UV.xy);"
  173. , GetVariableNameForNode()
  174. , id
  175. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : id);
  176. sb.AppendLine("$precision4 {0} = {1}_X * {1}_Blend.x + {1}_Y * {1}_Blend.y + {1}_Z * {1}_Blend.z;"
  177. , GetVariableNameForSlot(OutputSlotId)
  178. , GetVariableNameForNode());
  179. break;
  180. }
  181. }
  182. public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability)
  183. {
  184. return positionSlot.RequiresPosition();
  185. }
  186. public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability)
  187. {
  188. NeededCoordinateSpace neededSpaces = normalSlot.RequiresNormal();
  189. if (m_TextureType == TextureType.Normal)
  190. neededSpaces |= normalTransform.RequiresNormal;
  191. return neededSpaces;
  192. }
  193. public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability)
  194. {
  195. NeededCoordinateSpace neededSpaces = NeededCoordinateSpace.None;
  196. if (m_TextureType == TextureType.Normal)
  197. neededSpaces |= normalTransform.RequiresTangent;
  198. return neededSpaces;
  199. }
  200. public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability)
  201. {
  202. NeededCoordinateSpace neededSpaces = NeededCoordinateSpace.None;
  203. if (m_TextureType == TextureType.Normal)
  204. neededSpaces |= normalTransform.RequiresBitangent;
  205. return neededSpaces;
  206. }
  207. }
  208. }