TriplanarNode.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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. public override bool hasPreview { get { return true; } }
  26. public override PreviewMode previewMode
  27. {
  28. get { return PreviewMode.Preview3D; }
  29. }
  30. public TriplanarNode()
  31. {
  32. name = "Triplanar";
  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. public sealed override void UpdateNodeAfterDeserialization()
  51. {
  52. AddSlot(new Vector4MaterialSlot(OutputSlotId, kOutputSlotName, kOutputSlotName, SlotType.Output, Vector4.zero, ShaderStageCapability.Fragment));
  53. AddSlot(new Texture2DInputMaterialSlot(TextureInputId, kTextureInputName, kTextureInputName));
  54. AddSlot(new SamplerStateMaterialSlot(SamplerInputId, kSamplerInputName, kSamplerInputName, SlotType.Input));
  55. AddSlot(new PositionMaterialSlot(PositionInputId, kPositionInputName, kPositionInputName, CoordinateSpace.AbsoluteWorld));
  56. AddSlot(new NormalMaterialSlot(NormalInputId, kNormalInputName, kNormalInputName, CoordinateSpace.World));
  57. AddSlot(new Vector1MaterialSlot(TileInputId, kTileInputName, kTileInputName, SlotType.Input, 1));
  58. AddSlot(new Vector1MaterialSlot(BlendInputId, kBlendInputName, kBlendInputName, SlotType.Input, 1));
  59. RemoveSlotsNameNotMatching(new[] { OutputSlotId, TextureInputId, SamplerInputId, PositionInputId, NormalInputId, TileInputId, BlendInputId });
  60. }
  61. public override void ValidateNode()
  62. {
  63. var textureSlot = FindInputSlot<Texture2DInputMaterialSlot>(TextureInputId);
  64. textureSlot.defaultType = (textureType == TextureType.Normal ? Texture2DShaderProperty.DefaultType.Bump : Texture2DShaderProperty.DefaultType.White);
  65. base.ValidateNode();
  66. }
  67. // Node generations
  68. public virtual void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
  69. {
  70. sb.AppendLine("$precision3 {0}_UV = {1} * {2};", GetVariableNameForNode(),
  71. GetSlotValue(PositionInputId, generationMode), GetSlotValue(TileInputId, generationMode));
  72. //Sampler input slot
  73. var samplerSlot = FindInputSlot<MaterialSlot>(SamplerInputId);
  74. var edgesSampler = owner.GetEdges(samplerSlot.slotReference);
  75. var id = GetSlotValue(TextureInputId, generationMode);
  76. switch (textureType)
  77. {
  78. // Whiteout blend method
  79. // https://medium.com/@bgolus/normal-mapping-for-a-triplanar-shader-10bf39dca05a
  80. case TextureType.Normal:
  81. sb.AppendLine("$precision3 {0}_Blend = max(pow(abs({1}), {2}), 0);"
  82. , GetVariableNameForNode()
  83. , GetSlotValue(NormalInputId, generationMode)
  84. , GetSlotValue(BlendInputId, generationMode));
  85. sb.AppendLine("{0}_Blend /= ({0}_Blend.x + {0}_Blend.y + {0}_Blend.z ).xxx;", GetVariableNameForNode());
  86. sb.AppendLine("$precision3 {0}_X = UnpackNormal(SAMPLE_TEXTURE2D({1}, {2}, {0}_UV.zy));"
  87. , GetVariableNameForNode()
  88. , id
  89. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : "sampler" + id);
  90. sb.AppendLine("$precision3 {0}_Y = UnpackNormal(SAMPLE_TEXTURE2D({1}, {2}, {0}_UV.xz));"
  91. , GetVariableNameForNode()
  92. , id
  93. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : "sampler" + id);
  94. sb.AppendLine("$precision3 {0}_Z = UnpackNormal(SAMPLE_TEXTURE2D({1}, {2}, {0}_UV.xy));"
  95. , GetVariableNameForNode()
  96. , id
  97. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : "sampler" + id);
  98. sb.AppendLine("{0}_X = $precision3({0}_X.xy + {1}.zy, abs({0}_X.z) * {1}.x);"
  99. , GetVariableNameForNode()
  100. , GetSlotValue(NormalInputId, generationMode));
  101. sb.AppendLine("{0}_Y = $precision3({0}_Y.xy + {1}.xz, abs({0}_Y.z) * {1}.y);"
  102. , GetVariableNameForNode()
  103. , GetSlotValue(NormalInputId, generationMode));
  104. sb.AppendLine("{0}_Z = $precision3({0}_Z.xy + {1}.xy, abs({0}_Z.z) * {1}.z);"
  105. , GetVariableNameForNode()
  106. , GetSlotValue(NormalInputId, generationMode));
  107. sb.AppendLine("$precision4 {0} = $precision4(normalize({1}_X.zyx * {1}_Blend.x + {1}_Y.xzy * {1}_Blend.y + {1}_Z.xyz * {1}_Blend.z), 1);"
  108. , GetVariableNameForSlot(OutputSlotId)
  109. , GetVariableNameForNode());
  110. sb.AppendLine("$precision3x3 {0}_Transform = $precision3x3(IN.WorldSpaceTangent, IN.WorldSpaceBiTangent, IN.WorldSpaceNormal);", GetVariableNameForNode());
  111. sb.AppendLine("{0}.rgb = TransformWorldToTangent({0}.rgb, {1}_Transform);"
  112. , GetVariableNameForSlot(OutputSlotId)
  113. , GetVariableNameForNode());
  114. break;
  115. default:
  116. sb.AppendLine("$precision3 {0}_Blend = pow(abs({1}), {2});"
  117. , GetVariableNameForNode()
  118. , GetSlotValue(NormalInputId, generationMode)
  119. , GetSlotValue(BlendInputId, generationMode));
  120. sb.AppendLine("{0}_Blend /= dot({0}_Blend, 1.0);", GetVariableNameForNode());
  121. sb.AppendLine("$precision4 {0}_X = SAMPLE_TEXTURE2D({1}, {2}, {0}_UV.zy);"
  122. , GetVariableNameForNode()
  123. , id
  124. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : "sampler" + id);
  125. sb.AppendLine("$precision4 {0}_Y = SAMPLE_TEXTURE2D({1}, {2}, {0}_UV.xz);"
  126. , GetVariableNameForNode()
  127. , id
  128. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : "sampler" + id);
  129. sb.AppendLine("$precision4 {0}_Z = SAMPLE_TEXTURE2D({1}, {2}, {0}_UV.xy);"
  130. , GetVariableNameForNode()
  131. , id
  132. , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : "sampler" + id);
  133. sb.AppendLine("$precision4 {0} = {1}_X * {1}_Blend.x + {1}_Y * {1}_Blend.y + {1}_Z * {1}_Blend.z;"
  134. , GetVariableNameForSlot(OutputSlotId)
  135. , GetVariableNameForNode());
  136. break;
  137. }
  138. }
  139. public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability)
  140. {
  141. return CoordinateSpace.AbsoluteWorld.ToNeededCoordinateSpace() | CoordinateSpace.World.ToNeededCoordinateSpace();
  142. }
  143. public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability)
  144. {
  145. return CoordinateSpace.World.ToNeededCoordinateSpace();
  146. }
  147. public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability)
  148. {
  149. switch (m_TextureType)
  150. {
  151. case TextureType.Normal:
  152. return CoordinateSpace.World.ToNeededCoordinateSpace();
  153. default:
  154. return NeededCoordinateSpace.None;
  155. }
  156. }
  157. public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability)
  158. {
  159. switch (m_TextureType)
  160. {
  161. case TextureType.Normal:
  162. return CoordinateSpace.World.ToNeededCoordinateSpace();
  163. default:
  164. return NeededCoordinateSpace.None;
  165. }
  166. }
  167. }
  168. }