using System.Collections.Generic; using UnityEditor.Graphing; using UnityEditor.ShaderGraph.Drawing.Controls; using UnityEditor.ShaderGraph.Internal; using UnityEngine; namespace UnityEditor.ShaderGraph { [FormerName("UnityEditor.ShaderGraph.VoronoAbstractMaterialNode")] [Title("Procedural", "Noise", "Voronoi")] class VoronoiNode : AbstractMaterialNode, IGeneratesBodyCode, IGeneratesFunction, IMayRequireMeshUV { // 0 original version // 1 add deterministic noise option public override int latestVersion => 1; public override IEnumerable allowedNodeVersions => new int[] { 1 }; public const int UVSlotId = 0; public const int AngleOffsetSlotId = 1; public const int CellDensitySlotId = 2; public const int OutSlotId = 3; public const int CellsSlotId = 4; const string kUVSlotName = "UV"; const string kAngleOffsetSlotName = "AngleOffset"; const string kCellDensitySlotName = "CellDensity"; const string kOutSlotName = "Out"; const string kCellsSlotName = "Cells"; public VoronoiNode() { name = "Voronoi"; synonyms = new string[] { "worley noise" }; UpdateNodeAfterDeserialization(); } public enum HashType { Deterministic, LegacySine, }; static readonly string[] kHashFunctionPrefix = { "Hash_Tchou_2_2_", "Hash_LegacySine_2_2_", }; public override bool hasPreview => true; public sealed override void UpdateNodeAfterDeserialization() { AddSlot(new UVMaterialSlot(UVSlotId, kUVSlotName, kUVSlotName, UVChannel.UV0)); AddSlot(new Vector1MaterialSlot(AngleOffsetSlotId, kAngleOffsetSlotName, kAngleOffsetSlotName, SlotType.Input, 2.0f)); AddSlot(new Vector1MaterialSlot(CellDensitySlotId, kCellDensitySlotName, kCellDensitySlotName, SlotType.Input, 5.0f)); AddSlot(new Vector1MaterialSlot(OutSlotId, kOutSlotName, kOutSlotName, SlotType.Output, 0.0f)); AddSlot(new Vector1MaterialSlot(CellsSlotId, kCellsSlotName, kCellsSlotName, SlotType.Output, 0.0f)); RemoveSlotsNameNotMatching(new[] { UVSlotId, AngleOffsetSlotId, CellDensitySlotId, OutSlotId, CellsSlotId }); } [SerializeField] private HashType m_HashType = HashType.Deterministic; [EnumControl("Hash Type")] public HashType hashType { get { if (((int)m_HashType < 0) || ((int)m_HashType >= kHashFunctionPrefix.Length)) return (HashType)0; return m_HashType; } set { if (m_HashType == value) return; m_HashType = value; Dirty(ModificationScope.Graph); } } void IGeneratesFunction.GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode) { registry.RequiresIncludePath("Packages/com.unity.render-pipelines.core/ShaderLibrary/Hashes.hlsl"); var hashType = this.hashType; var hashTypeString = hashType.ToString(); var HashFunction = kHashFunctionPrefix[(int)hashType]; registry.ProvideFunction($"Unity_Voronoi_RandomVector_{hashTypeString}_$precision", s => { s.AppendLine($"$precision2 Unity_Voronoi_RandomVector_{hashTypeString}_$precision ($precision2 UV, $precision offset)"); using (s.BlockScope()) { s.AppendLine($"{HashFunction}$precision(UV, UV);"); s.AppendLine("return $precision2(sin(UV.y * offset), cos(UV.x * offset)) * 0.5 + 0.5;"); } }); registry.ProvideFunction($"Unity_Voronoi_{hashTypeString}_$precision", s => { s.AppendLine($"void Unity_Voronoi_{hashTypeString}_$precision($precision2 UV, $precision AngleOffset, $precision CellDensity, out $precision Out, out $precision Cells)"); using (s.BlockScope()) { s.AppendLine("$precision2 g = floor(UV * CellDensity);"); s.AppendLine("$precision2 f = frac(UV * CellDensity);"); s.AppendLine("$precision t = 8.0;"); s.AppendLine("$precision3 res = $precision3(8.0, 0.0, 0.0);"); s.AppendLine("for (int y = -1; y <= 1; y++)"); using (s.BlockScope()) { s.AppendLine("for (int x = -1; x <= 1; x++)"); using (s.BlockScope()) { s.AppendLine("$precision2 lattice = $precision2(x, y);"); s.AppendLine($"$precision2 offset = Unity_Voronoi_RandomVector_{hashTypeString}_$precision(lattice + g, AngleOffset);"); s.AppendLine("$precision d = distance(lattice + offset, f);"); s.AppendLine("if (d < res.x)"); using (s.BlockScope()) { s.AppendLine("res = $precision3(d, offset.x, offset.y);"); s.AppendLine("Out = res.x;"); s.AppendLine("Cells = res.y;"); } } } } }); } public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode) { var hashType = this.hashType; var hashTypeString = hashType.ToString(); string uv = GetSlotValue(UVSlotId, generationMode); string angleOffset = GetSlotValue(AngleOffsetSlotId, generationMode); string cellDensity = GetSlotValue(CellDensitySlotId, generationMode); string output = GetVariableNameForSlot(OutSlotId); string cells = GetVariableNameForSlot(CellsSlotId); sb.AppendLine($"{FindSlot(OutSlotId).concreteValueType.ToShaderString(PrecisionUtil.Token)} {output};"); sb.AppendLine($"{FindSlot(CellsSlotId).concreteValueType.ToShaderString(PrecisionUtil.Token)} {cells};"); sb.AppendLine($"Unity_Voronoi_{hashTypeString}_$precision({uv}, {angleOffset}, {cellDensity}, {output}, {cells});"); } public bool RequiresMeshUV(UVChannel channel, ShaderStageCapability stageCapability) { using (var tempSlots = PooledList.Get()) { GetInputSlots(tempSlots); var result = false; foreach (var slot in tempSlots) { if (slot.RequiresMeshUV(channel)) { result = true; break; } } tempSlots.Clear(); return result; } } public override void OnAfterMultiDeserialize(string json) { if (sgVersion < 1) { // old nodes should select "LegacySine" to replicate old behavior hashType = HashType.LegacySine; ChangeVersion(1); } } } }