Hardware instancing and color

Hi,

I’m building a football stadium with thousands of moving plane primitives in the stands, that comprise a card stunt system. Each one will be coloured with a single colour, and will basically act like a pixel in an image, and when assembled together, they’ll recreate that image. I also want to be able to change the image, so in a “wave”, cards will gradually flip around and change to a new colour to reveal a new image within a second or two.

I’m using hardware instancing, as explained here: https://developer.playcanvas.com/en/user-manual/optimization/hardware-instancing/ and the example here: https://playcanvas.github.io/#graphics/hardware-instancing.html

…and it works great. Very smooth. I can move them all around independently using matrices and pc.BUFFER_DYNAMIC. Currently however, they’re all the same colour. I’ve found I can change that one colour by adjusting the colour of the material’s diffuse property of the model that they’re all using, but how would I go about doing this on an individual basis for each board, any time I want?

The reason I suspect it’s possible is that the documentation in the first link above uses colour as an example of a property that you can change in this way:
“Each instance of the mesh can have a different limited amount of state (for example, position or color). It’s a technique suitable to drawing objects such as trees or bullets, say”.

Can I do something with the material, or do I have to use a completely different method to render each colour?

I’m hoping there won’t be many different colours per image, so if absolutely necessary I could probably work out a system of swapping out boards that belong to different vertex buffers, but I’d much rather just change the colour properties of each one, just like I’m currently doing with their positions as they animate around independently.

Any suggestions much appreciated.

Hi @Playerthree,

I don’t have the answer to this, I haven’t tried this. But I would very much like to know that myself. Maybe something for @mvaligursky?

If you would like to explore more, here is the default instancing vertex format, a thought would be to create a custom VertexFormat and include one more semantic for coloring per instance (to use later in a custom pixel shader). Still not sure if that’s the way to go!

What Leonidas says. You currently call pc.VertexFormat.defaultInstancingFormat get get default instancing format. You could copy the function above, and add something like this

{ semantic: SEMANTIC_TEXCOORD6, components: 4, type: TYPE_UINT8 }

and then add 4 bytes for each vertex to vertex buffer.

You’d need to patch the shader to use the information, the same way those instancing positions are used. You’d need to copy and replace at least this:

and then do something similar with color chunk to what is done here in normal one:


see this bit in there

 #elif defined(INSTANCING)
    dNormalMatrix = mat3(instance_line1.xyz, instance_line2.xyz, instance_line3.xyz);

Other option would be to create a single mesh, and generate quads to it yourself - that’d likely win in the performance as well, and ease of use. This is probably the closest example we have:
https://playcanvas.github.io/#graphics/mesh-decals.html

Each decal is a quad positioned where you need it. It already has random color support. Personally, I would go this way for the best solution.

But both should work fine.

4 Likes

In general terms, you can do this with UV coordinates and one big image. Not sure of the PC version, but you would use instance # to lookup or derive UV coordinates for access into a texture (which could just be one pixel per card).

Then your audience can show videos too!

Bruce

Thanks for the advice all (sorry, forgot to reply earlier) - I only ever needed a small handful of different colours, so I ended up just making a different vertex buffer per colour, which didn’t have a big performance impact, so it was fine for my purposes. Hopefully these suggestions can help others though.

1 Like

I could find a nice workaround for this:

if you take a look at the setTRS method from mat4 you will see it has 4 rows, where fourth value is always hardcoded.

so you can assign these 4 values by hand in vertex shader

mat4 getModelMatrix() {
    return mat4(
        vec4(instance_line1.xyz, 0.0), 
        vec4(instance_line2.xyz, 0.0), 
        vec4(instance_line3.xyz, 0.0), 
        vec4(instance_line4.xyz, 1.0)
    );
}

now you have 4 values over, where you could set you color

complete code
material:

material.onUpdateShader = function(options) {
        options.useInstancing = true;
        return options;
    };
    
    const transformVS = `

out vec4 color;

mat4 getModelMatrix() {
    color = vec4(instance_line1.w, instance_line2.w, instance_line3.w, instance_line4.w);
    return mat4(
        vec4(instance_line1.xyz, 0.0), 
        vec4(instance_line2.xyz, 0.0), 
        vec4(instance_line3.xyz, 0.0), 
        vec4(instance_line4.xyz, 1.0)
    );
}

vec4 getPosition() {
    dModelMatrix = getModelMatrix();
    vec3 localPos = vertex_position;
    vec4 posW = dModelMatrix * vec4(localPos, 1.0);
    dPositionW = posW.xyz;
    vec4 screenPos = matrix_viewProjection * posW;
    return screenPos;
}
vec3 getWorldPosition() {
    return dPositionW;
}
`;
    
    const emissivePS = `

in vec4 color;

vec3 getEmission() {
    return color.rgb;
}
`;
    material.chunks.transformVS = transformVS;
    material.chunks.emissivePS = emissivePS;
    material.update();

setup matrices:


matrix.setTRS(pos, rot, scale);
    
matrix.data[3] = color.r;
matrix.data[7] = color.g;
matrix.data[11] = color.b;
matrix.data[15] = color.a;

4 Likes