上面的示例仅使用粒子的默认顶点流设置。这包括位置、法线、颜色和一个 UV。但是,通过使用自定义顶点流,可以将其他数据发送到着色器,例如速度、旋转和大小。
在接下来这一个示例中,着色器用于显示特殊效果,使速度更快的粒子看起来更亮,更慢的粒子更暗。有一些额外的代码用于根据速度来提高粒子的亮度(使用速度顶点流)。此外,因为此着色器假定该效果不使用纹理帧动画,所以从自定义流结构中将其省略。
以下是完整的着色器:
Shader "Instanced/ParticleMeshesCustomStreams"
{
Properties
{
_MainTex("Albedo", 2D) = "white" {}
}
SubShader
{
Tags{ "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma exclude_renderers gles
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma instancing_options procedural:vertInstancingSetup
#define UNITY_PARTICLE_INSTANCE_DATA MyParticleInstanceData
#define UNITY_PARTICLE_INSTANCE_DATA_NO_ANIM_FRAME
struct MyParticleInstanceData
{
float3x4 transform;
uint color;
float speed;
};
#include "UnityCG.cginc"
#include "UnityStandardParticleInstancing.cginc"
struct appdata
{
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
o.color = v.color;
o.texcoord = v.texcoord;
vertInstancingColor(o.color);
vertInstancingUVs(v.texcoord, o.texcoord);
#if defined(UNITY_PARTICLE_INSTANCING_ENABLED)
UNITY_PARTICLE_INSTANCE_DATA data = unity_ParticleInstanceData[unity_InstanceID];
o.color.rgb += data.speed;
#endif
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
half4 albedo = tex2D(_MainTex, i.texcoord);
return i.color * albedo;
}
ENDCG
}
}
}
此着色器包含 UnityStandardParticleInstancing.cginc(其中含有未使用自定义顶点流时的默认实例化数据布局)。因此,在使用自定义流时,必须重载该头部中定义的某些默认值。这些重载必须在包含上述项目之前执行。上面的示例设置了以下自定义重载:
首先,有一行代码使用 UNITY_PARTICLE_INSTANCE_DATA 宏,告诉 Unity 为自定义流数据使用名为“MyParticleInstanceData”的自定义结构:
#define UNITY_PARTICLE_INSTANCE_DATA MyParticleInstanceData
其次,另一个 define 语句告诉实例化系统在此着色器中不需要动画帧流 (Anim Frame Stream),因为此示例中的效果不适用于纹理帧动画:
#define UNITY_PARTICLE_INSTANCE_DATA_NO_ANIM_FRAME
第三,声明自定义流数据的结构:
struct MyParticleInstanceData
{
float3x4 transform;
uint color;
float speed;
};
这些重载全部都是在包含 UnityStandardParticleInstancing.cginc 之前进行的,因此着色器不会对这些定义使用自己的默认值。
编写结构时,变量必须与粒子系统渲染器模块中检视面板 (Inspector) 中列出的顶点流相匹配。这意味着必须在渲染器模块__ UI__(即用户界面,User Interface)让用户能够与您的应用程序进行交互。Unity 目前支持三种 UI 系统。更多信息
See in Glossary 中选择要使用的流,然后以相同的顺序将它们添加到自定义流数据结构中的变量定义,从而使它们匹配:
第一项 (Position) 是必填项,因此无法将其删除。可使用加号和减号按钮自由添加/删除其他条目,从而自定义顶点流数据。
列表中以 INSTANCED 结尾的条目包含实例数据,因此必须将它们包含在粒子实例数据结构中。直接附加到单词 INSTANCED 后面的数字(例如 INSTANCED0 中的 0 和 INSTANCED1 中的 1)表示变量必须在结构中的初始“transform”变量之后出现的顺序。尾部字母(.x、.xy、.xyz 或 .xyzw)表示变量的类型,并映射到着色器代码中的 float、float2、float3 和 float4 变量类型。
可在粒子实例数据结构中忽略在列表中显示但结尾没有 单词 INSTANCED 的任何其他顶点流数据,因为它不是着色器要处理的实例化数据。此数据属于源网格,例如 UV、法线和切线。
完成我们的示例的最后一步是将速度应用于顶点着色器内的粒子颜色:
#if defined(UNITY_PARTICLE_INSTANCING_ENABLED)
UNITY_PARTICLE_INSTANCE_DATA data = unity_ParticleInstanceData[unity_InstanceID];
o.color.rgb += data.speed;
#endif
必须将所有实例化代码包装在 UNITY_PARTICLE_INSTANCING_ENABLED 的检查中,以便在未使用实例化时进行编译。
此时,如果要将数据传递给片元着色器,可将数据写入 v2f 结构,就像对任何其他着色器数据的处理一样。
此示例描述了如何修改自定义着色器以便用于自定义顶点流,但可以对表面着色器应用完全相同的方法来实现相同的功能。