processMaterial( material ) {

    		const cache = this.cache;
    		const json = this.json;

    		if ( cache.materials.has( material ) ) return cache.materials.get( material );

    		if ( material.isShaderMaterial ) {

    			console.warn( 'GLTFExporter: THREE.ShaderMaterial not supported.' );
    			return null;

    		}

    		if ( ! json.materials ) json.materials = [];

    		// @QUESTION Should we avoid including any attribute that has the default value?
    		const materialDef = {	pbrMetallicRoughness: {} };

    		if ( material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true ) {

    			console.warn( 'GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.' );

    		}

    		// pbrMetallicRoughness.baseColorFactor
    		const color = material.color.toArray().concat( [ material.opacity ] );

    		if ( ! equalArray( color, [ 1, 1, 1, 1 ] ) ) {

    			materialDef.pbrMetallicRoughness.baseColorFactor = color;

    		}

    		if ( material.isMeshStandardMaterial ) {

    			materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
    			materialDef.pbrMetallicRoughness.roughnessFactor = material.roughness;

    		} else {

    			materialDef.pbrMetallicRoughness.metallicFactor = 0.5;
    			materialDef.pbrMetallicRoughness.roughnessFactor = 0.5;

    		}

    		// pbrMetallicRoughness.metallicRoughnessTexture
    		if ( material.metalnessMap || material.roughnessMap ) {

    			if ( material.metalnessMap === material.roughnessMap ) {

    				const metalRoughMapDef = { index: this.processTexture( material.metalnessMap ) };
    				this.applyTextureTransform( metalRoughMapDef, material.metalnessMap );
    				materialDef.pbrMetallicRoughness.metallicRoughnessTexture = metalRoughMapDef;

    			} else {

    				console.warn( 'THREE.GLTFExporter: Ignoring metalnessMap and roughnessMap because they are not the same Texture.' );

    			}

    		}

    		// pbrMetallicRoughness.baseColorTexture or pbrSpecularGlossiness diffuseTexture
    		if ( material.map ) {

    			const baseColorMapDef = { index: this.processTexture( material.map ) };
    			this.applyTextureTransform( baseColorMapDef, material.map );
    			materialDef.pbrMetallicRoughness.baseColorTexture = baseColorMapDef;

    		}

    		if ( material.emissive ) {

    			// note: emissive components are limited to stay within the 0 - 1 range to accommodate glTF spec. see #21849 and #22000.
    			const emissive = material.emissive.clone().multiplyScalar( material.emissiveIntensity );
    			const maxEmissiveComponent = Math.max( emissive.r, emissive.g, emissive.b );

    			if ( maxEmissiveComponent > 1 ) {

    				emissive.multiplyScalar( 1 / maxEmissiveComponent );

    				console.warn( 'THREE.GLTFExporter: Some emissive components exceed 1; emissive has been limited' );

    			}

    			if ( maxEmissiveComponent > 0 ) {

    				materialDef.emissiveFactor = emissive.toArray();

    			}

    			// emissiveTexture
    			if ( material.emissiveMap ) {

    				const emissiveMapDef = { index: this.processTexture( material.emissiveMap ) };
    				this.applyTextureTransform( emissiveMapDef, material.emissiveMap );
    				materialDef.emissiveTexture = emissiveMapDef;

    			}

    		}

    		// normalTexture
    		if ( material.normalMap ) {

    			const normalMapDef = { index: this.processTexture( material.normalMap ) };

    			if ( material.normalScale && material.normalScale.x !== 1 ) {

    				// glTF normal scale is univariate. Ignore `y`, which may be flipped.
    				// Context: https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
    				normalMapDef.scale = material.normalScale.x;

    			}

    			this.applyTextureTransform( normalMapDef, material.normalMap );
    			materialDef.normalTexture = normalMapDef;

    		}

    		// occlusionTexture
    		if ( material.aoMap ) {

    			const occlusionMapDef = {
    				index: this.processTexture( material.aoMap ),
    				texCoord: 1
    			};

    			if ( material.aoMapIntensity !== 1.0 ) {

    				occlusionMapDef.strength = material.aoMapIntensity;

    			}

    			this.applyTextureTransform( occlusionMapDef, material.aoMap );
    			materialDef.occlusionTexture = occlusionMapDef;

    		}

    		// alphaMode
    		if ( material.transparent ) {

    			materialDef.alphaMode = 'BLEND';

    		} else {

    			if ( material.alphaTest > 0.0 ) {

    				materialDef.alphaMode = 'MASK';
    				materialDef.alphaCutoff = material.alphaTest;

    			}

    		}

    		// doubleSided
    		if ( material.side === DoubleSide ) materialDef.doubleSided = true;
    		if ( material.name !== '' ) materialDef.name = material.name;

    		this.serializeUserData( material, materialDef );

    		this._invokeAll( function ( ext ) {

    			ext.writeMaterial && ext.writeMaterial( material, materialDef );

    		} );

    		const index = json.materials.push( materialDef ) - 1;
    		cache.materials.set( material, index );
    		return index;

    	}
Process material
@param  {THREE.Material} material Material to process
@return {Integer|null} Index of the processed material in the "materials" array