-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
Closed
Description
I would like a more robust Material.onBeforeCompile system to work with the current state of the default materials.
- It's not actually documented, but from the example it's not obvious that there is a very specific way in which the callback needs to be written. Any conditional logic inside the callback may or may not be executed depending on the order in which the materials have been added to the scene or encountered by the first render call.
See:
[BUG]: onBeforeCompile hashing #13192
https://codepen.io/anon/pen/KQPBjd
The workaround for this seems extremely hacky, and one has to think of functions/callbacks in a specific way, if that makes sense. Literally the raw code you write in the callback matters. I would like to not think of this function as data, but the stuff inside it as data. If you are writing some kind of a utility that somehow configuresonBeforeCompilein different ways, rather than writing:
function myOnBeforeCompile(shader){
if(someUniform){
//replace some chunk
}
if(someArbitraryProperty){
//replace some chunk
}
if(someCondition){
//replace multiple chunks
}
}
someMaterial.onBeforeCompile = someUniform ? onBeforeCompileForCombinationFOO : someOtherOnBeforeCompile
//or maybe something like this, but it feels weird,
//whatever this Manager would do would probably look gnarly
someOtherMaterial.onBeforeCompile = OnBeforeCompileManager.getOnBeforeCompile( someUniform, someProperty)
- It's kinda verbose, and really wants me to understand that shader is a string. I would like it to be more like data. Instead of these chained "do stuff to string" calls, i would like to have a configurable object where i can say
{ chunkName: chunkString}. If replace() is the only thing i will ever want to call in this callback, onfragmentShaderorvertexShaderit would be nice if it's hidden away:
someMaterial.onBeforeCompile = shader = >{
shader.fragmentShader = shader.fragmentShader.replace('#include <foo>', myFoo)
shader.fragmentShader = shader.fragmentShader.replace('#include <bar>', myFoo)
shader.fragmentShader = shader.fragmentShader.replace('#include <baz>', myFoo)
// don't like calling shader.fragmentShader = shader.fragmentShader
// don't like looking for #include <> if I know that it's a chunk and it's name is foo,bar,baz
// don't like calling string.replace()
//chunks as data
someMaterial.replacableChunksOrWhatever.foo = fooGLSL
someMaterial.replacableChunksOrWhatever.bar = barGLSL
someMaterial.replacableChunksOrWhatever.baz = bazGLSL
- It's not obvious where uniforms, functions, attributes and varyings should be injected. Prepending the shader in the callback should work, but causes a warning for example with
#extensiondirectives (there's more stuff that happens above the pre-compile shader). Again i'd like to see it more as data.
someMaterial.onBeforeCompile = shader =>{
shader.fragmentShader = myPrependedAndFormattedUniformsVaryings + shader.fragmentShader
//...
}
//i'd rather see it as data and not concern myself over the semantics of how it gets injected
someMaterial.customGLSL.vertexUniforms.foo = {type: 'v4', value: new THREE.Vector4()}
I've tried to implement these changes in:
#13198
Thoughts @Mugen87 ?
Per discussion with @mrdoob all of this would go away in favor of all the materials being rewritten with the node material. Until that happens, i would like to see a way to interact with the default materials thats more robust.
I think this is called configuration over convention. I would configure these materials with custom stuff upon construction, and then not care about the string manipulation, and particular moments in time like compile time.
Three.js version
- Dev
- r90
- ...
Metadata
Metadata
Assignees
Labels
No labels