【UnityShader】如何自定義unity粒子系統使用的shader

前幾天特效那邊讓改一下一個粒子系統使用的shader,說是shader的一個值希望關聯上粒子的某個值。

我不假思索地就準備寫個腳本掛上去傳個值給shader。等腳本寫好,測試的時候才突然發現,傳值過去後,所有的粒子都是同時改變參數……然後就研究了一下unity粒子系統的自定義shader。

Unity粒子系統的每個粒子在生命週期裏都可以看做一個播放動畫的物體,生命週期結束,動畫就播完了。

如果需要單獨自定義每個粒子在生命週期裏的變化,可以使用腳本改變粒子系統的值,或者自定義shader。不過如果只是普通的更改值,只能使全部粒子發生變化,無法與Lifetime掛鉤,也就是逐粒子進行值的設定。

如果需要自定義一些可以跟隨Lifetime變化的值,有兩種方法:

  1. 使用Particle System類下的SetCustomParticleData方法和GetCustomParticleData方法來得到並設定當前粒子的數據。

  2. 第二種方法就是接下來說的,自定義匹配unity粒子系統的shader

Unity內置的粒子shader有兩種,一個Standard Surface,一個Standard Unlit,如果在material裏選擇這兩種shader,會出現這樣的字樣,這就是自定義粒子shader的重點——頂點流(Vertex Streams)

Vertex Streams

Unity在渲染粒子時,會先根據粒子系統的設置計算一份數據,這就是VertexStreams。在自定義shader里加上預編譯指令#pragma multi_compile_particles ,再在頂點結構體裏聲明對應的數據,就可以使用粒子系統中計算的這份數據了,也就是自定義粒子系統使用的shader。

默認的VertexStreams數據結構爲:粒子的Position,normal,Color,uv1。如果需要自定義頂點流,需要在粒子系統的Renderer頁面勾選Custom Vertex Streams,再根據shader裏所需的數據進行增減。

unity在各個數據後標註了該數據在頂點結構體裏儲存的位置,比如uv1和uv2都存在TEXCOORD0裏,一個在xy分量,一個在zw分量。

Custom Vertex Streams

粒子系統裏指定的動畫,unity會把動畫的關鍵幀傳給不同的uv,再給出當前時間位置的blend值,即可得到當前時間位置的動畫。

以下是幫特效美術那邊改動的一個粒子系統使用的扭曲shader,粒子系統的Alpha值可以影響扭曲程度。

Shader "heat_distortion_pc" 
{
	Properties 
	{
		[HideInInspector] _MainTex ("Particle Texture", 2D) = "white" {}

		_AlphaTex ("Alpha (A)", 2D) = "white" {}
		_NoiseTex ("Noise Texture (RG)", 2D) = "white" {}				
		_HeatTime  ("Heat Time", range (0,1.5)) = 1
		_HeatForce  ("Heat Force", range (0,1)) = 0.1
	}

	Category 
	{
		Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent+10" "PreviewType"="Plane" }

		Blend SrcAlpha OneMinusSrcAlpha
		ColorMask RGB
		Cull Off Lighting Off ZWrite Off

		SubShader 
		{
			GrabPass 
			{							
				Name "BASE"
				Tags { "LightMode" = "Always" }
 			}

			Pass 
			{        
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma fragmentoption ARB_precision_hint_fastest
				#pragma target 2.0
				#pragma multi_compile_particles           
				#include "UnityCG.cginc"
            
				sampler2D _AlphaTex;
				float4 _AlphaTex_ST;

				sampler2D _NoiseTex;
				float4 _NoiseTex_ST;

				sampler2D _MainTex;
				float4 _MainTex_ST;

				sampler2D _GrabTexture;
						
				float _HeatForce;
				float _HeatTime;

				struct appdata_t 
				{
					float4 vertex		: POSITION;
					fixed4 color		: COLOR;
					float4 texcoords	: TEXCOORD0;
					float texcoordBlend : TEXCOORD1;
				
					UNITY_VERTEX_INPUT_INSTANCE_ID
				};

				struct v2f 
				{
					float4 vertex		: SV_POSITION;
					fixed4 color		: COLOR;
					float4 uvs_partex	: TEXCOORD0;       
					float4 uvs_mytex	: TEXCOORD1;
					float4 uv_grab		: TEXCOORD2;
					fixed blend			: TEXCOORD3;
				
					UNITY_VERTEX_OUTPUT_STEREO
				};
                        

				v2f vert (appdata_t v)
				{
					v2f o;
					UNITY_SETUP_INSTANCE_ID(v);
					UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); 
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.color = v.color;

					o.uvs_partex.xy = TRANSFORM_TEX(v.texcoords.xy,_MainTex);
					o.uvs_partex.zw = TRANSFORM_TEX(v.texcoords.zw,_MainTex);
					o.uvs_mytex.xy = TRANSFORM_TEX(v.texcoords.xy,_AlphaTex);
					o.uvs_mytex.zw = TRANSFORM_TEX(v.texcoords.xy,_NoiseTex);

					#if UNITY_UV_STARTS_AT_TOP
						float scale = -1.0;
					#else
						float scale = 1.0;
					#endif

					o.uv_grab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
					o.uv_grab.zw = o.vertex.zw;

					o.blend = v.texcoordBlend;

					return o;
				}

            
            
				fixed4 frag (v2f i) : SV_Target
				{               
					fixed4 colA = tex2D(_MainTex, i.uvs_partex.xy);
					fixed4 colB = tex2D(_MainTex, i.uvs_partex.zw);
					fixed heatForce = 2.0f * i.color.a * lerp(colA.a, colB.a, i.blend) * _HeatForce;
					fixed4 parCol = 2.0f * i.color * lerp(colA, colB, i.blend);

					half4 offsetColor1 = tex2D(_NoiseTex, i.uvs_mytex.zw + _Time.xz * _HeatTime);
					half4 offsetColor2 = tex2D(_NoiseTex, i.uvs_mytex.zw - _Time.yx * _HeatTime);
					i.uv_grab.x += ((offsetColor1.r + offsetColor2.r) - 1) * heatForce;
					i.uv_grab.y += ((offsetColor1.g + offsetColor2.g) - 1) * heatForce;
	
					half4 grabCol = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uv_grab));
				
					half4 texCol = tex2D(_AlphaTex, i.uvs_mytex.xy) * parCol;

					return texCol * grabCol;
				}
				ENDCG 
			}
		}   

		SubShader 
		{
			Blend DstColor Zero
			Pass 
			{
				Name "BASE"
				SetTexture [_MainTex] {	combine texture }
			}
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章