Hardware Instancing; Is it possible to pass in an array of Vector3 into the Vertex Shader?

When Hardware Instancing; Is it possible to pass in an array of Vector3 filled with color values R, G, B into the Vertex Shader just like we are passing in the VertexBuffer?

I’m hardware instancing particles (HLSL)
I would like to add different colors for every particle, therefore add an array of color values into the Shader too.

Is that possible?

Regards, Morgan

I’m not super familiar with hardware instancing, so apologies if I’m misunderstanding.

Do mean pass an extra Vector3 along with each vertex in the VertexBuffer? You can add extra values to the VertexDeclaration. VertexPositionColor may be what you’re looking for, or you can define your own.

1 Like

You can use any format for vertices you want. You just gotta figure out what data you want to pass to the shader and how to efficiently encode it. For instancing you always need at least two vertex buffers. Usually that’s one vertex buffer for the model, and one vertex buffer for the instances.

Let’s take an example in which you have one model with normals and you want to instance it with different colors and with an offset. First you decide what data you want in the buffers. We want

  1. A buffer for model data with
  • a position encoded as a Vector3 (this is relative and will be added to the offset in the shader)
  • normals encoded in a Vector3
  1. A buffer for instance data with
  • A color for the model encoded as Color. Note that Color is smaller than Vector3 because Color consists of 4 bytes, one for each channel, while Vector3 consists of 3 floats = 3 * 4 = 12 bytes.
  • An offset encoded as a Vector3.

MonoGame provides a VertexPositionColor, but no IVertexType for a normal and float, so we’d need to create that one ourselves.

Once we loaded our data into the buffers we need to wrap them in a VertexBufferBinding. We need to set the InstanceFrequency property of the VertexBufferBinding to make sure data from our first buffer is reused for every instance, while the next data is always taken from the second buffer. XNA docs say

The frequency is the number of times to draw the same geometry each time
draw is called: 1 means draw one instance, 2 means draw two instances,
and so on. Use 0 if you are not instancing.

Buffer 1 is our model, so we don’t want to do instancing and set the InstanceFrequency to 0.
We want to use 1 element from the second buffer per instance, so it should draw the same geometry just once. We set the InstanceFrequency to 1.

Then we can set them on the GraphicsDevice by calling GraphicsDevice.SetVertexBuffers. Make sure the semantics in the shader match the semantics in your vertex format. After that you can call GraphicsDevice.DrawInstancedPrimitives.

Here’s how the data will be used, with B1 being our model buffer (with 3 vertices in my beautiful drawing) and B2 our the buffer with instance data.

I hope that clarifies how instancing works :slight_smile: Not exactly sure if that answers your question, but it seemed to me you did not understand instancing right. It does not make sense to only have a color per instance, because they’d all be drawn at the same place. You usually want some kind of offset or transform too.

Regarding your particle system: Think what data you want to have in each buffer and if it makes sense to have data shared across all particles. If you have simple particles and can manage with one vertex per particles (which might be of a custom vertex type), you don’t need instancing at all. If you want some more help regarding the design, could you elaborate on what you want exactly from your particle system?

If you do want to use instancing, I recommend you use VertexElementFormat.Color for your instances buffer. It only uses 4 bytes: 1 byte for each color channel; red, green, blue and alpha. If you use a Vector3 you will use 12 bytes per instance, so that’s 3 times as much data to send to the GPU.

3 Likes

Aah lovely you guys, thank you a lot I made a custom one and it’s working like a charm

public struct CustomVertex1 : IVertexType
{
    Matrix instanceTransform;
    Vector3 instanceColor;

    static VertexDeclaration instanceVertexDeclaration = new VertexDeclaration
    (
        new VertexElement(0, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 0),
        new VertexElement(16, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 1),
        new VertexElement(32, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 2),
        new VertexElement(48, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 3),
        new VertexElement(64, VertexElementFormat.Vector3, VertexElementUsage.Color, 0) // <-- My special color for a particle
    );

    public CustomVertex1(Matrix instanceTransform, Vector3 instanceColor)
    {
        this.instanceColor = instanceColor;
        this.instanceTransform = instanceTransform;
    }
    public Matrix InstaceTransform
    {
        get { return this.instanceTransform; }
        set { this.instanceTransform = value; }
    }
    public Vector3 InstanceColor
    {
        get { return this.instanceColor; }
        set { instanceColor = value; }
    }
    VertexDeclaration IVertexType.VertexDeclaration
    {
        get { return instanceVertexDeclaration; }
    }
}

And using it with

instanceVertexBuffer = new DynamicVertexBuffer(gD, typeof(CustomVertex1),
                                                           this.mCV.Length, BufferUsage.WriteOnly);

And setting the data with

instanceVertexBuffer.SetData<CustomVertex1>(this.mCV);//, 0, this.mCV.Length, SetDataOptions.Discard);

Thanks both of you

Regards, Morgan

1 Like

Well great explanation of how it works, helped me eliminating a lot of question marks that I’ve had, thanks again.

Regads, Morgan

1 Like