Top
sinefield

Creating Custom Shaders for PA Particle Field

PA Particle Field 1.1 added support for custom materials/shaders, letting you create your own shaders to use with Particle Fields.

If you’re used to writing shaders hopefully this will all be relatively clear and easy, but I’ll try to make this tutorial work for everyone.

To use a custom material on a Particle Field choose “Custom” from the material type drop down. This reveals the custom material slot where you can drag and drop a material.

The material you use must use a shader that is compatible with the PA Particle Field system, this tutorial will explain how to write one.

 

Shader Basics

Start by creating a new shader by clicking the Create menu and choosing Shader, give it a name and open the file.

Unity will have by default created a surface shader, replace this default shader code with a vertex/fragment shader using the commented template below.


Shader "CustomParticleField" {
	Properties {
	}
	SubShader {
		Pass {
			CGPROGRAM

			//define the vertex program
			#pragma vertex vert
			//define the fragment program
			#pragma fragment frag

			//include the default Unity functions/variables etc
			#include "UnityCG.cginc";

			//define a structure for passing data between the vertex and the fragment
			struct v2f {
				float4 pos : SV_POSITION;
			};

			//The vertex program
			v2f vert(appdata_full v) {
				v2f o;
				//position the vertex with the Model-View-Projection matrix
				o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
				return o;
			}

			//the fragment program
			float4 frag() : COLOR {
				return fixed4(1.0,0.0,0.0,1.0); //color the pixel red
			}

			ENDCG
		}
	}
}

This very basic shader just colors the model red.

 

Going PA Particle Field Compatible

PA Particle Field comes with its own .cginc file containing the methods used to simulate the field, to use these the .cginc must be included along side the “UnityCG.cginc”.

#include "Assets/PopupAsylum/PAParticleField/Shaders/ParticleField.cginc";

For shaders that just require different fragment program PA Particle Field can use a pre-built vertex program and data structure from “ParticleField.cginc”, this is all thats needed to make a shader compatible with PA Particle Field. The shader below is compatible with PA Particle Field, and will simply color the particles red.


Shader "CustomParticleField" {
	Properties {
	}
	SubShader {
        Pass {
			CGPROGRAM

			//use the vertex program defined in "ParticleField.cginc";
			#pragma vertex vertParticleFieldCube
			//define the fragment program
			#pragma fragment frag 

			//include the default Unity functions/variables etc
			#include "UnityCG.cginc";
			//include PA Particle Field
			#include "Assets/PopupAsylum/PAParticleField/Shaders/ParticleField.cginc";		

			//the fragment program using "v2fParticleField" data struct
			fixed4 frag(v2fParticleField i) : COLOR {
				return fixed4(1.0,0.0,0.0,1.0); //color the pixel red
			}

			ENDCG
		}
	}
}

Create a new material and apply the shader to it, then assign the material to the custom material slot on the PA Particle Field, the field should turn red.

e83ff446839498a897cdf07b15acea13[1]

Adding Full Feature Support

Currently this shader supports most of PA Particle Fields features, but there are some features that are still not enabled. To enable the full feature set the shader will need to compile multiple versions, this can be done by adding the following code;

#pragma multi_compile DIRECTIONAL_OFF DIRECTIONAL_ON SPIN_ON
#pragma multi_compile EDGE_SCALE_OFF EDGE_SCALE_ON
#pragma multi_compile EDGE_ALPHA_OFF EDGE_ALPHA_ON
#pragma multi_compile USER_FACING_OFF USER_FACING_ON
#pragma multi_compile SOFTPARTICLES_OFF SOFTPARTICLES_ON
#pragma multi_compile WORLDSPACE_OFF WORLDSPACE_ON
#pragma multi_compile SHAPE_CUBE SHAPE_SPHERE SHAPE_CYLINDER
#pragma multi_compile EXCLUSION_OFF EXCLUSION_ON

Adding all the multi_compile instructions will allow this shader to use the full feature set of PA Particle Field, if there are any features not required (like spin for example) it might be worth removing those instructions to reduce shader size.

Now the shader is fully compatible the particles can be made to fade at the fields edges using v2fParticleField’s color.a property.

Shader "CustomParticleField" {
	Properties {
	}
	SubShader {
        Pass {

        	//use alpha blending
        	Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM

			//use the vertex program defined in "ParticleField.cginc"
			#pragma vertex vertParticleFieldCube
			//define the fragment program
			#pragma fragment frag 

			//include the default Unity functions/variables etc
			#include "UnityCG.cginc"
			//include PA Particle Field
			#include "Assets/PopupAsylum/PAParticleField/Shaders/ParticleField.cginc"

			//include the full pa particle feature set
			#pragma multi_compile DIRECTIONAL_OFF DIRECTIONAL_ON SPIN_ON
			#pragma multi_compile EDGE_SCALE_OFF EDGE_SCALE_ON
			#pragma multi_compile EDGE_ALPHA_OFF EDGE_ALPHA_ON
			#pragma multi_compile USER_FACING_OFF USER_FACING_ON
			#pragma multi_compile SOFTPARTICLES_OFF SOFTPARTICLES_ON
			#pragma multi_compile WORLDSPACE_OFF WORLDSPACE_ON
			#pragma multi_compile SHAPE_CUBE SHAPE_SPHERE SHAPE_CYLINDER
			#pragma multi_compile EXCLUSION_OFF EXCLUSION_ON	

			//the fragment program using "v2fParticleField" data struct
			fixed4 frag(v2fParticleField i) : COLOR {
				fixed4 c = fixed4(1.0,0.0,0.0,1.0); //color the pixel red
				c.a *= i.color.a; //take the alpha from v2fParticleField's color.a property
				return c;
			}

			ENDCG
		}
	}
}

acafc596c4b29a9ad0bf6e1167dd0e24[1]

Custom Vertex Programs

A shader might be required to modify a vertex position or manipulate additional data in the vertex program, this is also supported by ParticleField.cginc.

The method PAParticleField(inout appdata_full) can be called from within a vertex program to apply the modifications Particle Field makes to the vertices of the mesh, modifications are made directly to the input data struct and the method returns the position of the pivot of the particle. Other instructions can be included before or after calling this method such as texture transforms or vertex displacement.

Finally the vertex must be positioned in view space which will either be Model-View-Projection or View-Projection depending on whether the field is using local or world space simulation, this can be done using the PAPositionVertex() method. The shader below uses a vertex program with a different data structure that applies a sine wave to the y position of the vertices based on _Time and the particles x position.

Shader "CustomParticleField" {
	Properties {
	}
	SubShader {
        Pass {

        	//use alpha blending
        	Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM

			//use a custom vertex program
			#pragma vertex vert
			//define the fragment program
			#pragma fragment frag 

			//include the default Unity functions/variables etc
			#include "UnityCG.cginc"
			//include PA Particle Field
			#include "Assets/PopupAsylum/PAParticleField/Shaders/ParticleField.cginc"

			//include the full pa particle feature set
			#pragma multi_compile DIRECTIONAL_OFF DIRECTIONAL_ON SPIN_ON
			#pragma multi_compile EDGE_SCALE_OFF EDGE_SCALE_ON
			#pragma multi_compile EDGE_ALPHA_OFF EDGE_ALPHA_ON
			#pragma multi_compile USER_FACING_OFF USER_FACING_ON
			#pragma multi_compile SOFTPARTICLES_OFF SOFTPARTICLES_ON
			#pragma multi_compile WORLDSPACE_OFF WORLDSPACE_ON
			#pragma multi_compile SHAPE_CUBE SHAPE_SPHERE SHAPE_CYLINDER
			#pragma multi_compile EXCLUSION_OFF EXCLUSION_ON

			struct v2f {
				float4 vertex : POSITION;
				fixed4 color : COLOR;
			};

			v2f vert(appdata_full v)
			{
				v2f o;
				//Initialize output using built in function
				UNITY_INITIALIZE_OUTPUT(v2f, o);			

				//Apply PAParticleField to the input
				PAParticleField(v);

				float4 finalPosition = float4(v.vertex.xyz, 1);
				finalPosition.y += sin(_Time.x + finalPosition.x);

				//pass on the color output from PAParticleField
				o.color = v.color;

				//position the vertex depending on defined WORLDSPACE
				o.vertex = PAPositionVertex(finalPosition);

				//pass on the output
				return o;
			}

			//the fragment program using custom v2f data struct
			fixed4 frag(v2f i) : COLOR {
				fixed4 c = fixed4(1.0,0.0,0.0,1.0); //color the pixel red
				c.a *= i.color.a; //take the alpha from v2fParticleField's color.a property
				return c;
			}

			ENDCG
		}
	}
}

sinefield

And That’s It…

Hopefully it’s quite clear that using these methods can produce very customized effects, future demos will also contain examples of these kinds of fields to really show off the field’s flexibility.

If you have any questions or anythings not clear let me know in the comments below or find me @markeahogan

Leave a comment

Your email address will not be published.

//