depthfunc = equalDepth It’s actually a pretty common and clean approach to material blending.
You can’t always predict what material combos you will need, (when precompiling shaders) and the depth func has no major side effects if you think about it. depth=Equal is fast and precise, and there is ~zero overhead if the variant materials aren’t in the .materials array. It’s also more efficient than a cloned mesh since the calls are happening right after each other, so no overhead of computing new sets of matrices etc… only the material params themselves need to be re-bound. Also, since they are still separate materials, the handling of transparent vs opaque is handled for you by the engine.
This is something you can actually lose if mashing materials together. For instance, rendering an oily sheen on top of a standard material. The standardmaterial is still “opaque” with respect to the rendering pipeline, it’s just your oil sheen that is transparent.
Precompiling your variations into the shader, on the other hand, can incur branching penalty if you’re not always using the variations, it increases the complexity of material handling since client code needs to Know that you’re doing weird stuff to the materials themselves, and how to re-parametize them, (unless you create custom wrappers.). You lose out on material settings… i.e. your variations will have all the same parameters as the first material… color/roughness/opacity etc., especially since threejs does a Lot of stuff behind the scenes to support the primarily named material params, like color + (colorspace conversion), .map handling, (assigning UV channels, texture rotation params, etc.)
If you “solve” this complexity, you may end up with some funky material with .variant1color, .variantcolor2, params etc. and imposed mental burden of “knowing” which params are for what variant, and also give up external control over layering order.
There are definitely cases where you do want to extend the material to augment its functionality… but you want to consider all these caveats when deciding if it’s appropriate.
(I think the scenario it’s most appropriate is when extending to add new global behaviors.)
Another pitfall of depthFunc == Equal, is that the materials need to use the exact same math to transform vertices, or Equal depth won’t work, and you’ll get z-fighting.
I ran into this in older versions of threejs that used slightly different vertex transforms… like…
modelView * projection * vertex on phong, but then
vs model * view * projection * vertex in standard.
(will yield very slightly different results due to the precision being shifted around ), so that’s something to be aware of as well.
Other downsides include… not being able to export multi material slots in most formats. Most formats assume single material per mesh. but you’re also not going to be exporting your custom mashed material either.
TLDR, I hack materials via onBeforeCompile, all the time. It’s totally my jam… but If I was trying to future proof something, or extending the threejs multiverse, I would consider all these aspects.