Skip to content

Fyrestar/Material-Plugins

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 

Repository files navigation

Chainable onBeforeCompile & Material Plugins

Makes onBeforeCompile of materials chainable rather than just one callback being possible. Enables per-mesh individual uniforms instead just global without core changes. Both features don't violate the API when integrating and fix cases where a material needs multiple patches but onBeforeCompile gets overwritten.

So you can do the following with both callbacks being called before compiling is done:

material.onBeforeCompile = function( shader ) { ... };
material.onBeforeCompile = function( shader ) { ... };

Demo https://codepen.io/Fyrestar/pen/BXYGgN 23

Demo: Simple waving grass plugin https://codepen.io/Fyrestar/pen/PMyZpR 13

Grass

Material Callbacks for per-mesh or per-material uniforms

When extending shaders with onBeforeCompile it only gives you acces to the uniforms once. Material callbacks allow you to not only use uniforms per material again (on in-built materials) but also per object, like in this example: https://codepen.io/Fyrestar/pen/ymjqMm 13

So you can extend materials in a plugin pattern and pass properties of meshes or materials to the shader.

Where you store per-mesh properties is up to you, you can either use the mesh directly like in the example (might be less optimal), store it in it’s userData object, or extend the mesh class (optimal).

Usage

Call THREE.MaterialCallback.use(); at the beginning of your app to apply it to the prototype of Mesh and SkinnedMesh, if you want it manually or need onBeforeRender yourself, just call within your callback: THREE.MaterialCallback.call( this, renderer, scene, ... )

Since onBeforeRender is calling a empty function by default anyway this won't add more overhead, being on the prototype just 1 object shared globally.

If you use post-processing or otherwise call renderer.render() more than once per frame set THREE.MaterialCallback.auto = false; and call THREE.MaterialCallback.frame(); whenever your actual app loop starts.

For the actual callback where you pass the property stored in a mesh or material instance to the program you create a onBeforeCompile callback with an object instead a function that contains the callbacks.

  • frame is called every frame (notice the bold paragraph above in order for this being every app frame rather than render call)
  • render is called before a mesh is rendered, here you update the uniforms
  • compile is the regular onBeforeCompile callback
const GrassPlugin = {
    time: 0,
    frame: function() {

        this.time += 0.025;

    },
    render: function( object, uniforms, context ) {

        context.set( uniforms.uTime, this.time );
        context.set( uniforms.uSize, object.waveLength );

    },
    compile: function( shader ) {

        shader.uniforms.uSize = {
            value: 0
        };
        shader.uniforms.uTime = {
            value: 0
        };

        shader.fragmentShader = shader.fragmentShader.replace(
            '#include <normal_fragment_begin>',
            'vec3 normal = normalize( vNormal );'
        );
        shader.vertexShader = shader.vertexShader.replace('#include <common>', `
                #include <common>
                
                #ifndef uTime
                uniform float uTime;
                #endif
                
                uniform float uSize;
                
                float rand(float n){return fract(sin(n) * 43758.5453123);}
                
                float noise(float p){
                float fl = floor(p);
                float fc = fract(p);
                return mix(rand(fl), rand(fl + 1.0), fc);
                }
                
                float noise(vec2 n) {
                const vec2 d = vec2(0.0, 1.0);
                vec2 b = floor(n), f = smoothstep(vec2(0.0), vec2(1.0), fract(n));
                return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y);
                }

        `);

        shader.vertexShader = shader.vertexShader.replace('#include <beginnormal_vertex>', `
            #include <beginnormal_vertex>
            objectNormal = vec3(0.0, 1.0, 0.0);
        
        `);
        shader.vertexShader = shader.vertexShader.replace('#include <project_vertex>', `
            
            vec4 GRASS_world = modelMatrix * vec4( transformed, 1.0 );
            transformed.xz += uv.y * ( noise(GRASS_world.xz + uTime ) * uSize );
            
            #include <project_vertex>
        `);

    }
};

About

Chainable onBeforeCompile and per-mesh uniforms.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published