using System; using System.Collections.Generic; using UnityEditor.Graphing; using UnityEditor.ShaderGraph.Drawing.Controls; using UnityEditor.ShaderGraph.Internal; using UnityEngine; namespace UnityEditor.ShaderGraph { enum OutputSpace { Tangent, World }; [Title("Artistic", "Normal", "Normal From Height")] class NormalFromHeightNode : AbstractMaterialNode, IGeneratesBodyCode, IGeneratesFunction, IMayRequireTangent, IMayRequireBitangent, IMayRequireNormal, IMayRequirePosition { public NormalFromHeightNode() { name = "Normal From Height"; synonyms = new string[] { "convert to normal", "bump map" }; UpdateNodeAfterDeserialization(); } [SerializeField] private OutputSpace m_OutputSpace = OutputSpace.Tangent; [EnumControl("Output Space")] public OutputSpace outputSpace { get { return m_OutputSpace; } set { if (m_OutputSpace == value) return; m_OutputSpace = value; Dirty(ModificationScope.Graph); } } const int InputSlotId = 0; const int StrengthSlotId = 2; const int OutputSlotId = 1; const string kInputSlotName = "In"; const string kStrengthSlotName = "Strength"; const string kOutputSlotName = "Out"; public override bool hasPreview { get { return true; } } string GetFunctionName() { return $"Unity_NormalFromHeight_{outputSpace.ToString()}_$precision"; } public sealed override void UpdateNodeAfterDeserialization() { AddSlot(new Vector1MaterialSlot(InputSlotId, kInputSlotName, kInputSlotName, SlotType.Input, 0)); AddSlot(new Vector1MaterialSlot(StrengthSlotId, kStrengthSlotName, kStrengthSlotName, SlotType.Input, 0.01f)); AddSlot(new Vector3MaterialSlot(OutputSlotId, kOutputSlotName, kOutputSlotName, SlotType.Output, Vector4.zero, ShaderStageCapability.Fragment)); RemoveSlotsNameNotMatching(new[] { InputSlotId, StrengthSlotId, OutputSlotId }); } public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode) { var inputValue = GetSlotValue(InputSlotId, generationMode); var strengthValue = GetSlotValue(StrengthSlotId, generationMode); var outputValue = GetSlotValue(OutputSlotId, generationMode); sb.AppendLine("{0} {1};", FindOutputSlot(OutputSlotId).concreteValueType.ToShaderString(), GetVariableNameForSlot(OutputSlotId)); sb.AppendLine("$precision3x3 _{0}_TangentMatrix = $precision3x3(IN.{1}SpaceTangent, IN.{1}SpaceBiTangent, IN.{1}SpaceNormal);", GetVariableNameForNode(), NeededCoordinateSpace.World.ToString()); sb.AppendLine("$precision3 _{0}_Position = IN.{1}SpacePosition;", GetVariableNameForNode(), NeededCoordinateSpace.World.ToString()); sb.AppendLine("{0}({1},{2},_{3}_Position,_{3}_TangentMatrix, {4});", GetFunctionName(), inputValue, strengthValue, GetVariableNameForNode(), outputValue); } public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode) { registry.ProvideFunction(GetFunctionName(), s => { s.AppendLine("void {0}({1} In, {2} Strength, $precision3 Position, $precision3x3 TangentMatrix, out {3} Out)", GetFunctionName(), FindInputSlot(InputSlotId).concreteValueType.ToShaderString(), FindInputSlot(StrengthSlotId).concreteValueType.ToShaderString(), FindOutputSlot(OutputSlotId).concreteValueType.ToShaderString()); using (s.BlockScope()) { s.AppendLine(GetRayTracingError()); s.AppendLine("$precision3 worldDerivativeX = ddx(Position);"); s.AppendLine("$precision3 worldDerivativeY = ddy(Position);"); s.AppendNewLine(); s.AppendLine("$precision3 crossX = cross(TangentMatrix[2].xyz, worldDerivativeX);"); s.AppendLine("$precision3 crossY = cross(worldDerivativeY, TangentMatrix[2].xyz);"); s.AppendLine("$precision d = dot(worldDerivativeX, crossY);"); s.AppendLine("$precision sgn = d < 0.0 ? (-1.0f) : 1.0f;"); s.AppendLine("$precision surface = sgn / max({0}, abs(d));", concretePrecision == ConcretePrecision.Single ? "0.000000000000001192093f" : "0.00006103515625f"); s.AppendNewLine(); s.AppendLine("$precision dHdx = ddx(In);"); s.AppendLine("$precision dHdy = ddy(In);"); s.AppendLine("$precision3 surfGrad = surface * (dHdx*crossY + dHdy*crossX);"); s.AppendLine("Out = SafeNormalize(TangentMatrix[2].xyz - (Strength * surfGrad));"); if (outputSpace == OutputSpace.Tangent) s.AppendLine("Out = TransformWorldToTangent(Out, TangentMatrix);"); } }); } public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability) { return NeededCoordinateSpace.World; } public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability) { return NeededCoordinateSpace.World; } public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability) { return NeededCoordinateSpace.World; } public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability) { return NeededCoordinateSpace.World; } } }