DitheringNode.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. // Amplify Shader Editor - Visual Shader Editing Tool
  2. // Copyright (c) Amplify Creations, Lda <info@amplify.pt>
  3. using UnityEngine;
  4. using UnityEditor;
  5. using System;
  6. namespace AmplifyShaderEditor
  7. {
  8. [Serializable]
  9. [NodeAttributes( "Dither", "Camera And Screen", "Generates a dithering pattern" )]
  10. public sealed class DitheringNode : ParentNode
  11. {
  12. private string m_functionHeader = "Dither4x4Bayer( {0}, {1} )";
  13. private string m_functionBody = string.Empty;
  14. [SerializeField]
  15. private int m_selectedPatternInt = 0;
  16. [SerializeField]
  17. private bool m_customScreenPos = false;
  18. private readonly string[] PatternsFuncStr = { "4x4Bayer", "8x8Bayer", "NoiseTex" };
  19. private readonly string[] PatternsStr = { "4x4 Bayer", "8x8 Bayer", "Noise Texture" };
  20. private UpperLeftWidgetHelper m_upperLeftWidget = new UpperLeftWidgetHelper();
  21. private InputPort m_texPort;
  22. private InputPort m_ssPort;
  23. protected override void CommonInit( int uniqueId )
  24. {
  25. base.CommonInit( uniqueId );
  26. AddInputPort( WirePortDataType.FLOAT, false, Constants.EmptyPortValue );
  27. AddInputPort( WirePortDataType.SAMPLER2D, false, "Pattern");
  28. m_inputPorts[ 1 ].CreatePortRestrictions( WirePortDataType.SAMPLER2D );
  29. m_texPort = m_inputPorts[ 1 ];
  30. AddInputPort( WirePortDataType.FLOAT4, false, "Screen Position" );
  31. AddInputPort( WirePortDataType.SAMPLERSTATE, false, "SS" );
  32. m_inputPorts[ 3 ].CreatePortRestrictions( WirePortDataType.SAMPLERSTATE );
  33. m_ssPort = m_inputPorts[ 3 ];
  34. AddOutputPort( WirePortDataType.FLOAT, Constants.EmptyPortValue );
  35. m_textLabelWidth = 110;
  36. m_autoWrapProperties = true;
  37. m_hasLeftDropdown = true;
  38. SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, PatternsStr[ m_selectedPatternInt ] ) );
  39. UpdatePorts();
  40. }
  41. public override void Destroy()
  42. {
  43. base.Destroy();
  44. m_upperLeftWidget = null;
  45. m_texPort = null;
  46. m_ssPort = null;
  47. }
  48. public override void AfterCommonInit()
  49. {
  50. base.AfterCommonInit();
  51. if( PaddingTitleLeft == 0 )
  52. {
  53. PaddingTitleLeft = Constants.PropertyPickerWidth + Constants.IconsLeftRightMargin;
  54. if( PaddingTitleRight == 0 )
  55. PaddingTitleRight = Constants.PropertyPickerWidth + Constants.IconsLeftRightMargin;
  56. }
  57. }
  58. public override void OnConnectedOutputNodeChanges( int outputPortId, int otherNodeId, int otherPortId, string name, WirePortDataType type )
  59. {
  60. base.OnConnectedOutputNodeChanges( outputPortId, otherNodeId, otherPortId, name, type );
  61. if( !m_texPort.CheckValidType( type ) )
  62. {
  63. m_texPort.FullDeleteConnections();
  64. UIUtils.ShowMessage( UniqueId, "Dithering node only accepts SAMPLER2D input type.\nTexture Object connected changed to " + type + ", connection was lost, please review and update accordingly.", MessageSeverity.Warning );
  65. }
  66. }
  67. public override void Draw( DrawInfo drawInfo )
  68. {
  69. base.Draw( drawInfo );
  70. EditorGUI.BeginChangeCheck();
  71. m_selectedPatternInt = m_upperLeftWidget.DrawWidget( this, m_selectedPatternInt, PatternsStr );
  72. if( EditorGUI.EndChangeCheck() )
  73. {
  74. UpdatePorts();
  75. }
  76. }
  77. public override void DrawProperties()
  78. {
  79. base.DrawProperties();
  80. EditorGUI.BeginChangeCheck();
  81. m_selectedPatternInt = EditorGUILayoutPopup( "Pattern", m_selectedPatternInt, PatternsStr );
  82. if ( EditorGUI.EndChangeCheck() )
  83. {
  84. UpdatePorts();
  85. }
  86. EditorGUI.BeginChangeCheck();
  87. m_customScreenPos = EditorGUILayoutToggle( "Screen Position", m_customScreenPos );
  88. if( EditorGUI.EndChangeCheck() )
  89. {
  90. UpdatePorts();
  91. }
  92. }
  93. private void UpdatePorts()
  94. {
  95. m_texPort.Visible = ( m_selectedPatternInt == 2 );
  96. m_ssPort.Visible = ( m_selectedPatternInt == 2 );
  97. m_inputPorts[ 2 ].Visible = m_customScreenPos;
  98. m_sizeIsDirty = true;
  99. SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, PatternsStr[ m_selectedPatternInt ] ) );
  100. }
  101. private void GeneratePattern( ref MasterNodeDataCollector dataCollector )
  102. {
  103. SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, PatternsStr[ m_selectedPatternInt ] ) );
  104. switch ( m_selectedPatternInt )
  105. {
  106. default:
  107. case 0:
  108. {
  109. m_functionBody = string.Empty;
  110. m_functionHeader = "Dither" + PatternsFuncStr[ m_selectedPatternInt ] + "( {0}, {1} )";
  111. IOUtils.AddFunctionHeader( ref m_functionBody, "inline float Dither" + PatternsFuncStr[ m_selectedPatternInt ] + "( int x, int y )" );
  112. IOUtils.AddFunctionLine( ref m_functionBody, "const float dither[ 16 ] = {" );
  113. IOUtils.AddFunctionLine( ref m_functionBody, " 1, 9, 3, 11," );
  114. IOUtils.AddFunctionLine( ref m_functionBody, " 13, 5, 15, 7," );
  115. IOUtils.AddFunctionLine( ref m_functionBody, " 4, 12, 2, 10," );
  116. IOUtils.AddFunctionLine( ref m_functionBody, " 16, 8, 14, 6 };" );
  117. IOUtils.AddFunctionLine( ref m_functionBody, "int r = y * 4 + x;" );
  118. IOUtils.AddFunctionLine( ref m_functionBody, "return dither[ r ] / 16; // same # of instructions as pre-dividing due to compiler magic" );
  119. IOUtils.CloseFunctionBody( ref m_functionBody );
  120. }
  121. break;
  122. case 1:
  123. {
  124. m_functionBody = string.Empty;
  125. m_functionHeader = "Dither" + PatternsFuncStr[ m_selectedPatternInt ] + "( {0}, {1} )";
  126. IOUtils.AddFunctionHeader( ref m_functionBody, "inline float Dither" + PatternsFuncStr[ m_selectedPatternInt ] + "( int x, int y )" );
  127. IOUtils.AddFunctionLine( ref m_functionBody, "const float dither[ 64 ] = {" );
  128. IOUtils.AddFunctionLine( ref m_functionBody, " 1, 49, 13, 61, 4, 52, 16, 64," );
  129. IOUtils.AddFunctionLine( ref m_functionBody, " 33, 17, 45, 29, 36, 20, 48, 32," );
  130. IOUtils.AddFunctionLine( ref m_functionBody, " 9, 57, 5, 53, 12, 60, 8, 56," );
  131. IOUtils.AddFunctionLine( ref m_functionBody, " 41, 25, 37, 21, 44, 28, 40, 24," );
  132. IOUtils.AddFunctionLine( ref m_functionBody, " 3, 51, 15, 63, 2, 50, 14, 62," );
  133. IOUtils.AddFunctionLine( ref m_functionBody, " 35, 19, 47, 31, 34, 18, 46, 30," );
  134. IOUtils.AddFunctionLine( ref m_functionBody, " 11, 59, 7, 55, 10, 58, 6, 54," );
  135. IOUtils.AddFunctionLine( ref m_functionBody, " 43, 27, 39, 23, 42, 26, 38, 22};" );
  136. IOUtils.AddFunctionLine( ref m_functionBody, "int r = y * 8 + x;" );
  137. IOUtils.AddFunctionLine( ref m_functionBody, "return dither[ r ] / 64; // same # of instructions as pre-dividing due to compiler magic" );
  138. IOUtils.CloseFunctionBody( ref m_functionBody );
  139. }
  140. break;
  141. case 2:
  142. {
  143. ParentGraph outsideGraph = UIUtils.CurrentWindow.OutsideGraph;
  144. m_functionBody = string.Empty;
  145. m_functionHeader = "Dither" + PatternsFuncStr[ m_selectedPatternInt ] + "( {0}, {1}, {2} )";
  146. IOUtils.AddFunctionHeader( ref m_functionBody, "inline float Dither" + PatternsFuncStr[ m_selectedPatternInt ] + "( float4 screenPos, " + GeneratorUtils.GetPropertyDeclaraction( "noiseTexture", TextureType.Texture2D, ", " ) + GeneratorUtils.GetSamplerDeclaraction( "samplernoiseTexture", TextureType.Texture2D, ", " ) + "float4 noiseTexelSize )" );
  147. string samplingCall = GeneratorUtils.GenerateSamplingCall( ref dataCollector, WirePortDataType.SAMPLER2D, "noiseTexture", "samplernoiseTexture", "screenPos.xy * _ScreenParams.xy * noiseTexelSize.xy", MipType.MipLevel, "0" );
  148. IOUtils.AddFunctionLine( ref m_functionBody, "float dither = "+ samplingCall + ".g;" );
  149. IOUtils.AddFunctionLine( ref m_functionBody, "float ditherRate = noiseTexelSize.x * noiseTexelSize.y;" );
  150. IOUtils.AddFunctionLine( ref m_functionBody, "dither = ( 1 - ditherRate ) * dither + ditherRate;" );
  151. IOUtils.AddFunctionLine( ref m_functionBody, "return dither;" );
  152. IOUtils.CloseFunctionBody( ref m_functionBody );
  153. }
  154. break;
  155. }
  156. }
  157. public override void PropagateNodeData( NodeData nodeData, ref MasterNodeDataCollector dataCollector )
  158. {
  159. base.PropagateNodeData( nodeData, ref dataCollector );
  160. dataCollector.UsingCustomScreenPos = true;
  161. }
  162. public override string GenerateShaderForOutput( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalVar )
  163. {
  164. if ( m_outputPorts[ 0 ].IsLocalValue( dataCollector.PortCategory ) )
  165. return m_outputPorts[ 0 ].LocalValue( dataCollector.PortCategory );
  166. GeneratePattern( ref dataCollector );
  167. if( !( dataCollector.IsTemplate && dataCollector.IsSRP ) )
  168. dataCollector.AddToIncludes( UniqueId, Constants.UnityShaderVariables );
  169. string varName = string.Empty;
  170. if( m_customScreenPos && m_inputPorts[ 2 ].IsConnected )
  171. {
  172. varName = "ditherCustomScreenPos" + OutputId;
  173. string customScreenPosVal = m_inputPorts[ 2 ].GeneratePortInstructions( ref dataCollector );
  174. dataCollector.AddLocalVariable( UniqueId, CurrentPrecisionType, WirePortDataType.FLOAT4, varName, customScreenPosVal );
  175. }
  176. else
  177. {
  178. if( dataCollector.TesselationActive && dataCollector.IsFragmentCategory )
  179. {
  180. varName = GeneratorUtils.GenerateScreenPositionPixelOnFrag( ref dataCollector, UniqueId, CurrentPrecisionType );
  181. }
  182. else
  183. {
  184. if( dataCollector.IsTemplate )
  185. {
  186. varName = dataCollector.TemplateDataCollectorInstance.GetScreenPosPixel( CurrentPrecisionType );
  187. }
  188. else
  189. {
  190. varName = GeneratorUtils.GenerateScreenPositionPixel( ref dataCollector, UniqueId, CurrentPrecisionType, !dataCollector.UsingCustomScreenPos );
  191. }
  192. }
  193. }
  194. //string surfInstruction = string.Format( "abs( {0}.xy ) * _ScreenParams.xy", varName );
  195. m_showErrorMessage = false;
  196. string functionResult = "";
  197. string noiseTex = string.Empty;
  198. switch ( m_selectedPatternInt )
  199. {
  200. default:
  201. case 0:
  202. functionResult = dataCollector.AddFunctions( m_functionHeader, m_functionBody, "fmod( " + varName + ".x, 4 )", "fmod( " + varName + ".y, 4 )" );
  203. break;
  204. case 1:
  205. functionResult = dataCollector.AddFunctions( m_functionHeader, m_functionBody, "fmod( " + varName + ".x, 8 )", "fmod( " + varName + ".y, 8 )" );
  206. break;
  207. case 2:
  208. {
  209. if( !m_texPort.IsConnected )
  210. {
  211. m_showErrorMessage = true;
  212. m_errorMessageTypeIsError = NodeMessageType.Warning;
  213. m_errorMessageTooltip = "Please connect a texture object to the Pattern input port to generate a proper dithered pattern";
  214. return "0";
  215. } else
  216. {
  217. ParentGraph outsideGraph = UIUtils.CurrentWindow.OutsideGraph;
  218. noiseTex = m_texPort.GeneratePortInstructions( ref dataCollector );
  219. //GeneratePattern( ref dataCollector );
  220. dataCollector.AddToUniforms( UniqueId, "float4 " + noiseTex + "_TexelSize;", dataCollector.IsSRP );
  221. if( outsideGraph.SamplingMacros )
  222. {
  223. string sampler = string.Empty;
  224. if( m_ssPort.IsConnected )
  225. {
  226. sampler = m_ssPort.GeneratePortInstructions( ref dataCollector );
  227. }
  228. else
  229. {
  230. sampler = GeneratorUtils.GenerateSamplerState( ref dataCollector, UniqueId, noiseTex, VariableMode.Create );
  231. }
  232. functionResult = dataCollector.AddFunctions( m_functionHeader, m_functionBody, varName, noiseTex + ", " + sampler, noiseTex + "_TexelSize" );
  233. }
  234. else
  235. {
  236. functionResult = dataCollector.AddFunctions( m_functionHeader, m_functionBody, varName, noiseTex, noiseTex + "_TexelSize" );
  237. }
  238. }
  239. }
  240. break;
  241. }
  242. dataCollector.AddLocalVariable( UniqueId, CurrentPrecisionType, WirePortDataType.FLOAT, "dither" + OutputId, functionResult );
  243. if( m_inputPorts[ 0 ].IsConnected )
  244. {
  245. string driver = m_inputPorts[ 0 ].GeneratePortInstructions( ref dataCollector );
  246. dataCollector.AddLocalVariable( UniqueId, "dither" + OutputId+" = step( dither"+ OutputId + ", saturate( "+ driver + " * 1.00001 ) );" );
  247. }
  248. //RegisterLocalVariable( 0, functionResult, ref dataCollector, "dither" + OutputId );
  249. m_outputPorts[ 0 ].SetLocalValue( "dither" + OutputId, dataCollector.PortCategory );
  250. return m_outputPorts[ 0 ].LocalValue( dataCollector.PortCategory );
  251. }
  252. public override void ReadFromString( ref string[] nodeParams )
  253. {
  254. base.ReadFromString( ref nodeParams );
  255. m_selectedPatternInt = Convert.ToInt32( GetCurrentParam( ref nodeParams ) );
  256. if( UIUtils.CurrentShaderVersion() > 15404 )
  257. {
  258. m_customScreenPos = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) );
  259. }
  260. UpdatePorts();
  261. }
  262. public override void WriteToString( ref string nodeInfo, ref string connectionsInfo )
  263. {
  264. base.WriteToString( ref nodeInfo, ref connectionsInfo );
  265. IOUtils.AddFieldValueToString( ref nodeInfo, m_selectedPatternInt );
  266. IOUtils.AddFieldValueToString( ref nodeInfo, m_customScreenPos );
  267. }
  268. }
  269. }