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.

423 lines
16 KiB

  1. // Amplify Shader Editor - Visual Shader Editing Tool
  2. // Copyright (c) Amplify Creations, Lda <info@amplify.pt>
  3. //https://www.shadertoy.com/view/XdS3RW
  4. //http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html
  5. //http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm
  6. using UnityEngine;
  7. using UnityEditor;
  8. using System;
  9. namespace AmplifyShaderEditor
  10. {
  11. public enum BlendOps
  12. {
  13. ColorBurn,
  14. ColorDodge,
  15. Darken,
  16. Divide,
  17. Difference,
  18. Exclusion,
  19. SoftLight,
  20. HardLight,
  21. HardMix,
  22. Lighten,
  23. LinearBurn,
  24. LinearDodge,
  25. LinearLight,
  26. Multiply,
  27. Overlay,
  28. PinLight,
  29. Subtract,
  30. Screen,
  31. VividLight
  32. }
  33. [Serializable]
  34. [NodeAttributes( "Blend Operations", "Image Effects", "Common layer blending modes" )]
  35. public class BlendOpsNode : ParentNode
  36. {
  37. //private const string ASEHardLightCall = "ASEHardLight({0},{1})";
  38. //private const string ASEHardLightFunc =
  39. //"inline float ASEHardLight( float srcLocalVar, float dstLocalVar ){" +
  40. //" return ( ( srcLocalVar > 0.5 ) ? ( 1.0 - ( 1.0 - 2.0 * ( srcLocalVar - 0.5 ) ) * ( 1.0 - dstLocalVar ) ) : ( 2.0 * srcLocalVar * dstLocalVar ) ); }";
  41. //private const string ASELinearLightCall = "ASELinearLight({0},{1})";
  42. //private const string ASELinearLightFunc =
  43. //"inline float ASELinearLight( float srcLocalVar, float dstLocalVar ){" +
  44. //" return ( ( srcLocalVar > 0.5 ) ? ( dstLocalVar + 2.0 * srcLocalVar - 1.0 ) : ( dstLocalVar + 2.0 * ( srcLocalVar - 0.5 ) ) ); }";
  45. //private const string ASEOverlayCall = "ASEOverlay({0},{1})";
  46. //private const string ASEOverlayFunc =
  47. //"inline float ASEOverlay( float srcLocalVar, float dstLocalVar ){" +
  48. //" return ( ( dstLocalVar > 0.5 ) ? ( 1.0 - ( 1.0 - 2.0 * ( dstLocalVar - 0.5 ) ) * ( 1.0 - srcLocalVar ) ) : ( 2.0 * dstLocalVar * srcLocalVar ) ); }";
  49. ////" return (dstLocalVar < 0.5) ? 2.0 * srcLocalVar * dstLocalVar : 1.0 - 2.0 * (1.0 - srcLocalVar) * (1.0 - dstLocalVar); }";
  50. //private const string ASEPinLightCall = "ASEPinLight({0},{1})";
  51. //private const string ASEPinLightFunc =
  52. //"inline float ASEPinLight( float srcLocalVar, float dstLocalVar ){" +
  53. //" return ( ( srcLocalVar > 0.5 ) ? max( dstLocalVar , 2.0 * ( srcLocalVar - 0.5 ) ) : min( dstLocalVar , 2.0 * srcLocalVar ) ); }";
  54. //private const string ASEVividLightCall = "ASEVividLight({0},{1})";
  55. //private const string ASEVividLightFunc = "inline float ASEVividLight( float srcLocalVar, float dstLocalVar ){" +
  56. //" return ( ( srcLocalVar > 0.5 ) ? ( dstLocalVar / ( ( 1.0 - srcLocalVar ) * 2.0 ) ) : ( 1.0 - ( ( ( 1.0 - dstLocalVar ) * 0.5 ) / srcLocalVar ) ) ); }";
  57. private const string ASEDarkerColorCall = "ASEDarkerColor{}({0},{1})";
  58. private const string ASEDarkerColorFunc = "inline float ASEDarkerColor{0}( float srcLocalVar, float dstLocalVar ){" +
  59. " return ({1} < {2}) ? s : d; }";
  60. private const string ASELighterColorCall = "ASELighterColor{}({0},{1})";
  61. private const string ASELighterColorFunc = "inline float ASELighterColor{0}( float srcLocalVar, float dstLocalVar ){" +
  62. " return ({1} > {2}) ? s : d; }";
  63. private const string BlendOpsModeStr = "Blend Op";
  64. private const string SaturateResultStr = "Saturate";
  65. [SerializeField]
  66. private BlendOps m_currentBlendOp = BlendOps.ColorBurn;
  67. [SerializeField]
  68. private WirePortDataType m_mainDataType = WirePortDataType.COLOR;
  69. [SerializeField]
  70. private bool m_saturate = true;
  71. private UpperLeftWidgetHelper m_upperLeftWidget = new UpperLeftWidgetHelper();
  72. protected override void CommonInit( int uniqueId )
  73. {
  74. base.CommonInit( uniqueId );
  75. AddInputPort( WirePortDataType.COLOR, false, "Source" );
  76. AddInputPort( WirePortDataType.COLOR, false, "Destiny" );
  77. AddOutputPort( WirePortDataType.COLOR, Constants.EmptyPortValue );
  78. m_inputPorts[ 0 ].AddPortForbiddenTypes( WirePortDataType.FLOAT3x3,
  79. WirePortDataType.FLOAT4x4,
  80. WirePortDataType.SAMPLER1D,
  81. WirePortDataType.SAMPLER2D,
  82. WirePortDataType.SAMPLER3D,
  83. WirePortDataType.SAMPLERCUBE );
  84. m_inputPorts[ 1 ].AddPortForbiddenTypes( WirePortDataType.FLOAT3x3,
  85. WirePortDataType.FLOAT4x4,
  86. WirePortDataType.SAMPLER1D,
  87. WirePortDataType.SAMPLER2D,
  88. WirePortDataType.SAMPLER3D,
  89. WirePortDataType.SAMPLERCUBE );
  90. m_textLabelWidth = 75;
  91. m_autoWrapProperties = true;
  92. m_hasLeftDropdown = true;
  93. SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, m_currentBlendOp ) );
  94. m_previewShaderGUID = "6d6b3518705b3ba49acdc6e18e480257";
  95. }
  96. public override void SetPreviewInputs()
  97. {
  98. base.SetPreviewInputs();
  99. m_previewMaterialPassId = (int)m_currentBlendOp;
  100. PreviewMaterial.SetInt( "_Sat", m_saturate ? 1 : 0 );
  101. }
  102. public override void AfterCommonInit()
  103. {
  104. base.AfterCommonInit();
  105. if( PaddingTitleLeft == 0 )
  106. {
  107. PaddingTitleLeft = Constants.PropertyPickerWidth + Constants.IconsLeftRightMargin;
  108. if( PaddingTitleRight == 0 )
  109. PaddingTitleRight = Constants.PropertyPickerWidth + Constants.IconsLeftRightMargin;
  110. }
  111. }
  112. public override void OnInputPortConnected( int portId, int otherNodeId, int otherPortId, bool activateNode = true )
  113. {
  114. base.OnInputPortConnected( portId, otherNodeId, otherPortId, activateNode );
  115. UpdateConnection( portId );
  116. }
  117. public override void OnConnectedOutputNodeChanges( int inputPortId, int otherNodeId, int otherPortId, string name, WirePortDataType type )
  118. {
  119. base.OnConnectedOutputNodeChanges( inputPortId, otherNodeId, otherPortId, name, type );
  120. UpdateConnection( inputPortId );
  121. }
  122. public override void OnInputPortDisconnected( int portId )
  123. {
  124. base.OnInputPortDisconnected( portId );
  125. UpdateDisconnection( portId );
  126. }
  127. void UpdateConnection( int portId )
  128. {
  129. m_inputPorts[ portId ].MatchPortToConnection();
  130. int otherPortId = ( portId + 1 ) % 2;
  131. if( m_inputPorts[ otherPortId ].IsConnected )
  132. {
  133. m_mainDataType = UIUtils.GetPriority( m_inputPorts[ 0 ].DataType ) > UIUtils.GetPriority( m_inputPorts[ 1 ].DataType ) ? m_inputPorts[ 0 ].DataType : m_inputPorts[ 1 ].DataType;
  134. }
  135. else
  136. {
  137. m_mainDataType = m_inputPorts[ portId ].DataType;
  138. m_inputPorts[ otherPortId ].ChangeType( m_mainDataType, false );
  139. }
  140. m_outputPorts[ 0 ].ChangeType( m_mainDataType, false );
  141. }
  142. void UpdateDisconnection( int portId )
  143. {
  144. int otherPortId = ( portId + 1 ) % 2;
  145. if( m_inputPorts[ otherPortId ].IsConnected )
  146. {
  147. m_mainDataType = m_inputPorts[ otherPortId ].DataType;
  148. m_inputPorts[ portId ].ChangeType( m_mainDataType, false );
  149. m_outputPorts[ 0 ].ChangeType( m_mainDataType, false );
  150. }
  151. }
  152. public override void DrawProperties()
  153. {
  154. base.DrawProperties();
  155. EditorGUI.BeginChangeCheck();
  156. m_currentBlendOp = (BlendOps)EditorGUILayoutEnumPopup( BlendOpsModeStr, m_currentBlendOp );
  157. if( EditorGUI.EndChangeCheck() )
  158. {
  159. SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, m_currentBlendOp ) );
  160. }
  161. m_saturate = EditorGUILayoutToggle( SaturateResultStr, m_saturate );
  162. }
  163. public override void Draw( DrawInfo drawInfo )
  164. {
  165. base.Draw( drawInfo );
  166. m_upperLeftWidget.DrawWidget<BlendOps>( ref m_currentBlendOp, this, OnWidgetUpdate );
  167. }
  168. private readonly Action<ParentNode> OnWidgetUpdate = ( x ) =>
  169. {
  170. x.SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, ( x as BlendOpsNode ).m_currentBlendOp ) );
  171. };
  172. private string CreateMultiChannel( ref MasterNodeDataCollector dataCollector, string function, string srcLocalVar, string dstLocalVar, string varName )
  173. {
  174. switch( m_outputPorts[ 0 ].DataType )
  175. {
  176. default:
  177. {
  178. return string.Format( function, srcLocalVar, dstLocalVar );
  179. }
  180. case WirePortDataType.FLOAT2:
  181. {
  182. string xChannelName = varName + OutputId + "X";
  183. string xChannelValue = string.Format( function, srcLocalVar + ".x", dstLocalVar + ".x" );
  184. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, WirePortDataType.FLOAT, xChannelName, xChannelValue );
  185. string yChannelName = varName + OutputId + "Y";
  186. string yChannelValue = string.Format( function, srcLocalVar + ".y", dstLocalVar + ".y" );
  187. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, WirePortDataType.FLOAT, yChannelName, yChannelValue );
  188. return string.Format( "float2({0},{1})", xChannelName, yChannelName );
  189. }
  190. case WirePortDataType.FLOAT3:
  191. {
  192. string xChannelName = varName + OutputId + "X";
  193. string xChannelValue = string.Format( function, srcLocalVar + ".x", dstLocalVar + ".x" );
  194. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, WirePortDataType.FLOAT, xChannelName, xChannelValue );
  195. string yChannelName = varName + OutputId + "Y";
  196. string yChannelValue = string.Format( function, srcLocalVar + ".y", dstLocalVar + ".y" );
  197. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, WirePortDataType.FLOAT, yChannelName, yChannelValue );
  198. string zChannelName = varName + OutputId + "Z";
  199. string zChannelValue = string.Format( function, srcLocalVar + ".z", dstLocalVar + ".z" );
  200. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, WirePortDataType.FLOAT, zChannelName, zChannelValue );
  201. return string.Format( "float3({0},{1},{2})", xChannelName, yChannelName, zChannelName );
  202. }
  203. case WirePortDataType.FLOAT4:
  204. case WirePortDataType.COLOR:
  205. {
  206. string xChannelName = varName + OutputId + "X";
  207. string xChannelValue = string.Format( function, srcLocalVar + ".x", dstLocalVar + ".x" );
  208. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, WirePortDataType.FLOAT, xChannelName, xChannelValue );
  209. string yChannelName = varName + OutputId + "Y";
  210. string yChannelValue = string.Format( function, srcLocalVar + ".y", dstLocalVar + ".y" );
  211. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, WirePortDataType.FLOAT, yChannelName, yChannelValue );
  212. string zChannelName = varName + OutputId + "Z";
  213. string zChannelValue = string.Format( function, srcLocalVar + ".z", dstLocalVar + ".z" );
  214. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, WirePortDataType.FLOAT, zChannelName, zChannelValue );
  215. string wChannelName = varName + OutputId + "W";
  216. string wChannelValue = string.Format( function, srcLocalVar + ".w", dstLocalVar + ".w" );
  217. dataCollector.AddLocalVariable( UniqueId, m_currentPrecisionType, WirePortDataType.FLOAT, wChannelName, wChannelValue );
  218. return string.Format( "float4({0},{1},{2},{3})", xChannelName, yChannelName, zChannelName, wChannelName );
  219. }
  220. }
  221. }
  222. public override string GenerateShaderForOutput( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar )
  223. {
  224. if( m_outputPorts[ 0 ].IsLocalValue( dataCollector.PortCategory ) )
  225. return m_outputPorts[ 0 ].LocalValue( dataCollector.PortCategory );
  226. string src = m_inputPorts[ 0 ].GenerateShaderForOutput( ref dataCollector, m_mainDataType, false, true );
  227. string dst = m_inputPorts[ 1 ].GenerateShaderForOutput( ref dataCollector, m_mainDataType, false, true );
  228. string srcLocalVar = "blendOpSrc" + OutputId;
  229. string dstLocalVar = "blendOpDest" + OutputId;
  230. dataCollector.AddLocalVariable( UniqueId, UIUtils.FinalPrecisionWirePortToCgType( m_currentPrecisionType, m_mainDataType ) + " " + srcLocalVar, src + ";" );
  231. dataCollector.AddLocalVariable( UniqueId, UIUtils.FinalPrecisionWirePortToCgType( m_currentPrecisionType, m_mainDataType ) + " " + dstLocalVar, dst + ";" );
  232. int currIndent = UIUtils.ShaderIndentLevel;
  233. if( dataCollector.MasterNodeCategory == AvailableShaderTypes.Template )
  234. {
  235. UIUtils.ShaderIndentLevel = 0;
  236. }
  237. else
  238. {
  239. UIUtils.ShaderIndentLevel = 1;
  240. UIUtils.ShaderIndentLevel++;
  241. }
  242. string result = string.Empty;
  243. switch( m_currentBlendOp )
  244. {
  245. case BlendOps.ColorBurn:
  246. {
  247. result = "( 1.0 - ( ( 1.0 - " + dstLocalVar + ") / " + srcLocalVar + ") )";
  248. }
  249. break;
  250. case BlendOps.ColorDodge:
  251. {
  252. result = "( " + dstLocalVar + "/ ( 1.0 - " + srcLocalVar + " ) )";
  253. }
  254. break;
  255. case BlendOps.Darken:
  256. {
  257. result = "min( " + srcLocalVar + " , " + dstLocalVar + " )";
  258. }
  259. break;
  260. case BlendOps.Divide:
  261. {
  262. result = "( " + dstLocalVar + " / " + srcLocalVar + " )";
  263. }
  264. break;
  265. case BlendOps.Difference:
  266. {
  267. result = "abs( " + srcLocalVar + " - " + dstLocalVar + " )";
  268. }
  269. break;
  270. case BlendOps.Exclusion:
  271. {
  272. result = "( 0.5 - 2.0 * ( " + srcLocalVar + " - 0.5 ) * ( " + dstLocalVar + " - 0.5 ) )";
  273. }
  274. break;
  275. case BlendOps.SoftLight:
  276. {
  277. result = string.Format( "2.0f*{0}*{1} + {0}*{0}*(1.0f - 2.0f*{1})", dstLocalVar, srcLocalVar );
  278. }
  279. break;
  280. case BlendOps.HardLight:
  281. {
  282. result = " (( " + srcLocalVar + " > 0.5 ) ? ( 1.0 - ( 1.0 - 2.0 * ( " + srcLocalVar + " - 0.5 ) ) * ( 1.0 - " + dstLocalVar + " ) ) : ( 2.0 * " + srcLocalVar + " * " + dstLocalVar + " ) )";
  283. //dataCollector.AddFunction( ASEHardLightCall, UIUtils.ShaderIndentTabs + ASEHardLightFunc );
  284. //result = CreateMultiChannel( ref dataCollector, ASEHardLightCall, srcLocalVar, dstLocalVar, "hardLightBlend" );
  285. }
  286. break;
  287. case BlendOps.HardMix:
  288. {
  289. result = " round( 0.5 * ( " + srcLocalVar + " + " + dstLocalVar + " ) )";
  290. }
  291. break;
  292. case BlendOps.Lighten:
  293. {
  294. result = " max( " + srcLocalVar + ", " + dstLocalVar + " )";
  295. }
  296. break;
  297. case BlendOps.LinearBurn:
  298. {
  299. result = "( " + srcLocalVar + " + " + dstLocalVar + " - 1.0 )";
  300. }
  301. break;
  302. case BlendOps.LinearDodge:
  303. {
  304. result = "( " + srcLocalVar + " + " + dstLocalVar + " )";
  305. }
  306. break;
  307. case BlendOps.LinearLight:
  308. {
  309. result = "(( " + srcLocalVar + " > 0.5 )? ( " + dstLocalVar + " + 2.0 * " + srcLocalVar + " - 1.0 ) : ( " + dstLocalVar + " + 2.0 * ( " + srcLocalVar + " - 0.5 ) ) )";
  310. //dataCollector.AddFunction( ASELinearLightCall, UIUtils.ShaderIndentTabs + ASELinearLightFunc );
  311. //result = CreateMultiChannel( ref dataCollector, ASELinearLightCall, srcLocalVar, dstLocalVar, "linearLightBlend" );
  312. }
  313. break;
  314. case BlendOps.Multiply:
  315. {
  316. result = "( " + srcLocalVar + " * " + dstLocalVar + " )";
  317. }
  318. break;
  319. case BlendOps.Overlay:
  320. {
  321. result = "(( " + dstLocalVar + " > 0.5 ) ? ( 1.0 - ( 1.0 - 2.0 * ( " + dstLocalVar + " - 0.5 ) ) * ( 1.0 - " + srcLocalVar + " ) ) : ( 2.0 * " + dstLocalVar + " * " + srcLocalVar + " ) )";
  322. //dataCollector.AddFunction( ASEOverlayCall, UIUtils.ShaderIndentTabs + ASEOverlayFunc );
  323. //result = CreateMultiChannel( ref dataCollector, ASEOverlayCall, srcLocalVar, dstLocalVar, "overlayBlend" );
  324. }
  325. break;
  326. case BlendOps.PinLight:
  327. {
  328. result = "(( " + srcLocalVar + " > 0.5 ) ? max( " + dstLocalVar + ", 2.0 * ( " + srcLocalVar + " - 0.5 ) ) : min( " + dstLocalVar + ", 2.0 * " + srcLocalVar + " ) )";
  329. //dataCollector.AddFunction( ASEPinLightCall, UIUtils.ShaderIndentTabs + ASEPinLightFunc );
  330. //result = CreateMultiChannel( ref dataCollector, ASEPinLightCall, srcLocalVar, dstLocalVar, "pinLightBlend" );
  331. }
  332. break;
  333. case BlendOps.Subtract:
  334. {
  335. result = "( " + dstLocalVar + " - " + srcLocalVar + " )";
  336. }
  337. break;
  338. case BlendOps.Screen:
  339. {
  340. result = "( 1.0 - ( 1.0 - " + srcLocalVar + " ) * ( 1.0 - " + dstLocalVar + " ) )";
  341. }
  342. break;
  343. case BlendOps.VividLight:
  344. {
  345. result = "(( " + srcLocalVar + " > 0.5 ) ? ( " + dstLocalVar + " / ( ( 1.0 - " + srcLocalVar + " ) * 2.0 ) ) : ( 1.0 - ( ( ( 1.0 - " + dstLocalVar + " ) * 0.5 ) / " + srcLocalVar + " ) ) )";
  346. //dataCollector.AddFunction( ASEVividLightCall, UIUtils.ShaderIndentTabs + ASEVividLightFunc );
  347. //result = CreateMultiChannel( ref dataCollector, ASEVividLightCall, srcLocalVar, dstLocalVar, "vividLightBlend" );
  348. }
  349. break;
  350. }
  351. UIUtils.ShaderIndentLevel = currIndent;
  352. if( m_saturate )
  353. result = "( saturate( " + result + " ))";
  354. return CreateOutputLocalVariable( 0, result, ref dataCollector );
  355. }
  356. public override void WriteToString( ref string nodeInfo, ref string connectionsInfo )
  357. {
  358. base.WriteToString( ref nodeInfo, ref connectionsInfo );
  359. IOUtils.AddFieldValueToString( ref nodeInfo, m_currentBlendOp );
  360. IOUtils.AddFieldValueToString( ref nodeInfo, m_saturate );
  361. }
  362. public override void ReadFromString( ref string[] nodeParams )
  363. {
  364. base.ReadFromString( ref nodeParams );
  365. m_currentBlendOp = (BlendOps)Enum.Parse( typeof( BlendOps ), GetCurrentParam( ref nodeParams ) );
  366. m_saturate = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) );
  367. SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, m_currentBlendOp ) );
  368. }
  369. public override void Destroy()
  370. {
  371. base.Destroy();
  372. m_upperLeftWidget = null;
  373. }
  374. }
  375. }