Three.js: Material Property Block

Created on 8 Aug 2019  路  23Comments  路  Source: mrdoob/three.js

Hi all,

Apologies if I'm missing anything as I'm new to Three, but from my research it looks like that the following feature is not currently supported, so I would like to request you to consider adding it in the future releases if possible.

I want to be able to assign different material settings per object, for the same material. For example, use the same material, on multiple objects with different lightmaps. Similar to the Material Property Block in Unity3d https://docs.unity3d.com/ScriptReference/MaterialPropertyBlock.html.

Thanks,
Nick

Enhancement

Most helpful comment

@sunag , many thanks for the reply. I haven't tried that to be honest, but according to this post this won't work (https://discourse.threejs.org/t/how-to-apply-2-lightmap-for-two-different-objects-that-share-same-material/1739)

In the meanwhile, ShaderMaterial.uniformsNeedUpdate was introduced for this use case.

All 23 comments

Bad comment...
Please use https://discourse.threejs.org/c/questions for help requests, after consulting https://stackoverflow.com/tags/three.js .

Other users, including myself, will gladly help you there.

Hi @EliasHasle , many thanks for the reply. This is not a question, this is a feature request. I did searched the places you suggested for answers, and others have asked similar questions and the answers were that it's not supported. I then searched the guidelines for where is the best place to post features requests, and according to this repo, it's in this place.

image

Apologies if I misunderstood something.

If I understand your request right, it can be solved by making multiple material instances:

var mat1 = new THREE.MeshBasicMaterial({map: map1, color: color1}); var mat2 = new THREE.MeshBasicMaterial({map: map2, color: color2}); var mat3 = new THREE.MeshBasicMaterial({map: map3, color: color3});

Internally, the shader program will be reused. This is also the case if you make multiple ShaderMaterial instances that differ only by values of uniforms.

There is also geometry instancing, which allows varying certain values per instance. I have no experience with that yet, though.

I recommend you to ask on the forum. Even if a particular feature you are used to is not readily available in Three.js, existing solutions may be sufficient. And the forum is much more welcoming to what can be perceived as help requests. 馃槃

thanks for your reply. Like I said in the previous replies, I have already gone through the forums and the answer is that is not supported. Yes, people suggested a few solutions like copying materials, but these are just workarounds.
So, all I'm saying that I'm not looking for another "messy" workaround. I'm requesting a feature so this thing can be supported natively in Three same as in Unity.

I'm requesting a feature so this thing can be supported natively in Three same as in Unity.

Only because a game engine does things in a certain way, it does not mean the approach also fits to three.js. Creating multiple instances of a material type is not a "messy" workaround but a valid concept. From my point of view, it's a cleaner way of organizing material properties.

@Mugen87 , thanks for your reply. I'm not requesting it because another engine supports it, I'm requesting it because it is very useful in certain cases.

I'm working on some very configurable systems, with more than quadrillions of options where many objects share the same materials. If I start creating and updating clones of the materials, and updating all of their properties interactively, you can end up having tens of thousands of materials. I've been there, done that with Unity before I discover the MaterialPropertyBlock and it wasn't great. The moment I switched the system to the MaterialPropertyBlock, all the artists became instantly more productive and they don't want to go back, and I fully understand them.

and guys, I really don't want to argue or anything. This is a very important feature for us so I thought to request it as this is inline with the repos guidelines. If nobody wants to implement it, it's fine, but since others requested similar things, I assumed that it's an important feature to others too.

Of course it's a valid feature request. But honestly it's the first time for me that I hear a users is demanding such a system.

Instead of enhancing the existing code in the core, I wonder if NodeMaterial could be helpful in this context. A node-based material is usually composed of many different types of nodes. I think it should be possible (at least conceptually) to share certain building blocks/nodes between concrete material instances.

@sunag Any thoughts about this? 馃槉

@Mugen87 , not the first time.. 馃榿馃榿
https://discourse.threejs.org/t/how-to-apply-2-lightmap-for-two-different-objects-that-share-same-material/1739

Many thanks for your replies! :)

Fair enough^^. Still I think it's one of the more exotic feature requests. Let's see how NodeMaterial can help here...

Yes, I know. We do very "exotic" things so many times we do have a bit more unique feature requests. 馃槗
Regarding the NodeMaterial, does this support custom shaders as we plan to write our own ShaderMaterials going fwd. I'll do some reading on the NodeMaterial now. I've only seen mentions about it, on the sea3d flow editor.

I think that the feature request can be resolved using onBeforeRender, this way you can have one single material and change your uniforms per object before of render.

oh, it's not documented yet.

I found this example https://threejs.org/examples/webgl_materials_nodes and if I understand correctly, you can have nodes like Nodes.MathNode, Nodes.MathNode etc and you slowly compose a shader program using those. Like a node based editor without interface. So, your idea is to "instance" a few properties and keep some unique. But this would still require multiple materials.

Let me try to give an example of why I don't want to create materials. If you have a configurable product, that one of the materials comes in 150 colours, and you have different features that turn on/off let's be very optimistic and say it's just 20, that means that you instantly need to generate 3000 materials. Imagine having 5 different materials with different finishes (metal, plastic, fabric etc) per colour, that's already 15000 materials. 馃槍

@sunag , many thanks for the reply. I haven't tried that to be honest, but according to this post this won't work (https://discourse.threejs.org/t/how-to-apply-2-lightmap-for-two-different-objects-that-share-same-material/1739)

Even if you would use .onBeforeRender() to change the material, the uniforms of the underlying shader program are not updated.

The discussion at #16922 may be related, in the broader scope.

Anyway, can you explain why you would need 15000 materials at the same time? Surely, if they are merely _potential_ combinations you can just make them on the fly, reusing/modifying existing material instances as you see fit?

@sunag , many thanks for the reply. I haven't tried that to be honest, but according to this post this won't work (https://discourse.threejs.org/t/how-to-apply-2-lightmap-for-two-different-objects-that-share-same-material/1739)

In the meanwhile, ShaderMaterial.uniformsNeedUpdate was introduced for this use case.

Even if you would use .onBeforeRender() to change the material, the uniforms of the underlying shader program are not updated.

In theory it should work, but I will find out what is going on. Whay you want is related with instances and particles, have you ever seen the examples of instances in threejs if it meets your need?

There is still an open issue in context of ShaderMaterial.uniformsNeedUpdate, see #15581. Unfortunately, the PR was never merged 馃槥

@EliasHasle , in certain when an artists is updating one material, their expectations is that all objects with the same material update interactively, even when a object is hidden.
@Mugen87 thanks for the help!
@sunag , We can't use instancing I'm afraid. All parts of the products are unique.

@EliasHasle , in certain when an artists is updating one material, their expectations is that all objects with the same material update interactively, even when a object is hidden.

I admit that it's nice when an engine supports you in this task but you _can_ solve this on app level with the current state of three.js.

We can't use instancing I'm afraid. All parts of the products are unique.

This you want is already possible using NodeMaterial for example, but you need to know the concept of instances, these different values that come from object to object using a single material are allocated in the geometry buffer, this is relatively easy to create using NodeMaterial. ( https://github.com/mrdoob/three.js/issues/14919 )

https://github.com/mrdoob/three.js/blob/e0ecdf7ee117c37db75d6d57488063245dfc34d9/examples/webgl_materials_nodes.html#L2396-L2413

Thanks all for your help and suggestion! I will definitely give it a go with NodeMaterial, even though it's not documented yet which makes me feel that it's still a bit "experimental".

I would still love it if this feature was implemented natively and it would work with any material, not just the NodeMaterial. 馃槂

@sinokgr Thanks feedback, hope this helps a bit. One of the goals is to bring NodeMaterial to the core of three.js and create your documentation.

Was this page helpful?
0 / 5 - 0 ratings