Пример создания экземпляра Particle System GPU с настраиваемыми потоками вершин во Встроенном Render Pipeline
Приведенные выше примеры используют только настройку потока вершин по умолчанию для частиц. Это включает позицию, нормаль, цвет и UV. Однако, используя пользовательские потоки вершин, вы можете отправлять другие данные в шейдер, такие как скорости, вращения и размеры.
В следующем примере шейдер создаёт специальный эффект: быстрые частицы становятся ярче, а медленные — тусклее. Дополнительный код осветляет частицы в зависимости от их скорости, используя вершинный поток Speed. Кроме того, поскольку этот шейдер исходит из того, что анимация по листу текстур применяться не будет, она не включена в структуру пользовательского потока.
Вот полный Shader:
Shader "Instanced/ParticleMeshesCustomStreams"
{
Properties
{
_MainTex("Albedo", 2D) = "white" {}
}
SubShader
{
Tags{ "RenderType" = "Opaque" }
LOD 100
Pass
{
HLSLPROGRAM
#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;
}
ENDHLSL
}
}
}
Шейдер включает UnityStandardParticleInstancing.cginc, в котором содержится макет данных инстансинга по умолчанию — на случай, когда Custom Vertex Streams не используются. Поэтому при работе с пользовательскими потоками часть значений по умолчанию, объявленных в этом заголовке, нужно переопределить. Эти переопределения должны идти до В приведенном выше примере установлены следующие пользовательские переопределения:
Во-первых, есть строка, указывающая Unity использовать для данных пользовательского потока собственную структуру «MyParticleInstanceData» через макрос UNITY_PARTICLE_INSTANCE_DATA:
#define UNITY_PARTICLE_INSTANCE_DATA MyParticleInstanceData
Далее, другое определение сообщает системе экземпляров, что поток кадров Anim не требуется в этом шейдере, так как эффект в этом примере не предназначен для использования с анимацией листов текстуры:
#define UNITY_PARTICLE_INSTANCE_DATA_NO_ANIM_FRAME
В-третьих, объявляется структура для данных пользовательского потока:
struct MyParticleInstanceData
{
float3x4 transform;
uint color;
float speed;
};
Все эти переопределения происходят до включения UnityStandardParticleInstancing.cginc, так что шейдер не использует свои собственные значения по умолчанию для этих определений.
Описывая свою структуру, следите за тем, чтобы переменные соответствовали вершинным потокам, перечисленным в инспекторе модуля рендерера Particle System. То есть выберите нужные потоки в интерфейсе модуля Renderer и добавьте их в объявления переменных своей структуры данных потока в том же порядке, чтобы они совпадали:
Первый элемент (Позиция) является обязательным, поэтому его нельзя удалить. Вы можете свободно добавлять/удалять другие элементы, используя кнопки плюс и минус, чтобы настроить поток данных вершины.
Записи в перечне, за которыми следует INSTANCED содержат данные экземпляра, поэтому их нужно включить в свою структуру данных экземпляра частицы. Число, приписанное сразу после слова INSTANCED (например, ноль в INSTANCED0 и один в INSTANCED1) указывает порядок, в котором переменные должны идти в вашей структуре, после Последующие буквы (.x, .xy, .xyz или .xyzw) указывают тип переменной и отображение на float, float2, float3 и float4 типы переменных в вашем коде шейдера.
Вы можете опустить любые другие данные потока вершин, которые появляются в списке, но это не не слово INSTANCED после него, из структуры данных экземпляра частицы, поскольку это не инстансированные данные для обработки шейдером. Эти данные принадлежат исходному мешу — например, UV, нормали и касательные.
Последним шагом для завершения нашего примера является применение скорости к цвету частиц внутри Vertex Shader:
#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 — так же, как поступают с любыми другими данными шейдера.
В этом примере описывается, как изменить пользовательский шейдер для использования с пользовательскими потоками вершин, но вы можете применить точно такой же подход к шейдеру поверхности, чтобы достичь той же функциональности.