There are a few things that I would do differently. I’ll start with the graph I did for my test:
The first difference you may notice is that the event is plugged directly to the Initialize context. Event attributes get passed through SpawnEvents, but if you have more than one in the same frame, it will only keep one value. Also, I can pass the count in the event directly, so why bother?
Next thing, is the entityID. You are setting the entity ID value as an exposed variable, and that will kind of work if you don’t have more than one entity spawning particles per frame. But I think it is safer to pass it in the event itself, as an event attribute. To do that, I created a custom attribute entityID, and I set it per-particle in Initialize, reading from the source (the event). This custom attribute will be stored during the entire particle lifetime, so the particle will always know which entity spawned it.
Here is the small component I created to send the event. I do it OnEnable, but you get the idea:
using UnityEngine;
using UnityEngine.VFX;
[ExecuteAlways]
public class SpawnFlash : MonoBehaviour
{
public int entityID;
public VisualEffect visualEffect;
private VFXEventAttribute eventAttributes;
private ManageEntityMatrices manageEntityMatrices;
void OnEnable()
{
if (visualEffect != null)
{
if (eventAttributes == null)
eventAttributes = visualEffect.CreateVFXEventAttribute();
if (manageEntityMatrices == null)
manageEntityMatrices = visualEffect.GetComponent<ManageEntityMatrices>();
eventAttributes.SetFloat("spawnCount", 16.0f);
eventAttributes.SetInt("entityID", entityID);
manageEntityMatrices.SetMatrix(entityID, transform.localToWorldMatrix);
visualEffect.SendEvent("SpawnFlash", eventAttributes);
}
}
}
Finally, we have to deal with the GraphicsBuffer. In your code, you are creating the graphics buffer every time, and destroying it before the graph even has time to run.
This is what I do instead: I create one single GraphicsBuffer, that will be set to to the visual effect as an exposed property. This GraphicsBuffer will be shared by all entities, indexed by their entity ID. It is your responsibility to generate these entity IDs, and to guarantee that they are unique (although you can reuse) and that you have room for all in your buffer.
These is the component that I attached to the visual effect, to manage the GraphicsBuffer:
using UnityEngine;
using UnityEngine.VFX;
[ExecuteAlways]
public class ManageEntityMatrices : MonoBehaviour
{
private GraphicsBuffer entitiesGraphicsBuffer;
void OnEnable()
{
VisualEffect visualEffect = GetComponent<VisualEffect>();
if (visualEffect != null )
{
entitiesGraphicsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 10, sizeof(float) * 16);
Matrix4x4[] matrixArray = new Matrix4x4[10];
entitiesGraphicsBuffer.SetData(matrixArray);
visualEffect.SetGraphicsBuffer("MatrixBuffer", entitiesGraphicsBuffer);
}
}
void OnDisable()
{
if (entitiesGraphicsBuffer != null )
{
entitiesGraphicsBuffer.Dispose();
}
}
public void SetMatrix(int entityID, Matrix4x4 matrix)
{
if (entitiesGraphicsBuffer != null)
{
Matrix4x4[] matrixArray = { matrix };
entitiesGraphicsBuffer.SetData(matrixArray, 0, entityID, 1);
}
}
}
In that script you can also see how we update one matrix at a time when we spawn a new flash, called from the first script. If your object is moving, you should set its matrix every frame! If you do that, consider grouping all the matrices together for a single SetData.
After that, we can easily access the graphics buffer and get the matrix (make sure to set up the node correctly):
You can use your own struct types as well, but you need to follow some rules:
https://docs.unity3d.com/Packages/com.unity.visualeffectgraph@17.0/manual/Operator-SampleBuffer.html
In my example I’m only multiplying the position at the end, but technically you could extract position and forward direction from your matrix directly, without having to store them.
And that is pretty much it. It is a small example but it showcases some advanced features, that I hope will be useful in other scenarios as well