Instancing a mesh with shaderMaterials

Hi ya all, I wanted to use instances for every possible mesh in my scene and encountered some problems on the way. If you have a Mesh with a custom shader instancing isn’t working flawless, i already found some post and got that i have to adjust the custom shader, but i can’t figure out how. Here is the cutom vertexShader used in this case:

// Precision
	+ 'precision highp float;\r\n'
	// Attributes
	+ 'attribute vec3 position;\r\n'
	+ 'attribute vec2 uv;\r\n'
	// Uniforms
	+ 'uniform mat4 world;\r\n'
    + '#include<instancesDeclaration>\r\n'
	+ 'uniform mat4 worldViewProjection;\r\n'
	// Varying
	+ 'varying vec2 vUV;\r\n'
	// MAIN
	+ 'void main(void) {\r\n'
    + '#include<instancesVertex>\r\n'
	+ '  vec4 p = vec4(position, 1.0);\r\n'
	+ '  gl_Position = worldViewProjection * finalWorld * p;\r\n'
	+ '  vUV = uv;\r\n'
	+ '}\r\n';

I basically just added the two includes and multiplied worldViewProjection by finalWorld, but i have no idea where this value comes from.
And the fragmentShader:

// Precision
+ 'precision highp float;\r\n'
// Varying
+ 'varying vec2 vUV;\r\n'
// Uniforms - Diffuse
+ 'uniform sampler2D diffuseTexture;\r\n'
// MAIN
+ 'void main(void) {\r\n'
+ '  gl_FragColor = texture2D(diffuseTexture, vUV);\r\n'
+ '}\r\n';

And here the shaderMaterial gets created:

const material = new ShaderMaterial(
	mesh.name + '-material',
	scene,
	{
		vertexElement: 'custom',
		fragmentElement: 'custom'
	},
	{
		attributes: ['position', 'uv', 'normal', 'world0', 'world1', 'world2', 'world3'],
		uniforms: ['world', 'worldViewProjection'],
		needAlphaBlending: true,
		defines: ['#define INSTANCES']
	}
);
material.alphaMode = Engine.ALPHA_MULTIPLY;

In the final creation i just extended the attributes and added the defines statement.
It seems better than before, cause at least the plane materials get rendered at diffrent positions, but their aren’t the correct ones. I have to say i’m really not that common with shader programming and have no good understanding, what is going on there

Hello, maybe you could be more interested by NME? How To use the Node Material - Babylon.js Documentation

Else you need to do more in your ShaderMaterial:

First of all thanks for the quick response, was not excepting it to be this fast :slight_smile: and much thanks for this link, had not seen this before, it is a geniuos tool. It will take me some time to fully understand this, so i have one quick question, how easy is it to still set a texture on my custom NodeMaterial. Basically what i want to accomplish is to have a texture with black/white and set the white as alpha so it only renders the black part. I don’t need a guide on how to accomplish that exactly, but can you tell me if it’s possible without spending serveral days designing the nodeMaterial. Sorry for my maybe uneducated question.

It will be dead fast to do with NME :slight_smile:

Hi, there. I didn’t get it to work right away and had a few other things with higher prio, but today i finished it (it is very basic, but i’m still learning that 3d stuff) and it is still not working. I looked in the sources and noticed, that there is no setTexture function on the NodeMaterial class. Is this really the case, that it is not possible to set a texture for a NodeMaterial?

You can use smthg like this :
let block = nodeMaterial.getInputBlockByPredicate((b) => b.name === "foo"); block.texture = bar;

Have a look at the nme programming doc:

https://doc.babylonjs.com/how_to/node_material

Texture blocks have a texture property that you can set:


Once a graph is built inside a NodeMaterial, you can use the following API to get a specific node by name:

let block = nodeMaterial.getBlockByName("MyBlock");

You can also get a block using a predicate:

let block = nodeMaterial.getBlockByPredicate((b) => b.getClassName() === "AddBlock" && b.name === "foo");

Or you can also use this API to get an InputNode and use it to setup its value if the node is set manual value:

let block = nodeMaterial.getInputBlockByPredicate((b) => b.name === "foo");
block.value = 10;

Note that while this API generically works for all input blocks, not all input blocks have a “value” attribute. For example texture blocks would have block.texture instead of block.value. Be sure to check the API documentation for detail.

Thanks, that did the trick, didn’t got that. Ended up with using getTextureBlocks(), cause my material has only one and i didn’t have to rely on that name. But somehow the whole thing is still not working, when i create instances. It applies different positions for the instances, but just not the correct ones (without instances it get’s drawn in the correct place). Here is a link to my material https://nme.babylonjs.com/#8PFDX7#16. And another question right away, can i clone this material, after loading it via url, it returns null if i call clone() on it.

const material = await NodeMaterial.ParseFromSnippetAsync(‘8PFDX7#16’, scene);
material.clone(‘new-name’); // this is null

You need to use the Instances block to be able to use a node material with instances:

I think you will need to provide a PG for the cloning problem as cloning should work.

aww, damn it, i put the wrong version in the link, corrected it now (@Evgeni_Popov it is the same that you propably already saw). I will work out a playground and put it here in a few hours

Ok, here is a playground for my positioning Problem: Babylon.js Playground. This was surprisingly easier to reproduce, than the cloning, which works, flawless in the playground. Anyway, if you don’t apply the node material, or outline the creation of the instance the position is as expected, but it somehow alters both positions, that of the original and the position of the instance

This will be corrected by:

1 Like

thank you so mouch for your time and effort, looking forward to using the awesome NME-Editor more often in the future

2 Likes