Node material instance U, V offset and color?

Hi @PatrickRyan

In relation to your great article Creating Variations in Instances Using Node Materials, I’m wanting to have different UVs or UV offsets per instance, but also different colors per instance, which I’ll blend with a texture.

I’m confused by the use of mesh.color as I thought that was for vertex color, whereas mesh.instanceColor is what I thought I’d have to use?

If I wanted unique U and V offsets, and colors per instance, can this be achieved with node material?

I’m basically trying to achieve a similar result to this PG, but using NodeMaterial instead.

For context, what I’m trying to do is create a virtual keyboard where each keycap is an InstancedMesh with a single NodeMaterial that allows individual keycap color change and UV offsets to determine which legend/glyph is displayed on top of the keycap.

I’m not sure if it’s the “right” way to do it, but following your article @PatrickRyan I was able to use a Color4 (which I’ll change to mesh InstanceColor or Color), split that with a VectorSplitter, use x and y for the UV offsets and z=1 or z=0 to select between 2 different user-defined colors for instanced keycaps on a single themed keyboard, like this:

I only have the 2 keycap colors per keyboard theme, so it seems to work.

Am I on the right track or is there an even better way?

cc @Evgeni_Popov who can help on this.

@inteja, the reason I am using mesh.color in the example is because I created all of my random values with instance.instancedBuffers.color which would be accessed with mesh.color. Basically, I am creating an array of values, one color set for each instance, and then the node material is accessing the correct color value set based on the instance’s position in the buffer.

You could do something similar and instead of creating an instanced buffer of random values, you could assign one of the two user-defined colors to each instance. Then you would access them in the same way and you would have control over which instance receives which color. The trick here is that you need to store the colors of the individual instances somewhere to render them correctly, and the instancedBuffers.color is a good place to do so. You can then use the color from the buffer as your albedo/base color for the individual key cap, or you can multiply it with a greyscale texture value if you need to add variation in the key cap color.

I hope this makes sense, but please let me know if you have more questions.

Thanks @PatrickRyan

My only remaining questions are:

  1. In NME there’s mesh.color and mesh.instanceColor nodes with the tooltip for the former saying it’s for vertex color, so I’m unsure why this is used in this case rather than the latter, or is the tooltip incorrect?
  2. There’s a limit to the number of instance buffers I can create and use with NME right? It’s only color? So I can’t register a uvOffset instanced buffer and use that because there’s no NME node corresponding to that?

@inteja, I agree with you that the tool tip for instance color is misleading. But the answers to your questions are:

  1. The reason that we are using mesh.color in the example is because we are setting custom VertexBuffers for vertex color on each instance. Because we’ve written a custom color into the vertices, we access them with mesh.color. For mesh.instanceColor, if my memory serves me correctly (but @sebavan or @Evgeni_Popov can correct me if I am wrong) this mesh parameter refers only to thin instances so there was a differentiator from mesh.color. We should update the tooltip to be clearer.
  2. As you can see in the vertex buffers link above, there are many different “KINDS” of mesh attributes that can be stored as a custom buffer. As you can see in this code example, the VertexBuffer is being defined as BABYLON.VertexBuffer.ColorKind, but it could just as easily be defined as BABYLON.VertexBuffer.UVKind or any of the six UV sets that the engine supports. In this case, you would add the final value with UV offset applied to the value to the buffer and your mesh.uv block should pass the correct value.

The reason I chose to use color in the example I did was that mesh.color does not affect anything in the mesh if I don’t reference the vertex color in the node material. Changing other parameters like UV, Position, Normal, etc. would have an effect on the final mesh. However, if you want to control your color and UVs per instance, then you have all the tools you need by writing custom buffers for each and then accessing them with the appropriate mesh parameter input blocks. Does this make more sense?

1 Like

Thanks @PatrickRyan

That answers my questions. I think I just misunderstood the docs and examples to mean that there was some hard limit to only color when using instance buffers with NME. I will try registering custom UV instance buffers and modify my node material accordingly.

1 Like

instanceColor is actually the instance color, and color is the vertex color.

The reason color works as an instance color in the example is that the vertex color buffer is created with a registerInstancedBuffer("color", 4); call, which creates an instance buffer for “color”. Before instanceColor existed (feb 2022), this was the only way to have a per-instance color, and it was not possible to have an instance color + a vertex color at the same time.

To make the instance color work with thin instances, you have to use the instanceColor attribute, because thinInstanceRegisterAttribute("color", 4) will actually be translated to thinInstanceRegisterAttribute("instanceColor", 4) (we did this to retain backward compatibility). So, overall, instanceColor should always be used when you want to use a per-instance color, and you should register the buffer by calling registerInstancedBuffer("instanceColor", 4); / thinInstanceRegisterAttribute("instanceColor", 4).

Here is an example that shows how to create a custom attribute for use in the NME:


Thanks for explaining the distinction between and history behind color vs instanceColor. I switched to using instanceColor and that works fine too.

Thanks also for the link on how to inject custom attributes for use in NME. That’ll be handy for the future, but I think for my current use case, although the naming isn’t ideal, I’m just using one of the unused UV channels (uv3) as the UV offset for one of the in use UV channels, which requires less code and complexity.