Strange behavior with texture when extending MeshStandardMaterial as a ShaderMaterial

I wanted to extend the MeshStandardMaterial with my own code and used this:


as an example on how to do that. There, MeshPhongMaterial is adapted.

First the texture just didn’t appear at all on the adapted material so in order to test if the texture is loaded at all, I created a minimal example with a custom ShaderMaterial and displayed it in the scene as well, there it works.
Then I tested if it works with the spread syntax and surprisingly it did.
I then tested if I get a usual complaint in the console if I don’t define myTex in the uniforms at all but weirdly, it becomes myOtherTex from the minimal ShaderMaterial instead.

Now I can just work with the spread syntax but it would still be good to know what’s going on here.

Examples can be seen here:


Just click the three links in the readme.

I expected the first and the second link to display myTex on the left and myOtherTex on the right and the third link to display a black square on the left and some error about the missing myTex uniform in the console.

I have seen this post:


and added .needsUpdate = true to texture and material and .uniformsNeedUpdate = true to the material but nothing helped.

Any help appreciated!

There is actually a couple things to consider when trying to use a ShaderMaterial as built-in material in disguise basically. I made a plugin for this to handle it more easy, it also adds more very useful features such as clean patching, sharing uniforms and creating derivative materials with previous applied patches, sharing lights uniforms globally etc:

Especially maps THREE needs in order to setup the right constants, which if you do it hardcoded will often conflict.

Also:

	// this strangely causes the texture to be myOtherTex
	uniforms:THREE.ShaderLib.standard.uniforms,

You’re assigning the libraries uniforms without cloning them. The entire set of uniforms isn’t even required though, the plugin also will only use which you define or set as null, and will add any necessary that require default values.

2 Likes

Thanks a lot! Works like a charm.

Sorry, first I couldn’t figure out how to make an extended MeshStandardMaterial work with an environment map. So I console.logged what THREE.js does inside initMaterial and compared, turns out I had to add these defines manually:
#define ENVMAP_TYPE_CUBE
#define ENVMAP_MODE_REFLECTION
#define ENVMAP_BLENDING_NONE
#define TEXTURE_LOD_EXT
and then the roughness still didn’t blur the reflected environment and I couldn’t figure out why.

So what I am doing now is ditch that approach altogether and just work with onBeforeRender.

Did you defined class: THREE.CustomMaterial in the options as below? It works as a built-in material in disguise telling THREE what features are used. This is specifically needed for envMap, CustomMaterial isn’t used as shader class by default since it’s not only used to extend built-in materials.

const myMaterial = THREE.extendMaterial( THREE.MeshStandardMaterial, {
	
	class: THREE.CustomMaterial
	
});

This isn’t done for ShaderMaterial as it should be avoided creating hidden classes by adding properties that don’t exist on it.

Here a envMap example: https://codepen.io/Fyrestar/pen/eYZovOw

I’ll also update the repo as i made a fix for cloning CustomMaterial for those cases.

Ah no. Should have asked before all this debugging…
But now that it’s working with onBeforeCompile, I’d rather use that so I can keep the number of dependencies small.
But tbh a usable way to extend the built-in materials should at least be provided in the examples… Did you ask if they want to include your lib there?