Skip to content

Latest commit

 

History

History
146 lines (103 loc) · 4.27 KB

readme.md

File metadata and controls

146 lines (103 loc) · 4.27 KB

Three.js Material Modifier

Extend or modify three.js materials easily.

Existing three.js shader code is modified directly from the THREE.ShaderLib code at runtime.

Works with either :

  • MeshBasicMaterial ('basic')
  • MeshLambertMaterial ('lambert')
  • MeshPhongMaterial ('phong')
  • MeshStandardMaterial ('standard')
  • MeshDepthMaterial ('depth')

MaterialModifier.extend( classOrString, shaderConfig )

Specify either the three.js material class or a string and supply a shader config object. The function returns a usable material class that can be instantiated with parameters as a standard three.js material.

import MaterialModifier from 'three-material-modifier';
import {
	Mesh,
	MeshStandardMaterial,
	BoxBufferGeometry
} from 'three';

let CustomStandardMaterial = MaterialModifier.extend( MeshStandardMaterial, {
	uniforms: {
		time: { type: 'f', value: 0.0 }
	},

	vertexShader: {
		preNormal: `
			objectNormal = normalize( objectNormal );
		`,
		preTransform: `
    		transformed.x = sin( time );
    	`
	},
	fragmentShader: {
		postFragColor: `
			gl_FragColor = vec4( 1.0,0.0,0.0,1.0 );
		`
	}

});

let mesh = new Mesh(
    new BoxBufferGeometry(),
    new CustomStandardMaterial( { color: 0x000000 } )    
)

scene.add( mesh );

MaterialModifier.modify( classOrString, shaderConfig )

Specify either the three.js material class or a string and supply a shader config object. The function returns an object that contains the modified vertex and fragment shaders and a uniforms object.

{
	vertexShader: "modified vertex code..",
	fragmentShader: "modified vertex code..",
	uniforms: {
		/** All base uniforms and custom uniforms included here **/
	}
}

Customising MaterialModifier

Under the hood, the MaterialModifier is modifying existing THREE.ShaderLib using regexp. When instantiating a custom MaterialModifier instance a config object can be supplied with 'insertbefore:' and 'insertafter:' statements. These lines are followed by the actual code found in the three.js ShaderLib for which additions should be inserted. This makes it possible to define additional hooks if needed.


import { MaterialModifier } from 'three-material-modifier'

let modifierConfig = {

    vertexHooks: {

        uniforms: 'insertbefore:#include <common>\n',
        functions: 'insertafter:#include <clipping_planes_pars_vertex>\n',
        preTransform: 'insertafter:#include <begin_vertex>\n',
        postTransform: 'insertafter:#include <project_vertex>\n',
        preNormal: 'insertafter:#include <beginnormal_vertex>\n'

    },

    fragmentHooks: {

        uniforms: 'insertbefore:#include <common>\n',
        functions: 'insertafter:#include <clipping_planes_pars_fragment>\n',
        preFragColor: 'insertbefore:gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n',
        postFragColor: 'insertafter:gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n'

    }

}

let customMaterialModifier = new MaterialModifier( modifierConfig );

// customMaterialModifier.extend( etc );
// customMaterialModifier.modify( etc );


The default MaterialModifier instance can also be modified to include new hooks by passing in the same hook definition syntax to the two define functions - MaterialModifier.defineFragmentHooks() and MaterialModifier.defineVertexHooks():


MaterialModifier.defineVertexHooks({
	declarations: 'insertafter:#include <clipping_planes_pars_vertex>\n'
});

// This would then give an additional property to modify in the shader config :

let CustomMaterial = MaterialModifier.extend( MeshBasicMaterial, {
	vertexShader: {
		declarations: `
			mat4 rotationMatrix(vec3 axis, float angle) {
			    axis = normalize(axis);
			    float s = sin(angle);
			    float c = cos(angle);
			    float oc = 1.0 - c;
			    return mat4(oc * axis.x * axis.x + c,           oc * axis.x * axis.y - axis.z * s,  oc * axis.z * axis.x + axis.y * s,  0.0,
			                oc * axis.x * axis.y + axis.z * s,  oc * axis.y * axis.y + c,           oc * axis.y * axis.z - axis.x * s,  0.0,
			                oc * axis.z * axis.x - axis.y * s,  oc * axis.y * axis.z + axis.x * s,  oc * axis.z * axis.z + c,           0.0,
			                0.0,                                0.0,                                0.0,                                1.0
			            );
			}
		`
	}
}