Unity Products:Amplify Shader Editor/Templates

From Amplify Creations Wiki
Jump to: navigation, search

Product Page - Included Shaders - Manual - Shader Functions - Tutorials - API - Shader Templates - Scriptable Rendering Pipeline - Nodes - Community Nodes

Templates Description

Introduction

This feature allows users to create an ASE shader from an already existing one which will be used as base, or Template.
Templates are regular Unity shaders with special ASE tags placed on key points. These tags are written as comments so they don't affect the shader compilation and its default behavior. Any shader can be converted to a template and, if the given shader has multiple passes, each one is represented by its own Output node.

How to use

Create a Shader

Creating an ASE shader using templates is as easy as creating a regular surface one. Hit the right mouse button on the project view, select Create > Amplify Shader and from that menu a list with the default ASE Surface Shader is shown followed with all the available templates.



As for now, the available templates are:

  • Post-Process
  • Default Unlit
  • Default UI
  • Default Sprites
  • Particles Alpha-Blended


A user can, at any time, swap which template is being currently used and also go back to the default Surface Shader. This is done by hitting the Shader Type drop down on the current Output Node property window.

Options available at the Output node are determined on what was made available to modify over the template.

Check Template Source Code

A user can, at any time, hit the Edit Template button on the Output Node properties window to check the source code of the current template.
Please note that, as for now, ASE only loads a template's info on its startup, so any changes on the template require the user to restart the plugin to take effect on your shader's next compilation.
This limitation will be addressed in a future update.

Template Nodes

New nodes were added in order to get access to specific template data from inside the graph.

Templates Parameters

A new Template Parameter node is also available now. It gives you access to Properties and Global Variables available on the current template.
Simply hit the Parameter drop down on its Node Properties window, or on the upper left corner of the nodes body to choose which Property/Global Variable to use. When selecting a parameter it will let you know if a Property or a Global Variable is selected.

Multiple Templates Parameter nodes can be placed across the shader graph, each one with the same or different selected parameters.

Please note that previews for this node are only available when on Material mode and for Property type parameters.

Templates Vertex and Fragment Data

Similar to Template Parameters, Amplify Shader Editor also allows direct access to Vertex and Interpolated data.
The Vertex Data node allows users to get direct access to vertex data declared over the structure set over the vertex shader body on the template.
Likewise the Fragment Data node allows user to get direct access to fragment data declared over the interpolator structure on the template which will be sent into the fragment function.

Template Local Var Data

Local variables declared on the template can now be accessed over the shader graph via the Template Local Var Data node. Only local variables preceded with the /*ase_local_var*/ tag over its declaration will be caught and made available over the graph.

Create your own template

The process of converting a shader into a Template is quite straightforward.

Single Pass Example

Here's a simple example of a single pass unlit vertex/frag shader converted to an ASE Template.

Shader /*ase_name*/ "ASETemplateShaders/DefaultUnlit" /*end*/
{
	Properties
	{
		_MainTex ("Sprite Texture", 2D) = "white" {}
		_Color ("Tint", Color) = (1,1,1,1)
		/*ase_props*/
	}
	
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100
		Cull Off
		/*ase_pass*/

		Pass
		{
			CGPROGRAM
			#pragma target 3.0 
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			/*ase_pragma*/

			struct appdata
			{
				float4 vertex : POSITION;
				float4 texcoord : TEXCOORD0;
				float4 texcoord1 : TEXCOORD1;
				/*ase_vdata:p=p;uv0=tc0.xy;uv1=tc1.xy*/
			};
			
			struct v2f
			{
				float4 vertex : SV_POSITION;
				float4 texcoord : TEXCOORD0;
				/*ase_interp(1,7):sp=sp.xyzw;uv0=tc0.xy;uv1=tc0.zw*/
			};

			uniform sampler2D _MainTex;
			uniform fixed4 _Color;
			/*ase_globals*/
			
			v2f vert ( appdata v /*ase_vert_input*/)
			{
				v2f o;
				o.texcoord.xy = v.texcoord.xy;
				o.texcoord.zw = v.texcoord1.xy;
				
				// ase common template code
				/*ase_vert_code:v=appdata;o=v2f*/
				
				o.vertex.xyz += /*ase_vert_out:Local Vertex;Float3*/ float3(0,0,0) /*end*/;
				o.vertex = UnityObjectToClipPos(v.vertex);
				return o;
			}
			
			fixed4 frag (v2f i /*ase_frag_input*/) : SV_Target
			{
				fixed4 myColorVar;
				// ase common template code
				/*ase_frag_code:i=v2f*/
				
				myColorVar = /*ase_frag_out:Frag Color;Float4*/fixed4(1,0,0,1)/*end*/;
				return myColorVar;
			}
			ENDCG
		}
	}
}

Available Tags

As can be noted, there are a couple of ASE tags scattered across the shader code. Here's a brief explanation of all the available tags:

ase_name

This tag is extremely important since it's the one which lets ASE know this shader needs to be analysed as a Template. It also shows where ASE can inject a new custom shader name.
Shader /*ase_name*/ "ASETemplateShaders/DefaultUnlit" /*end*/
The string between ase_name and end is used as the default name when a new shader is created using this template.

ase_props

This tag specifies both where the current shader properties are and where ASE can inject new ones. Only properties declared before the tag are read and made available to the user.
On this template, two properties will be detected and registered, Sprite Texture and Tint.

ase_main_pass

This tag is specific to multi-pass effects and let ASE to now which pass is the main one. Please check the Multi-Pass section for additional information.

ase_hide_pass

This tag is specific to multi-pass effects and allows passes to be hidden, or completely excluded from being modified. Please check the Excludind/Hidding Passes section for additional information.

ase_tags

This tag specifies where ASE can inject new shader tags/parameters.
LEGACY: The /*ase_tag*/ tag is no longer used on ASE version v1.4.5 or newer since the template can automatically figure out where they are.

ase_pass

This tag specifies where ASE can inject new passes and/or states setup. This is necessary for nodes like Grab Screen Color, where a Grab Pass needs to be declared on the shader.
LEGACY: The /*ase_pass*/ tag is optional on ASE version v1.4.5 or newer since the template can automatically figure out where to place the Grab Pass declaration.

ase_pragma

This tag specifies where ASE can inject new pragma directives and include files. Multiple important nodes like Texture Sampler require this tag to work since they internally use Unity functions from its library.
Besides internal usage by the referred nodes, the /*ase_pragma*/ is also used to add new #defines, #includes and #pragmas explicitly declared on the Additional Defines, Additional Includes and Additional Pragmas subsection on Pass section of the Output node.
We heavily encourage this tag to be declared BEFORE the /*ase_globals*/ tag.

ase_vdata

This tag specifies where vertex data is declared and where to add new one. It must be set at the final of the vertex data struct. It's also responsible for letting ASE know which type of data each element contains."
On this example,
/*ase_vdata:p=p;uv0=tc0.xy;uv1=tc1.xy*/
Three elements are declared on the struct.

float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;

For the POSITION, semantic data sent is usually a vertex position but semantics like COLOR or TEXCOORD# can be used for a lot of different purposes.
Each data type is declared via Data Type = Semantic shortcut using a ; as a separator between declarations.
Here are all the data types:

Data Type Description
p Local Vertex Position
sp Clip Space Position
c Color
n Normal
t Tangent
uv# UV Coordinates #
Shortcut Semantic
p POSITION
sp SV_POSITION
c COLOR
n NORMAL
t TANGENT
tc# TEXCOORD0#

Looking at how this tag was declared, we can see the following info:

  • p=p: POSITION semantic variable contains local vertex position
  • uv0=tc0.xy: TEXCOORD0 semantic variable, most specifically its x and y component, contains UV set 0
  • uv1=tc1.xy: TEXCOORD0 semantic variable, most specifically its x and y component, contains UV set 1
ase_interp

This tag is quite similar to ase_vdata on how it's declared but it now lets ASE know where the interpolators struct is being declared.
It also must be set at the final of the interpolators data struct and is responsible for letting ASE know which type of data each element contains.
An additional step is required, where interpolators that can be used by ASE must be explicitly declared on the tag.
This is done via the current syntax ase_interp(Min,Max). All interpolators between Min and Max , [ TEXCOORD#MIN - TEXCOORD#MAX ] are considered available to be used. Please note that Min may be not be a full interpolator but only part of it.
E.g. if x and y of TEXCOORD1 is being used, you can declare the tag like this ase_interp(1.zw,7). This way TEXCOORD1 can be used by ASE, but only the z and w channels.

So, on this example,
/*ase_interp(1,7):sp=sp.xyzw;uv0=tc0.xy;uv1=tc0.zw*/
two elements are declared on the struct.

float4 vertex : SV_POSITION;
float4 texcoord : TEXCOORD0;

We can see the following info set on the tag.

  • ase_interp(1,7): Interpolators between TEXCOORD1 and TEXCOORD7 are available
  • sp=sp.xyzw: SV_POSITION semantic variable contains Clip Space Position
  • uv0=tc0.xy: TEXCOORD0 semantic variable contains UV Set 0 on its xy components
  • uv1=tc0.zw: TEXCOORD0 semantic variable contains UV Set 1 on its xy components

Also, besides the usual interpolated data, some specific data can also be marked as available over the interpolators.

Data Type Description
wn World Normal
wt World Tangent
wb World Bitangent
wvd World View Dir
wp World Position
ase_globals

This tag is similar to ase_props and specify both where current global variables are and where ASE can inject new ones. Only declared global variables before the tag are read and made available to the user.
On this template, two global variables will be detected _MainTex and _Color. Since these global variables are associated to properties they will not be registered since this already was done by the ase_props tag.

ase_vert_input

This tag defines where the vertex function input parameters are located so ASE can add additional parameters if needed. E.g. Switch By Face node requires a VFACE type variable to be declared as input.

ase_vert_code

This tag defines where ASE can insert additional vertex code created by vertex input ports on the graph. Two additional parameters need to be defined, the first specifies the type and name of the function input parameter while the second defines the type and name of the output/return parameter.
/*ase_vert_code:v=appdata;o=v2f*/

  • v=appdata: Vertex data received from variable v of type appdata
  • o=v2f: Return output variable o of type v2f
ase_vert_out

This tag defines major code injection points inside the vertex body. These points are transformed into Vertex Input ports on the Output Node.
Port Name and Port Type are defined inside the tag as the first and second of its parameters. On this current example a port of name Local vertex and type Float3/Vector3 is going to be created.
The tag must be placed inside a variable operation and encompasses the operation itself. This variable must then used by the original template code.

On this example,
o.vertex.xyz += /*ase_vert_out:Local Vertex;Float3*/ float3(0,0,0) /*end*/
The tag can modify o.vertex and this way add an offset to it. If no node is connected to that input port then the original operation is maintained.

ase_frag_input

This tag is similar to ase_vert_input but it defines where the fragment function input parameters are located so ASE can add additional parameters if needed.

ase_frag_code

This tag is similar to ase_vert_code and defines where ASE can insert additional fragment code created by fragment input ports on the graph.
However, only one parameter is defined on this tag, since a fragment always returns a color. This parameter specifies the type and name of the interpolator parameter.<br/
/*ase_frag_code:i=v2f*/

  • i=v2f: Interpolator variable i of type v2f
ase_frag_out

This tag is similar to ase_vert_out but it defines major code injection points inside the fragment body. These points are transformed into Fragment Input ports on the Output Node.
Port Name and Port Type are defined inside the tag as the first and second of its parameters. On this current example a port of name Frag Color and type Float4/Vector4 is going to be created.
The tag must be placed inside a variable operation and must encompass a default operation. This variable must then be used in some manner by the original template code.

On this example:
myColorVar = /*ase_frag_out:Frag Color;Float4*/fixed4(1,0,0,1)/*end*/
The tag can modify the myColorVar in order to modify the final fragment color. If no node is connected to the input port then the original operation is maintained.

Subshader/Pass Render State Options

For a specific render state option ( p.e.Cull Mode ) to be available for modification over the canvas, it must first be declared on the template. No special tags are needed for it to be correctly captured.
Options which are declared inside a Pass scope are set as Pass Properties and will be accessible on the Pass section on the output node. Options which are declared on a sub-shader scope before all its passes will be set as Sub-Shader properties and will be made available on the Sub-Shader section of the Output node.
Render State Options that are currently being captured by the template are:

  • Blend Mode
  • Blend Operation
  • Cull Mode
  • Color Mask
  • Stencil Operation
  • ZTest Mode
  • ZWrite Mode
  • Depth Offset
  • Sub-Shader/Pass Tags
  • Shader Model

Sub-Shader LOD is also being captured but is not editable from the ASE graph.

Here is each render state option displayed for the current example.



As can be seen over the SubShader template source, Render Tags and Cull Mode are declared, so they are made available at the SubShader section on the output node. On the Pass code only Shader Model is declared so it will the only one available at the Pass section.

Multi-Pass

Some extra considerations must be taken when creating multi-pass templates.

Setting Main Output node

A main Output node must be selected since multiple ones are created ( one per pass ) on a multi-pass shader. This selected output will internally command all others among other things. By default if nothing is specified, the Output node representing Sub-Shader 0 Pass 0 will set as the main Output node, but the user can manually select another one by declaring the /*ase_main_pass*/ tag inside of the intended pass.

Linking Pass/Output node ports

There may be situations on which a user will always need a result/sub graph to be shared across similar input ports from different passes. This can be done by internally linking these input ports through the assignment of a link Id to each of the input ports configuration over the template. The input port will only appear on the pass which is marked as the main output node or, if none is marked, the first one which declares the link id.

P.e. take a look at this input port configuration:
myColorVar = /*ase_frag_out:Frag Color (2);Float4;_MainColor*/fixed4 ( 0,0,1,1 )/*end*/;

The _MainColor parameter is the link id set for this particular fragment input port.

Although the sub graph will be connected to only a main port, its code will be generated to each specific linked port.

Excluding/Hidding Passes

Passes can be completely excluded from being modified on ASE or simply be on hidden state. For a pass to be excluded, the /*ase_hide_pass*/ tag must be set over its body and no vertex or fragment data entry points can be declared. This way the Pass body will be copied from the template to the final resulting shader.
An hidden pass is one which is not visible over the canvas but new code can be injected on it. This is done by not only applying the /*ase_hide_pass*/ tag but also declaring data entry points as linked ports. This way the graph connected to the main link port will also be analyzed to each one of the ports on the invisible pass.

Template Multi-Pass Switch

Template Multi-Pass Switch is a node specific to multi-pass shaders as it relays, in compile time, the input port data corresponding to the current pass being analyzed over its output.

Multi Pass Example

Let's extend the previous example to make use of multiple passes and create an outline shader template.

We'll mostly focus only on explaining the specific tags and behaviors of multi-pass templates

Shader /*ase_name*/ "WikiExamples/Simple Multi Pass Outline" /*end*/
{
	Properties
	{
		_OutlineWidth( "Outline Width", Float ) = 0.1
		_OutlineColor( "Outline Color", Color ) = (0,0,0,1)
		_MainBodyColor ( "Main Body Color", Color ) = ( 0,0,0,1 )
		/*ase_props*/
	}

	SubShader
	{
		Tags{ "RenderType" = "Opaque" }
		
		Pass
		{
			Cull Front
			Blend Off
		
			Name "Outline"
			CGPROGRAM
			#pragma target 3.0 
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			/*ase_pragma*/

			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				/*ase_vdata:p=p;n=n;uv0=tc0.xy*/
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float4 texcoord : TEXCOORD0;
				UNITY_VERTEX_OUTPUT_STEREO
				/*ase_interp(0.zw,7):sp=sp.xyzw;uv0=tc0.xy*/
			};

			uniform float4 _OutlineColor;
			uniform float _OutlineWidth;
			/*ase_globals*/

			v2f vert ( appdata v /*ase_vert_input*/ )
			{
				v2f o;
				UNITY_SETUP_INSTANCE_ID ( v );
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO ( o );
				o.texcoord.xy = v.texcoord.xy;
				o.texcoord.zw = 0;
				/*ase_local_var*/float3 MyLocalVar;
				/*ase_vert_code:v=appdata;o=v2f*/
				v.vertex.xyz += /*ase_vert_out:Vertex Offset;Float3*/ v.normal*_OutlineWidth /*end*/;
				o.vertex = UnityObjectToClipPos ( v.vertex );
				return o;
			}

			fixed4 frag ( v2f i /*ase_frag_input*/ ) : SV_Target
			{
				fixed4 myColorVar;
				/*ase_frag_code:i=v2f*/
				myColorVar = /*ase_frag_out:Color;Float4*/_OutlineColor/*end*/;
				return myColorVar;
			}
			ENDCG
		}

		Pass
		{
			/*ase_main_pass*/
			Cull Back
			Blend Off
			
			Name "MainBody"
			CGPROGRAM
			#pragma target 3.0 
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			/*ase_pragma*/

			struct appdata
			{
				float4 vertex : POSITION;
				float4 texcoord : TEXCOORD0;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				/*ase_vdata:p=p;uv0=tc0.xy*/
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float4 texcoord : TEXCOORD0;
				UNITY_VERTEX_OUTPUT_STEREO
				/*ase_interp(0.zw,7):sp=sp.xyzw;uv0=tc0.xy*/
			};
			uniform float4 _MainBodyColor;
			/*ase_globals*/

			v2f vert ( appdata v /*ase_vert_input*/ )
			{
				v2f o;
				UNITY_SETUP_INSTANCE_ID ( v );
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO ( o );
				o.texcoord.xy = v.texcoord.xy;
				o.texcoord.zw = 0;
				/*ase_vert_code:v=appdata;o=v2f*/
				v.vertex.xyz += /*ase_vert_out:Vertex Offset;Float3*/ float3( 0,0,0 ) /*end*/;
				o.vertex = UnityObjectToClipPos ( v.vertex );
				return o;
			}

			fixed4 frag ( v2f i /*ase_frag_input*/ ) : SV_Target
			{
				fixed4 myColorVar;
				/*ase_frag_code:i=v2f*/
				myColorVar = /*ase_frag_out:Color;Float4*/_MainBodyColor/*end*/;
				return myColorVar;
			}
			ENDCG
		}
	}
	CustomEditor "ASEMaterialInspector"
}
Shader Name

First and foremost please notice the template shader name which is between the /*ase_name*/ and /*end*/ tags. This not only determines the shader default name but also how it is presented on the create menu and Shader Type dropdown. So the WikiExamples/Simple Multi Pass Outline will be translated to this over the Create menu item:



Output Nodes Amount

Here's a newly created shader using this template:



Like stated before, each pass will be represented by its own output node. There are two passes declared over this template so, two output nodes will be created.
Each output node name is set by the respective pass name. For the first pass we can see its name declared via the Name "Outline":

Pass
{
	Cull Front
	Blend Off

	Name "Outline"
	(...)
}

As for the second pass its name is declared via the Name "MainBody" line.

Pass
{
	/*ase_main_pass*/
	Cull Back
	Blend Off
	
	Name "MainBody"
	(...)
}

If no name is set then an internal name is generated, p.e. SubShader 0 Pass 0, SubShader 0 Pass 1 and so on.

Main Output

Although multiple output nodes are created, one must be assigned to be the main one ( and rule them all ). This is visually represented by the icon.
Going back to the Main Body template source shown right above, we can see the /*ase_main_pass*/ tag. This lets ASE know Main Body output was selected to be the main one.

Linking Ports

For demonstration purposes, lets link both Color ports. In order to achieve this, on the first pass we'll replace the fragment Color declaration:

myColorVar = /*ase_frag_out:Color;Float4*/_OutlineColor/*end*/;

by:

myColorVar = /*ase_frag_out:Color;Float4;_Color*/_OutlineColor/*end*/;

Also on second pass we'll replace:

myColorVar = /*ase_frag_out:Color;Float4;*/fixed4 ( 1,1,1,1 )/*end*/;

by:

myColorVar = /*ase_frag_out:Color;Float4;_Color*/fixed4 ( 1,1,1,1 )/*end*/;

Each Color input port now shares the _Color Id and lets ASE know they are linked. Notice that it will disappear from the Outline output node.



Now the graph connected to the Main Body's Color port will also be shared to an invisible Color port on the Outline node.

Under the Hood

Let's take a closer look at a compiled shader using templates and compare it with its selected template. A simple example was created for demonstration purposes using the Unlit Template explained on Single Pass example.

On this simple shader, a color is being fetched from the already existing 2D Sampler template property and a luminance value is being calculated from it. A lerp is then applied between the luminance value and the original color using a newly created Float property called Amount as the interpolator.

Here's the final result after compiling the shader. Template original source code is presented on the left and compiled shader is on the right.

  • The usual Made with Amplify Shader Editor header is placed and ase_name is replaced by the new user name My Shader.
  • The ase_props tag is replaced by the newly created Amount property.
  • Since no new tags were added it remains unchanged.
  • Since no new passes were added, ase_pass was only removed.
  • Since no new pragmas were added, ase_pragma was only removed.
  • Since no new vertex data were added, ase_vdata was only removed.
  • Since no new interpolators were added, ase_interp was only removed
  • The ase_globals tag was replaced by the _Amount variable declaration. Also a _MainTex_ST variable was declared which will be used to calculate texture coordinates.
  • Since no new vertex function parameters were added, ase_vert_input was only removed.
  • Since no new vertex instructions were added, ase_vert_code was only removed.
  • Since the Local Vertex input port is not connected ase_vert_out is removed but it keeps the default float3(0,0,0) value assigned to the o.vertex.xyz += operation.
  • Since no new fragment function parameters were added, ase_frag_input was only removed.
  • The ase_frag_code tag was replaced with all the necessary code for textures fetching, applying the Luminance function and creating the Lerp result.
  • Since Frag Color input color is connected,ase_frag_out is removed and the default value is replaced with the new lerp result.

Examples

Please check the folder AmplifyShaderEditor > Examples > Official > TemplateExamples on your Amplify Shader Editor package for samples using each one of the available templates.