OK so I have successfully been able to extend materials to use the same image for diffuse/normal/specular maps. My next step was to use the same image also for all diffuse variants.
I got this working by cloning the cached base material. The clone method had some draw backs. For one initial scene rendering took forever. Like way too long. So to get around this I used a method that I found in a discussion on github regarding per object uniforms.
This SO post contains a similar question. WestLangley’s solution was to clone the material and not worry about it. This may be ok in small little demo’s but in large applications it’s not ideal on the CPU side although GPU side seems ok.
Problem… I cannot seem to get this to work correctly. With only one object being rendered at a time, it works fine. But when two objects sharing a material but with different uniforms set by onBeforeRender, it breaks down.
Here is a live demonstration (pan the camera to screen left, so that only the left most Mesh is on screen and you will see what I mean).
Thanks, it was a minor change after the latest update, fixed it.
Yeah i used the linked fiddle as template, for a example usecase
Assigning uniforms on the fly is a little tricky, i described it in the thread, in order for all objects having uniforms assigned your plugin should use the object passed in the compile callback and use it’s properties as initial uniform value. You can also not do this, but it means the first mesh using the material will not have the values assigned for the first frame. The addon basically sets the uniforms directly as THREE does internally, since standard materials don’t have a exposed uniforms object.
Edit: It seems in your example the uniforms are only set once, then the next calls will stay with these values, i had a similar result at the beginning when i made the plugin
Hmm, I though this would set the uniform per object each frame:
mesh.onBeforeRender = function(renderer, scene, camera, geometry, material){
if( this.userData.customUniforms ){
if( material.userData.shader ){
for( var name in this.userData.customUniforms ){
material.userData.shader.uniforms[name].value = this.userData.customUniforms[name];
}
}
}
};
Am I misunderstanding this?
While your plugin is super cool, I would like to have this working in “raw” three.js. I probably will wind up using your plugin though. It’s too bad onBeforeRender doesn’t just work out of the box without adding frame sync operations and such.
The issue seems that only ShaderMaterial is able to do this with uniformsNeedUpdate i’ve set on your material isShaderMaterial and uniformsNeedUpdate to true and it works, though this could cause side effects since standard materials are different from ShaderMaterial.
Oof… hacky. But it works! Although with forcing a reload of uniforms every object, I may be defeating the whole point of this which was to improve performance. I guess using onBeforeRender for per object uniforms is alone not a complete solution.
Thanks for looking into this. I’ll also take a stab at implementing your plugin. My primary concern of course is three.js pushing a change that breaks it.
Yes, in the plugin you only update the uniforms that change in the render callback.
It patches THREE without any conflicts, i highly doubt they change something about onBeforeCompile, but if i’d adapt it as long as it doesn’t cover all you can do with the plugin, especially chaining shader plugins in a modular way, having various kind of features patched into one material.
When you include the plugin and call THREE.MaterialCallback.use() it will assign THREE.MaterialCallback to the prototype of THREE.Mesh and THREE.SkinnedMesh, you can do that manually too if you want, THREE calls a empty function anyway. If you need to implemented onBeforeRender yourself somewhere just call THREE.MaterialCallback with the parameters. You can find all relevant details in the thread though.
I got it integrated and it works perfectly! Good stuff.
One question, why is the header shared between both shaders? I had to separate mine to silence console warning about adding attributes to the fragment shader. No big deal at all though of course.
Can you elaborate on what’s going on here?
Several onBeforeRender encounter the same material and you want to copy the values into the same uniforms?
Yes. I’m curious why do this? We’re not copying values into the same materials over and over, but instead make copies of materials with their own colors and values, when working with regular materials.
As the title suggests, for per-mesh uniform values without having a lot materials, it’s also easier to deal with instead manually assigning them. It also saves unecessary uploads of other uniforms
Not sure if it’s all that easier to deal with there used to be a dynamic uniform class but it got nuked and I didn’t quite like it. This wouldn’t be an issue if there was smarter about diffing uniforms and batching stuff?
I didn’t meant the experimental approach of the OP, i mean via the plugin system. It’s easier to setup and better for code managment. Like in this example, the THREE.Mesh class gets extended with the per-mesh values stored directly on the mesh. It depends on if there are different values per-mesh expected.
The header property is just for code that is supposed to be the same on vertex and fragment code like varyings or similar. It is prepended to both, you souldn’t declare vertex shader related things here like attributes, for vertex specific you can always insert at #include <common>, this line is available on all type of materials.