浏览代码

Add specular to MeshPhysicalMaterial and GLTFLoader KHR_materials_specular support (#22156)

* Add specularStrength and specular to MeshPhysicalMaterial

* GLTFLoader: Add KHR_materials_specular support

* Add specularStrength and specular to GUI in webgl_materials_physical_transmission example

* Update webgl_loader_gltf_transmission example

* Update example screenshot

* MeshPhysicalMaterial: Rename specularStrength and specular to specularIntensity and specularTint
Takahiro 4 年之前
父节点
当前提交
dc5aafee26

+ 1 - 0
docs/examples/en/loaders/GLTFLoader.html

@@ -32,6 +32,7 @@
 			<li>KHR_materials_clearcoat</li>
 			<li>KHR_materials_clearcoat</li>
 			<li>KHR_materials_ior</li>
 			<li>KHR_materials_ior</li>
 			<li>KHR_materials_pbrSpecularGlossiness</li>
 			<li>KHR_materials_pbrSpecularGlossiness</li>
+			<li>KHR_materials_specular</li>
 			<li>KHR_materials_transmission</li>
 			<li>KHR_materials_transmission</li>
 			<li>KHR_materials_unlit</li>
 			<li>KHR_materials_unlit</li>
 			<li>KHR_materials_volume</li>
 			<li>KHR_materials_volume</li>

+ 1 - 0
docs/examples/zh/loaders/GLTFLoader.html

@@ -30,6 +30,7 @@
 			<li>KHR_materials_clearcoat</li>
 			<li>KHR_materials_clearcoat</li>
 			<li>KHR_materials_ior</li>
 			<li>KHR_materials_ior</li>
 			<li>KHR_materials_pbrSpecularGlossiness</li>
 			<li>KHR_materials_pbrSpecularGlossiness</li>
+			<li>KHR_materials_specular</li>
 			<li>KHR_materials_transmission</li>
 			<li>KHR_materials_transmission</li>
 			<li>KHR_materials_unlit</li>
 			<li>KHR_materials_unlit</li>
 			<li>KHR_materials_volume</li>
 			<li>KHR_materials_volume</li>

+ 77 - 1
examples/jsm/loaders/GLTFLoader.js

@@ -111,6 +111,12 @@ class GLTFLoader extends Loader {
 
 
 		} );
 		} );
 
 
+		this.register( function ( parser ) {
+
+			return new GLTFMaterialsSpecularExtension( parser );
+
+		} );
+
 		this.register( function ( parser ) {
 		this.register( function ( parser ) {
 
 
 			return new GLTFLightsExtension( parser );
 			return new GLTFLightsExtension( parser );
@@ -421,6 +427,7 @@ const EXTENSIONS = {
 	KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
 	KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
 	KHR_MATERIALS_IOR: 'KHR_materials_ior',
 	KHR_MATERIALS_IOR: 'KHR_materials_ior',
 	KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
 	KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
+	KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
 	KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
 	KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
 	KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
 	KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
 	KHR_MATERIALS_VOLUME: 'KHR_materials_volume',
 	KHR_MATERIALS_VOLUME: 'KHR_materials_volume',
@@ -861,6 +868,73 @@ class GLTFMaterialsIorExtension {
 
 
 }
 }
 
 
+/**
+ * Materials specular Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular
+ */
+class GLTFMaterialsSpecularExtension {
+
+	constructor( parser ) {
+
+		this.parser = parser;
+		this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR;
+
+	}
+
+	getMaterialType( materialIndex ) {
+
+		const parser = this.parser;
+		const materialDef = parser.json.materials[ materialIndex ];
+
+		if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
+
+		return MeshPhysicalMaterial;
+
+	}
+
+	extendMaterialParams( materialIndex, materialParams ) {
+
+		const parser = this.parser;
+		const materialDef = parser.json.materials[ materialIndex ];
+
+		if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
+
+			return Promise.resolve();
+
+		}
+
+		const pending = [];
+
+		const extension = materialDef.extensions[ this.name ];
+
+		materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0;
+
+		if ( extension.specularTexture !== undefined ) {
+
+			pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) );
+
+		}
+
+		const colorArray = extension.specularColorFactor || [ 1, 1, 1 ];
+		materialParams.specularTint = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
+
+		if ( extension.specularColorTexture !== undefined ) {
+
+			pending.push( parser.assignTexture( materialParams, 'specularTintMap', extension.specularColorTexture ).then( function ( texture ) {
+
+				texture.encoding = sRGBEncoding;
+
+			} ) );
+
+		}
+
+		return Promise.all( pending );
+
+	}
+
+}
+
 /**
 /**
  * BasisU Texture Extension
  * BasisU Texture Extension
  *
  *
@@ -2780,7 +2854,7 @@ class GLTFParser {
 	 * @param {Object} materialParams
 	 * @param {Object} materialParams
 	 * @param {string} mapName
 	 * @param {string} mapName
 	 * @param {Object} mapDef
 	 * @param {Object} mapDef
-	 * @return {Promise}
+	 * @return {Promise<Texture>}
 	 */
 	 */
 	assignTexture( materialParams, mapName, mapDef ) {
 	assignTexture( materialParams, mapName, mapDef ) {
 
 
@@ -2812,6 +2886,8 @@ class GLTFParser {
 
 
 			materialParams[ mapName ] = texture;
 			materialParams[ mapName ] = texture;
 
 
+			return texture;
+
 		} );
 		} );
 
 
 	}
 	}

二进制
examples/models/gltf/IridescentDishWithOlives.glb


二进制
examples/screenshots/webgl_loader_gltf_transmission.jpg


+ 8 - 3
examples/webgl_loader_gltf_transmission.html

@@ -11,7 +11,7 @@
 		<div id="info">
 		<div id="info">
 			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - GLTFLoader + <a href="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission" target="_blank" rel="noopener">KHR_materials_transmission</a> extension<br />
 			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - GLTFLoader + <a href="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission" target="_blank" rel="noopener">KHR_materials_transmission</a> extension<br />
 			Iridescent Dish With Olives by <a href="https://github.com/echadwick-wayfair" target="_blank" rel="noopener">Eric Chadwick</a><br />
 			Iridescent Dish With Olives by <a href="https://github.com/echadwick-wayfair" target="_blank" rel="noopener">Eric Chadwick</a><br />
-			<a href="https://hdrihaven.com/hdri/?h=quarry_01" target="_blank" rel="noopener">Quarry</a> by <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
+			<a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> by <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
 		</div>
 		</div>
 
 
 		<script type="module">
 		<script type="module">
@@ -22,6 +22,8 @@
 			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
 			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
 			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
 			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
 
 
+			import { DRACOLoader } from './jsm/loaders/DRACOLoader.js';
+
 			let camera, scene, renderer, clock, mixer;
 			let camera, scene, renderer, clock, mixer;
 
 
 			init();
 			init();
@@ -42,7 +44,7 @@
 				new RGBELoader()
 				new RGBELoader()
 					.setDataType( THREE.FloatType )
 					.setDataType( THREE.FloatType )
 					.setPath( 'textures/equirectangular/' )
 					.setPath( 'textures/equirectangular/' )
-					.load( 'quarry_01_1k.hdr', function ( texture ) {
+					.load( 'royal_esplanade_1k.hdr', function ( texture ) {
 
 
 						texture.mapping = THREE.EquirectangularReflectionMapping;
 						texture.mapping = THREE.EquirectangularReflectionMapping;
 
 
@@ -51,7 +53,10 @@
 
 
 						// model
 						// model
 
 
-						const loader = new GLTFLoader().setPath( 'models/gltf/' );
+						const loader = new GLTFLoader()
+							.setPath( 'models/gltf/' )
+							.setDRACOLoader( new DRACOLoader().setDecoderPath( 'js/libs/draco/gltf/' ) );
+
 						loader.load( 'IridescentDishWithOlives.glb', function ( gltf ) {
 						loader.load( 'IridescentDishWithOlives.glb', function ( gltf ) {
 
 
 							mixer = new THREE.AnimationMixer( gltf.scene );
 							mixer = new THREE.AnimationMixer( gltf.scene );

+ 20 - 0
examples/webgl_materials_physical_transmission.html

@@ -27,6 +27,8 @@
 				roughness: 0,
 				roughness: 0,
 				reflectivity: 0.5,
 				reflectivity: 0.5,
 				thickness: 0.01,
 				thickness: 0.01,
+				specularIntensity: 1,
+				specularTint: 0xffffff,
 				envMapIntensity: 1,
 				envMapIntensity: 1,
 				lightIntensity: 1,
 				lightIntensity: 1,
 				exposure: 1
 				exposure: 1
@@ -89,6 +91,8 @@
 					envMap: hdrEquirect,
 					envMap: hdrEquirect,
 					envMapIntensity: params.envMapIntensity,
 					envMapIntensity: params.envMapIntensity,
 					transmission: params.transmission, // use material.transmission for glass materials
 					transmission: params.transmission, // use material.transmission for glass materials
+					specularIntensity: params.specularIntensity,
+					specularTint: params.specularTint,
 					opacity: params.opacity,
 					opacity: params.opacity,
 					side: THREE.DoubleSide,
 					side: THREE.DoubleSide,
 					transparent: true
 					transparent: true
@@ -166,6 +170,22 @@
 
 
 					} );
 					} );
 
 
+				gui.add( params, 'specularIntensity', 0, 1, 0.01 )
+					.onChange( function () {
+
+						material.specularIntensity = params.specularIntensity;
+						render();
+
+					} );
+
+				gui.addColor( params, 'specularTint' )
+					.onChange( function () {
+
+						material.specularTint.set( params.specularTint );
+						render();
+
+					} );
+
 				gui.add( params, 'envMapIntensity', 0, 1, 0.01 )
 				gui.add( params, 'envMapIntensity', 0, 1, 0.01 )
 					.name( 'envMap intensity' )
 					.name( 'envMap intensity' )
 					.onChange( function () {
 					.onChange( function () {

+ 4 - 0
src/loaders/MaterialLoader.js

@@ -77,6 +77,8 @@ class MaterialLoader extends Loader {
 		if ( json.sheen !== undefined ) material.sheen = new Color().setHex( json.sheen );
 		if ( json.sheen !== undefined ) material.sheen = new Color().setHex( json.sheen );
 		if ( json.emissive !== undefined && material.emissive !== undefined ) material.emissive.setHex( json.emissive );
 		if ( json.emissive !== undefined && material.emissive !== undefined ) material.emissive.setHex( json.emissive );
 		if ( json.specular !== undefined && material.specular !== undefined ) material.specular.setHex( json.specular );
 		if ( json.specular !== undefined && material.specular !== undefined ) material.specular.setHex( json.specular );
+		if ( json.specularIntensity !== undefined ) material.specularIntensity = json.specularIntensity;
+		if ( json.specularTint !== undefined && material.specularTint !== undefined ) material.specularTint.setHex( json.specularTint );
 		if ( json.shininess !== undefined ) material.shininess = json.shininess;
 		if ( json.shininess !== undefined ) material.shininess = json.shininess;
 		if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat;
 		if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat;
 		if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness;
 		if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness;
@@ -258,6 +260,8 @@ class MaterialLoader extends Loader {
 		if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;
 		if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;
 
 
 		if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );
 		if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );
+		if ( json.specularIntensityMap !== undefined ) material.specularIntensityMap = getTexture( json.specularIntensityMap );
+		if ( json.specularTintMap !== undefined ) material.specularTintMap = getTexture( json.specularTintMap );
 
 
 		if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );
 		if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );
 		if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity;
 		if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity;

+ 4 - 0
src/materials/Material.js

@@ -174,6 +174,8 @@ class Material extends EventDispatcher {
 		if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;
 		if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;
 
 
 		if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();
 		if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();
+		if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity;
+		if ( this.specularTint && this.specularTint.isColor ) data.specularTint = this.specularTint.getHex();
 		if ( this.shininess !== undefined ) data.shininess = this.shininess;
 		if ( this.shininess !== undefined ) data.shininess = this.shininess;
 		if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat;
 		if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat;
 		if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness;
 		if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness;
@@ -243,6 +245,8 @@ class Material extends EventDispatcher {
 
 
 		if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;
 		if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;
 		if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
 		if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
+		if ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid;
+		if ( this.specularTintMap && this.specularTintMap.isTexture ) data.specularTintMap = this.specularTintMap.toJSON( meta ).uuid;
 
 
 		if ( this.envMap && this.envMap.isTexture ) {
 		if ( this.envMap && this.envMap.isTexture ) {
 
 

+ 16 - 1
src/materials/MeshPhysicalMaterial.js

@@ -23,7 +23,12 @@ import * as MathUtils from '../math/MathUtils.js';
  *  thickness: <float>,
  *  thickness: <float>,
  *  thicknessMap: new THREE.Texture( <Image> ),
  *  thicknessMap: new THREE.Texture( <Image> ),
  *  attenuationDistance: <float>,
  *  attenuationDistance: <float>,
- *  attenuationColor: <Color>
+ *  attenuationColor: <Color>,
+ *
+ *  specularIntensity: <float>,
+ *  specularIntensityhMap: new THREE.Texture( <Image> ),
+ *  specularTint: <Color>,
+ *  specularTintMap: new THREE.Texture( <Image> )
  * }
  * }
  */
  */
 
 
@@ -74,6 +79,11 @@ class MeshPhysicalMaterial extends MeshStandardMaterial {
 		this.attenuationDistance = 0.0;
 		this.attenuationDistance = 0.0;
 		this.attenuationColor = new Color( 1, 1, 1 );
 		this.attenuationColor = new Color( 1, 1, 1 );
 
 
+		this.specularIntensity = 1.0;
+		this.specularIntensityMap = null;
+		this.specularTint = new Color( 1, 1, 1 );
+		this.specularTintMap = null;
+
 		this.setValues( parameters );
 		this.setValues( parameters );
 
 
 	}
 	}
@@ -116,6 +126,11 @@ class MeshPhysicalMaterial extends MeshStandardMaterial {
 		this.attenuationDistance = source.attenuationDistance;
 		this.attenuationDistance = source.attenuationDistance;
 		this.attenuationColor.copy( source.attenuationColor );
 		this.attenuationColor.copy( source.attenuationColor );
 
 
+		this.specularIntensity = source.specularIntensity;
+		this.specularIntensityMap = source.specularIntensityMap;
+		this.specularTint.copy( source.specularTint );
+		this.specularTintMap = source.specularTintMap;
+
 		return this;
 		return this;
 
 
 	}
 	}

+ 5 - 5
src/renderers/shaders/ShaderChunk/bsdfs.glsl.js

@@ -56,7 +56,7 @@ vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {
 
 
 } // validated
 } // validated
 
 
-vec3 F_Schlick( const in vec3 specularColor, const in float dotVH ) {
+vec3 F_Schlick( const in vec3 f0, const in vec3 f90, const in float dotVH ) {
 
 
 	// Original approximation by Christophe Schlick '94
 	// Original approximation by Christophe Schlick '94
 	// float fresnel = pow( 1.0 - dotVH, 5.0 );
 	// float fresnel = pow( 1.0 - dotVH, 5.0 );
@@ -65,7 +65,7 @@ vec3 F_Schlick( const in vec3 specularColor, const in float dotVH ) {
 	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
 	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
 	float fresnel = exp2( ( -5.55473 * dotVH - 6.98316 ) * dotVH );
 	float fresnel = exp2( ( -5.55473 * dotVH - 6.98316 ) * dotVH );
 
 
-	return ( 1.0 - specularColor ) * fresnel + specularColor;
+	return ( f90 - f0 ) * fresnel + f0;
 
 
 } // validated
 } // validated
 
 
@@ -125,7 +125,7 @@ float D_GGX( const in float alpha, const in float dotNH ) {
 }
 }
 
 
 // GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
 // GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
-vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {
+vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in vec3 f90, const in float roughness ) {
 
 
 	float alpha = pow2( roughness ); // UE4's roughness
 	float alpha = pow2( roughness ); // UE4's roughness
 
 
@@ -136,7 +136,7 @@ vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 view
 	float dotNH = saturate( dot( normal, halfDir ) );
 	float dotNH = saturate( dot( normal, halfDir ) );
 	float dotLH = saturate( dot( incidentLight.direction, halfDir ) );
 	float dotLH = saturate( dot( incidentLight.direction, halfDir ) );
 
 
-	vec3 F = F_Schlick( specularColor, dotLH );
+	vec3 F = F_Schlick( f0, f90, dotLH );
 
 
 	float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );
 	float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );
 
 
@@ -318,7 +318,7 @@ vec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in Ge
 	float dotNH = saturate( dot( geometry.normal, halfDir ) );
 	float dotNH = saturate( dot( geometry.normal, halfDir ) );
 	float dotLH = saturate( dot( incidentLight.direction, halfDir ) );
 	float dotLH = saturate( dot( incidentLight.direction, halfDir ) );
 
 
-	vec3 F = F_Schlick( specularColor, dotLH );
+	vec3 F = F_Schlick( specularColor, vec3( 1.0 ), dotLH );
 
 
 	float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );
 	float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );
 
 

+ 29 - 1
src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js

@@ -11,11 +11,39 @@ material.specularRoughness = min( material.specularRoughness, 1.0 );
 
 
 #ifdef REFLECTIVITY
 #ifdef REFLECTIVITY
 
 
-	material.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );
+	#ifdef SPECULAR
+
+		vec3 specularIntensityFactor = vec3( specularIntensity );
+		vec3 specularTintFactor = specularTint;
+
+		#ifdef USE_SPECULARINTENSITYMAP
+
+			specularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a;
+
+		#endif
+
+		#ifdef USE_SPECULARTINTMAP
+
+			specularTintFactor *= specularTintMapTexelToLinear( texture2D( specularTintMap, vUv ) ).rgb;
+
+		#endif
+
+		material.specularColorF90 = mix( specularIntensityFactor, vec3( 1.0 ), metalnessFactor );
+
+	#else
+
+		vec3 specularIntensityFactor = vec3( 1.0 );
+		vec3 specularTintFactor = vec3( 1.0 );
+		material.specularColorF90 = vec3( 1.0 );
+
+	#endif
+
+	material.specularColor = mix( min( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ) * specularTintFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );
 
 
 #else
 #else
 
 
 	material.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );
 	material.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );
+	material.specularColorF90 = vec3( 1.0 );
 
 
 #endif
 #endif
 
 

+ 3 - 2
src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js

@@ -4,6 +4,7 @@ struct PhysicalMaterial {
 	vec3 diffuseColor;
 	vec3 diffuseColor;
 	float specularRoughness;
 	float specularRoughness;
 	vec3 specularColor;
 	vec3 specularColor;
+	vec3 specularColorF90;
 
 
 #ifdef CLEARCOAT
 #ifdef CLEARCOAT
 	float clearcoat;
 	float clearcoat;
@@ -93,7 +94,7 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 
 
 		float clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );
 		float clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );
 
 
-		reflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );
+		reflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), vec3( 1.0 ), material.clearcoatRoughness );
 
 
 	#else
 	#else
 
 
@@ -109,7 +110,7 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 			material.sheenColor
 			material.sheenColor
 		);
 		);
 	#else
 	#else
-		reflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);
+		reflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularColorF90, material.specularRoughness);
 	#endif
 	#endif
 
 
 	reflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
 	reflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );

+ 5 - 1
src/renderers/shaders/ShaderLib.js

@@ -304,7 +304,11 @@ ShaderLib.physical = {
 			thickness: { value: 0 },
 			thickness: { value: 0 },
 			thicknessMap: { value: null },
 			thicknessMap: { value: null },
 			attenuationDistance: { value: 0 },
 			attenuationDistance: { value: 0 },
-			attenuationColor: { value: new Color( 0x000000 ) }
+			attenuationColor: { value: new Color( 0x000000 ) },
+			specularIntensity: { value: 0 },
+			specularIntensityMap: { value: null },
+			specularTint: { value: new Color( 1, 1, 1 ) },
+			specularTintMap: { value: null },
 		}
 		}
 	] ),
 	] ),
 
 

+ 14 - 0
src/renderers/shaders/ShaderLib/meshphysical_frag.glsl.js

@@ -4,6 +4,7 @@ export default /* glsl */`
 #ifdef PHYSICAL
 #ifdef PHYSICAL
 	#define REFLECTIVITY
 	#define REFLECTIVITY
 	#define CLEARCOAT
 	#define CLEARCOAT
+	#define SPECULAR
 #endif
 #endif
 
 
 uniform vec3 diffuse;
 uniform vec3 diffuse;
@@ -23,6 +24,19 @@ uniform float opacity;
 	uniform float reflectivity;
 	uniform float reflectivity;
 #endif
 #endif
 
 
+#ifdef SPECULAR
+	uniform float specularIntensity;
+	uniform vec3 specularTint;
+
+	#ifdef USE_SPECULARINTENSITYMAP
+		uniform sampler2D specularIntensityMap;
+	#endif
+
+	#ifdef USE_SPECULARTINTMAP
+		uniform sampler2D specularTintMap;
+	#endif
+#endif
+
 #ifdef CLEARCOAT
 #ifdef CLEARCOAT
 	uniform float clearcoat;
 	uniform float clearcoat;
 	uniform float clearcoatRoughness;
 	uniform float clearcoatRoughness;

+ 25 - 0
src/renderers/webgl/WebGLMaterials.js

@@ -187,6 +187,8 @@ function WebGLMaterials( properties ) {
 		// 10. clearcoat map
 		// 10. clearcoat map
 		// 11. clearcoat normal map
 		// 11. clearcoat normal map
 		// 12. clearcoat roughnessMap map
 		// 12. clearcoat roughnessMap map
+		// 13. specular intensity map
+		// 14. specular tint map
 
 
 		let uvScaleMap;
 		let uvScaleMap;
 
 
@@ -238,6 +240,14 @@ function WebGLMaterials( properties ) {
 
 
 			uvScaleMap = material.clearcoatRoughnessMap;
 			uvScaleMap = material.clearcoatRoughnessMap;
 
 
+		} else if ( material.specularIntensityMap ) {
+
+			uvScaleMap = material.specularIntensityMap;
+
+		} else if ( material.specularTintMap ) {
+
+			uvScaleMap = material.specularTintMap;
+
 		}
 		}
 
 
 		if ( uvScaleMap !== undefined ) {
 		if ( uvScaleMap !== undefined ) {
@@ -615,6 +625,21 @@ function WebGLMaterials( properties ) {
 		uniforms.attenuationDistance.value = material.attenuationDistance;
 		uniforms.attenuationDistance.value = material.attenuationDistance;
 		uniforms.attenuationColor.value.copy( material.attenuationColor );
 		uniforms.attenuationColor.value.copy( material.attenuationColor );
 
 
+		uniforms.specularIntensity.value = material.specularIntensity;
+		uniforms.specularTint.value.copy( material.specularTint );
+
+		if ( material.specularIntensityMap ) {
+
+			uniforms.specularIntensityMap.value = material.specularIntensityMap;
+
+		}
+
+		if ( material.specularTintMap ) {
+
+			uniforms.specularTintMap.value = material.specularTintMap;
+
+		}
+
 	}
 	}
 
 
 	function refreshUniformsMatcap( uniforms, material ) {
 	function refreshUniformsMatcap( uniforms, material ) {

+ 5 - 0
src/renderers/webgl/WebGLProgram.js

@@ -465,6 +465,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 			parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
 			parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
 			parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
 			parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+			parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '',
+			parameters.specularTintMap ? '#define USE_SPECULARTINTMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
 			parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
 			parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
 			parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
 			parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
@@ -606,6 +608,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 			parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',
 			parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',
 			parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
 			parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+			parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '',
+			parameters.specularTintMap ? '#define USE_SPECULARTINTMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
 			parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
 			parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
 			parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
 			parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
@@ -655,6 +659,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 			parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '',
 			parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '',
 			parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '',
 			parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '',
 			parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '',
 			parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '',
+			parameters.specularTintMap ? getTexelDecodingFunction( 'specularTintMapTexelToLinear', parameters.specularTintMapEncoding ) : '',
 			parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '',
 			parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '',
 			getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ),
 			getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ),
 
 

+ 8 - 4
src/renderers/webgl/WebGLPrograms.js

@@ -36,8 +36,9 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 	const parameterNames = [
 	const parameterNames = [
 		'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor',
 		'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor',
 		'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV',
 		'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV',
-		'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap',
-		'roughnessMap', 'metalnessMap', 'gradientMap',
+		'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap',
+		'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap',
+		'specularMap', 'specularIntensityMap', 'specularTintMap', 'specularTintMapEncoding', 'roughnessMap', 'metalnessMap', 'gradientMap',
 		'alphaMap', 'combine', 'vertexColors', 'vertexAlphas', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2',
 		'alphaMap', 'combine', 'vertexColors', 'vertexAlphas', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2',
 		'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning',
 		'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning',
 		'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', 'premultipliedAlpha',
 		'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', 'premultipliedAlpha',
@@ -196,6 +197,9 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 			roughnessMap: !! material.roughnessMap,
 			roughnessMap: !! material.roughnessMap,
 			metalnessMap: !! material.metalnessMap,
 			metalnessMap: !! material.metalnessMap,
 			specularMap: !! material.specularMap,
 			specularMap: !! material.specularMap,
+			specularIntensityMap: !! material.specularIntensityMap,
+			specularTintMap: !! material.specularTintMap,
+			specularTintMapEncoding: getTextureEncodingFromMap( material.specularTintMap ),
 			alphaMap: !! material.alphaMap,
 			alphaMap: !! material.alphaMap,
 
 
 			gradientMap: !! material.gradientMap,
 			gradientMap: !! material.gradientMap,
@@ -211,8 +215,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 			vertexTangents: ( material.normalMap && object.geometry && object.geometry.attributes.tangent ),
 			vertexTangents: ( material.normalMap && object.geometry && object.geometry.attributes.tangent ),
 			vertexColors: material.vertexColors,
 			vertexColors: material.vertexColors,
 			vertexAlphas: material.vertexColors === true && object.geometry && object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4,
 			vertexAlphas: material.vertexColors === true && object.geometry && object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4,
-			vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap || !! material.thicknessMap,
-			uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.transmission || !! material.transmissionMap || !! material.thicknessMap ) && !! material.displacementMap,
+			vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularTintMap,
+			uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.transmission || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularTintMap ) && !! material.displacementMap,
 
 
 			fog: !! fog,
 			fog: !! fog,
 			useFog: material.fog,
 			useFog: material.fog,