Custom Particle Data in Shader

Accessing custom particle data in shaders is one of the advertised features of the particle system improvements, but it’s only been mentioned, I haven’t been able to find an example or explanation on how to. Can anyone provide a simple shader using custom particle data?

A bit of a side question in C#, does GetCustomParticleData return the list in the same order for particles in GetParticles?

To answer the side question: yes, the order is the same.

I dont have a great example handy, but here is a shader that uses custom data to do some custom flipbook stuff. hope it helps. i can post a better example later in the week but im not in the office right now :slight_smile:

Shader "VertexStreams/Custom 1" {
Properties {
    _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
    _MainTex ("Particle Texture", 2D) = "white" {}
    _OffsetValue("Offset Value", float ) = 1
}

Category {
    Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    Blend SrcAlpha OneMinusSrcAlpha
    ColorMask RGB
    Cull Off Lighting Off ZWrite Off
    //#pragma target 5.0

    SubShader {
        Pass {
       
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_particles
            #pragma multi_compile_fog
           
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            fixed4 _TintColor;
            float _OffsetValue;
           
            struct appdata_t {
                float4 vertex : POSITION;
                float4 normal : NORMAL;
                float4 tangent : TANGENT;
                fixed4 color : COLOR;
                float2 texcoord : TEXCOORD0;
                float4 customData : TEXCOORD1;
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                float2 texcoord : TEXCOORD0;
                float4 customData : TEXCOORD1;
                UNITY_FOG_COORDS(2)
            };
           
            float4 _MainTex_ST;

            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.vertex *= v.customData.x;
                o.color = v.color;
                o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
                o.texcoord.x += v.customData.y;
                o.customData = v.customData;
                UNITY_TRANSFER_FOG(o,o.vertex);

                return o;
            }
           
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord);
                UNITY_APPLY_FOG(i.fogCoord, col);
                fixed4 col2 = fixed4(i.customData.x, i.customData.y, i.customData.z, 1);
               
                return col;
            }
            ENDCG
        }
    }   
}
}

Thanks for the shader!
This works great for billboard, however I am using mesh particles, sorry for not stating that before. I noticed you can only modify vertex streams for billboards, does that mean custom data for mesh particles is unsupported?

Ah, yes, currently custom vertex streams for mesh particles are unsupported.

Thank you for the support, wanted to test GPU Instancing vs mesh particles on a project of mine, but seems like GPU Instancing is the way to go.

We are working on integrating the GPU instancing with mesh particle rendering, but it’s not ready yet :slight_smile:

That’s fantastic news, can’t wait to get my hands on that!

1 Like

Speaking of custom data, if I understood correctly currently we can only pass that data from a script using SetCustomParticleData.

Are there any plans to add “custom data” modules?
So that we can control the custom data like any other vertex-based data through the UI. It would also allow more flexbility than a script-based approach I think.
Here’s a small mockup to illustrate the idea:
2786557--201882--Unity55-Particle-Custom-Data-Modules.png
(the vector type would also have a W curve here)

Also would like to know the reason behind “no vertex streams for mesh particles”, is it technical? Are you hoping to eventually support it?

Anyway, love the VFX-related improvements lately, thanks for that! :slight_smile:

4 Likes

Hey,

We don’t currently have any plans to support the custom data via the UI, though it’s an interesting idea, so I will add it to our roadmap, thanks.

Regarding the mesh particle thing - it would be very expensive to add to mesh particles, due to the way we build mesh particle vertex data all on the CPU. However, there is no technical limitation to adding it, so perhaps we will expose it and then it is up to the user’s if their performance budget allows using it :wink:

We are also working on adding GPU instancing support to the mesh particles at the moment. It would be very easy to and efficient to expose the custom vertex stream data to the GPU instancing implementation.

Thanks for the feedback!

this is whats missing to make the feature really usable I think.
Using scripts to add your own data is all fine and good. But it can not be properly tweaked or previewed ( not that i know of anyway) without starting the game. And while running the game you can’t really tweak your parameters because they are just lost…
I think anyone working with vfx would appreciate that feature being bumped to the very top of the roadmap :slight_smile: I know I would.
Great work on the improvements on the whole though, love the trails.

1 Like

Out of curiosity, what kind of custom data would you imagine setting up via the Inspector? Any kind of example?
I ask because we imagined it being a mechanism to assign dynamic game-logic-based values to particles, i.e. the kind of data that is only known from scripts during gameplay.

Thanks!

For me it’d mainly be to control custom shaders arbitrarily.

For example a particle shader that has an erosion value (which smoothsteps() based on the texture alpha), and I want to control that value precisely.
The current workaround would be to either use vertex colors (but then you lose the per-particle color ability for at least one channel), or lifetime in 5.5 but then the value would be linearly tied to the particle lifetime.

Another basic usage would be to have an easy interface to get random values per particle (with a “random between two constant” property for example).
Although this might be doable with the script interface? I haven’t tested it in-depth yet, but I saw you have to supply a List, so what happens to newly created particles if they are outside this list’s range? Does it get a zero Vector4 or it cycles back to the start of the list?

Also updating that list every frame sounds way slower to me than the ParticleSystem’s interface that automatically updates values based on curves and lifetimes for example. But maybe that’s my imagination?

4 Likes

As a VFX artist, I agree with Jean-Moreno. His recommended custom data interface modules was the behavior I was expecting when you announced the feature. I was sad when I found out that this is not in fact the case. It would be way more useful for VFX artists if you implement Jean’s suggestion, as we otherwise need to set up arbitrary scripts per fx. Myself and many great VFX artists that I work with use unity daily, but cant code at all. A visual interface embedded in the particle system would be indispensable for us.

2 Likes

Obviously that’s one clever way to use it, but for the most part particle Jean and Weibe hit the nail on the head.

I’ve previously used vertex colors as a way to pass a value to the material. Because you can change color based on (whatever) and set it to some random value nicely, it’s sort of an ideal way to get some information to a shader. Obviously, that means using vertex colors and fade-in/fade-out normally then goes out the window.

If you guys just copy all the [color by…] sections and just cross out color and replace it with [custom value 1 (or 2)] that’d probably be perfect.

I strongly agree with Jean-Moreno, GWiebe, and Jesus. I currently control most of the dynamic properties of my particles by hijacking particle color. This is a problem for two reason:

  1. It’s unintuitive if I hand the effect off to anyone else. I regularly use the Red channel as a random value in shaders to randomize UV offsets, Green to control heat values in blackbody shaders, Blue to control the strength of UV warp textures, or Alpha to control the minClip value in an alpha erosion shader – just to name a few. It makes my particle color values hard to edit and only barely human-readable (for an actual example/use case see the FXVille Blood FX Pack that I made, and its associated documentation).

  2. Particle Color in unity seems to be tracked in some bit depth lower than float (I suspect it’s 8 bit), because I often see what looks like 255 discrete steps between 0 and 1 values. This stops me from using my techniques on longer-lived particles, since it would make the steps more obvious. In my testing so far, custom data seems to pass 16 bit floats, which is super useful for me!

It would be really nice to have a UI interface like Jean-Moreno mocked up (excepting that the vector example should have a W graph too, since it’s for authoring a float4). I love unity’s curve editor and it would be a really pleasant way to author data for particle shaders. I use this kind of interface all the time when I’m working in Unreal (but their curve editor is terrible).

When I read that custom particle data was on its way, this is the implementation I was excited for.

I agree with the predecessors. Without a dedicated module the whole idea of sending custom particle data will be very limited and rarely useful. What we, VFX artist, tent to do is we use custom shader data to additionally animate particles over its lifetime. For example, for a fire shader it is very common to have additional parameter which distorts the UV. The strength of the distortion needs to be precisely defined over particle’s/emitter’s lifetime (otherwise you cannot achieve a proper visual quality). For explosion shader, we usually simulate smoke and fire and save them into separate textures. Then, you can create a shader which combines both textures taking smoke as a diffuse and fire as an emissive. Via custom parameter we set fire’s color, intensity (preferably in high dynamic range), you can then use “color over lifetime” module to set smoke’s opacity&color. These are just some common uses. There are a lot more examples. If you need additional information on the use cases, plase let me know - I will provide you with all the information that I’ve got.
VFX in general are very specific and they require a lot of precise tweaking.

If you need some direct references you should definitely dig into Unreal Engine so called “dynamic parameter” feature built-in into Cascade. Essentially, they have a special dedicated module which allows you to set custom shader data and drive it over particle’s lifetime, set the value once or drive it over emitter’s lifetime. I know that you’ve probably heard a lot of “unreal this, unreal that” but “dynamic parameter” approach is the best I have even encountered and fits Unity’s semi-modular approach.

@richardkettlewell Any news on this? Is it being considered or at least discussed internally?
I’m sure we can find lots more of other VFX Artists who would be very interested in this!

Hey, yes, sorry been on vacation :wink: Thanks for the feedback, it’s good to know that this would be useful. We will add it to our roadmap!

Right now, you can do a workaround by adding an AnimationCurve to a script, and setting the custom particle data based on the particle age and by evaluating that curve yourself.

I know this approach isn’t as good as a native implementation where we provide the UI, and support all the particle curve modes, but maybe it’s better than nothing, depending on your use case :slight_smile:

2 Likes

Cool, thanks for the heads up!

Just curious, how would you expect this workaround to perform compared to a native implementation?
It sounds really slow having to get each particle lifetime to sample the curve every frame.

Hi,

Welcome back Richard :wink:

I agree with all previous suggestions about the UI for custom data, because for now assigning custom data in a vertex stream is not really optimal :

When a particle is emited, its custom data value is always Vector4(0,0,0,0).
So we have to identify somehow (by script) which particles were emitted since the last update, and then set the custom values for these particles.
In this configuration the custom values can only be processed one frame after the particles birth, I’m not sure of it, but this can be a problem in some cases.
Also, each time, the full array of custom data has to be retrieved, modified, and set back to the stream, all by script.

I don’t know how difficult it is, but in addition to the UI for custom vertex stream, would it be possible to add a native function to the Monobehavior class, something like “OnParticleBirth(Particle[ ] particles)”, where programmers could define some arbitrary values to all new particles (or tweak the existing ones).