浏览代码

GLTF KHR_materials_anisotropy support (#25580)

* plumbing

* refactored tangents

* fix bump map

* fix bump map again

* added IBL anisotropy support

* added direct anisotropy support

* reshuffle chunks

* fix clearcoat

* fix dangling endif

* separate physical BRDF from blinnPhong

* prod CI

* move Schlick to common

* fixed plumbing

* fixed shader compilation

* fixed mappings

* fixed tangents

* changed IBL mapping

* fix typo

* fixed direct lighting

* linearized mappings

* added anisotropyDirection

* fixed angle direction

* added references

* fix anisotropy map

* Angle -> Rotation

* fixed tangent sign

* rename anisotropyMat

* fix 90 deg rotation

* update to spec

* updated alphaT mapping

* Revert "updated alphaT mapping"

This reverts commit cebace1d27762c007580ca3eb72ae8b071aa25eb.

* WebGLProgram: Added missing ANISOTROPYMAP_UV define.

* Reverted builds.

* WebGLPrograms: Fixed typo.

* update to spec

* updated golden

* update texture definition

* added example

* add build files

* added screenshot

---------

Co-authored-by: Mr.doob <[email protected]>
Emmett Lalish 2 年之前
父节点
当前提交
22c5f25bda
共有 31 个文件被更改,包括 535 次插入36 次删除
  1. 27 6
      build/three.cjs
  2. 27 6
      build/three.js
  3. 0 0
      build/three.min.js
  4. 27 6
      build/three.module.js
  5. 0 0
      build/three.module.min.js
  6. 1 0
      examples/files.json
  7. 49 0
      examples/jsm/exporters/GLTFExporter.js
  8. 71 0
      examples/jsm/loaders/GLTFLoader.js
  9. 二进制
      examples/models/gltf/AnisotropyBarnLamp.glb
  10. 二进制
      examples/screenshots/webgl_loader_gltf_anisotropy.jpg
  11. 二进制
      examples/screenshots/webgl_loader_gltf_lights.jpg
  12. 112 0
      examples/webgl_loader_gltf_anisotropy.html
  13. 3 0
      manual/resources/threejs-material-table.js
  14. 4 0
      src/loaders/MaterialLoader.js
  15. 9 0
      src/materials/Material.js
  16. 32 6
      src/materials/MeshPhysicalMaterial.js
  17. 1 1
      src/renderers/WebGLRenderer.js
  18. 26 3
      src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js
  19. 9 1
      src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js
  20. 26 0
      src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js
  21. 52 2
      src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js
  22. 1 1
      src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js
  23. 1 1
      src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js
  24. 5 0
      src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl.js
  25. 5 0
      src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl.js
  26. 5 0
      src/renderers/shaders/ShaderChunk/uv_vertex.glsl.js
  27. 3 1
      src/renderers/shaders/ShaderLib.js
  28. 8 0
      src/renderers/shaders/ShaderLib/meshphysical.glsl.js
  29. 12 0
      src/renderers/webgl/WebGLMaterials.js
  30. 7 0
      src/renderers/webgl/WebGLProgram.js
  31. 12 2
      src/renderers/webgl/WebGLPrograms.js

文件差异内容过多而无法显示
+ 27 - 6
build/three.cjs


文件差异内容过多而无法显示
+ 27 - 6
build/three.js


文件差异内容过多而无法显示
+ 0 - 0
build/three.min.js


文件差异内容过多而无法显示
+ 27 - 6
build/three.module.js


文件差异内容过多而无法显示
+ 0 - 0
build/three.module.min.js


+ 1 - 0
examples/files.json

@@ -94,6 +94,7 @@
 		"webgl_loader_gltf_sheen",
 		"webgl_loader_gltf_transmission",
 		"webgl_loader_gltf_variants",
+		"webgl_loader_gltf_anisotropy",
 		"webgl_loader_ifc",
 		"webgl_loader_imagebitmap",
 		"webgl_loader_kmz",

+ 49 - 0
examples/jsm/exporters/GLTFExporter.js

@@ -120,6 +120,12 @@ class GLTFExporter {
 
 		} );
 
+		this.register( function ( writer ) {
+
+			return new GLTFMaterialsAnisotropyExtension( writer );
+
+		} );
+
 		this.register( function ( writer ) {
 
 			return new GLTFMaterialsEmissiveStrengthExtension( writer );
@@ -2856,6 +2862,49 @@ class GLTFMaterialsSheenExtension {
 
 }
 
+/**
+ * Anisotropy Materials Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_anisotropy
+ */
+class GLTFMaterialsAnisotropyExtension {
+
+	constructor( writer ) {
+
+		this.writer = writer;
+		this.name = 'KHR_materials_anisotropy';
+
+	}
+
+	writeMaterial( material, materialDef ) {
+
+		if ( ! material.isMeshPhysicalMaterial || material.anisotropy == 0.0 ) return;
+
+		const writer = this.writer;
+		const extensionsUsed = writer.extensionsUsed;
+
+		const extensionDef = {};
+
+		if ( material.anisotropyMap ) {
+
+			const anisotropyMapDef = { index: writer.processTexture( material.anisotropyMap ) };
+			writer.applyTextureTransform( anisotropyMapDef, material.anisotropyMap );
+			extensionDef.anisotropyTexture = anisotropyMapDef;
+
+		}
+
+		extensionDef.anisotropyStrength = material.anisotropy;
+		extensionDef.anisotropyRotation = material.anisotropyRotation;
+
+		materialDef.extensions = materialDef.extensions || {};
+		materialDef.extensions[ this.name ] = extensionDef;
+
+		extensionsUsed[ this.name ] = true;
+
+	}
+
+}
+
 /**
  * Materials Emissive Strength Extension
  *

+ 71 - 0
examples/jsm/loaders/GLTFLoader.js

@@ -142,6 +142,12 @@ class GLTFLoader extends Loader {
 
 		} );
 
+		this.register( function ( parser ) {
+
+			return new GLTFMaterialsAnisotropyExtension( parser );
+
+		} );
+
 		this.register( function ( parser ) {
 
 			return new GLTFLightsExtension( parser );
@@ -472,6 +478,7 @@ const EXTENSIONS = {
 	KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
 	KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
 	KHR_MATERIALS_IRIDESCENCE: 'KHR_materials_iridescence',
+	KHR_MATERIALS_ANISOTROPY: 'KHR_materials_anisotropy',
 	KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
 	KHR_MATERIALS_VOLUME: 'KHR_materials_volume',
 	KHR_TEXTURE_BASISU: 'KHR_texture_basisu',
@@ -1187,6 +1194,70 @@ class GLTFMaterialsSpecularExtension {
 
 }
 
+/**
+ * Materials anisotropy Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_anisotropy
+ */
+class GLTFMaterialsAnisotropyExtension {
+
+	constructor( parser ) {
+
+		this.parser = parser;
+		this.name = EXTENSIONS.KHR_MATERIALS_ANISOTROPY;
+
+	}
+
+	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 ];
+
+		if ( extension.anisotropyStrength !== undefined ) {
+
+			materialParams.anisotropy = extension.anisotropyStrength;
+
+		}
+
+		if ( extension.anisotropyRotation !== undefined ) {
+
+			materialParams.anisotropyRotation = extension.anisotropyRotation;
+
+		}
+
+		if ( extension.anisotropyTexture !== undefined ) {
+
+			pending.push( parser.assignTexture( materialParams, 'anisotropyMap', extension.anisotropyTexture ) );
+
+		}
+
+		return Promise.all( pending );
+
+	}
+
+}
+
 /**
  * BasisU Texture Extension
  *

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


二进制
examples/screenshots/webgl_loader_gltf_anisotropy.jpg


二进制
examples/screenshots/webgl_loader_gltf_lights.jpg


+ 112 - 0
examples/webgl_loader_gltf_anisotropy.html

@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - GLTFloader + Anisotropy</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+
+	<body>
+		<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_anisotropy" target="_blank" rel="noopener">KHR_materials_anisotropy</a><br />
+			Anisotropy Barn Lamp from <a href="https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/AnisotropyBarnLamp" target="_blank" rel="noopener">glTF-Sample-Models</a><br />
+			<a href="https://hdrihaven.com/hdri/?h=venice_sunset" target="_blank" rel="noopener">Venice Sunset</a> by <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
+		</div>
+
+		<!-- Import maps polyfill -->
+		<!-- Remove this when import maps will be widely supported -->
+		<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
+			import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
+
+			let renderer, scene, camera, controls;
+
+			init().catch( function ( err ) {
+
+				console.error( err );
+
+			} );
+
+			async function init() {
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				document.body.appendChild( renderer.domElement );
+
+				scene = new THREE.Scene();
+
+				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.25, 20 );
+				camera.position.set( 0.35, 0.05, 0.35 );
+
+				controls = new OrbitControls( camera, renderer.domElement );
+				controls.addEventListener( 'change', render );
+				controls.target.set( 0, -0.05, 0.1 );
+				controls.update();
+
+				const rgbeLoader = new RGBELoader()
+					.setPath( 'textures/equirectangular/' );
+
+				const gltfLoader = new GLTFLoader().setPath( 'models/gltf/' );
+
+				const [ texture, gltf ] = await Promise.all( [
+					rgbeLoader.loadAsync( 'venice_sunset_1k.hdr' ),
+					gltfLoader.loadAsync( 'AnisotropyBarnLamp.glb' ),
+				] );
+
+				// environment
+
+				texture.mapping = THREE.EquirectangularReflectionMapping;
+
+				scene.background = texture;
+				scene.environment = texture;
+
+				// model
+
+				scene.add( gltf.scene );
+
+				render();
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				render();
+
+			}
+
+			function render() {
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 3 - 0
manual/resources/threejs-material-table.js

@@ -164,6 +164,9 @@ const materials = [
 			'transmissionMap',
 			'attenuationDistance',
 			'attenuationColor',
+			'anisotropy',
+			'anisotropyRotation',
+			'anisotropyMap',
 			'specularIntensity',
 			'specularIntensityMap',
 			'specularColor',

+ 4 - 0
src/loaders/MaterialLoader.js

@@ -110,6 +110,8 @@ class MaterialLoader extends Loader {
 		if ( json.thickness !== undefined ) material.thickness = json.thickness;
 		if ( json.attenuationDistance !== undefined ) material.attenuationDistance = json.attenuationDistance;
 		if ( json.attenuationColor !== undefined && material.attenuationColor !== undefined ) material.attenuationColor.setHex( json.attenuationColor );
+		if ( json.anisotropy !== undefined ) material.anisotropy = json.anisotropy;
+		if ( json.anisotropyRotation !== undefined ) material.anisotropyRotation = json.anisotropyRotation;
 		if ( json.fog !== undefined ) material.fog = json.fog;
 		if ( json.flatShading !== undefined ) material.flatShading = json.flatShading;
 		if ( json.blending !== undefined ) material.blending = json.blending;
@@ -313,6 +315,8 @@ class MaterialLoader extends Loader {
 		if ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap );
 		if ( json.thicknessMap !== undefined ) material.thicknessMap = getTexture( json.thicknessMap );
 
+		if ( json.anisotropyMap !== undefined ) material.anisotropyMap = getTexture( json.anisotropyMap );
+
 		if ( json.sheenColorMap !== undefined ) material.sheenColorMap = getTexture( json.sheenColorMap );
 		if ( json.sheenRoughnessMap !== undefined ) material.sheenRoughnessMap = getTexture( json.sheenRoughnessMap );
 

+ 9 - 0
src/materials/Material.js

@@ -230,6 +230,15 @@ class Material extends EventDispatcher {
 
 		}
 
+		if ( this.anisotropy !== undefined ) data.anisotropy = this.anisotropy;
+		if ( this.anisotropyRotation !== undefined ) data.anisotropyRotation = this.anisotropyRotation;
+
+		if ( this.anisotropyMap && this.anisotropyMap.isTexture ) {
+
+			data.anisotropyMap = this.anisotropyMap.toJSON( meta ).uuid;
+
+		}
+
 		if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid;
 		if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid;
 		if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;

+ 32 - 6
src/materials/MeshPhysicalMaterial.js

@@ -20,6 +20,9 @@ class MeshPhysicalMaterial extends MeshStandardMaterial {
 
 		this.type = 'MeshPhysicalMaterial';
 
+		this.anisotropyRotation = 0;
+		this.anisotropyMap = null;
+
 		this.clearcoatMap = null;
 		this.clearcoatRoughness = 0.0;
 		this.clearcoatRoughnessMap = null;
@@ -63,30 +66,31 @@ class MeshPhysicalMaterial extends MeshStandardMaterial {
 		this.specularColor = new Color( 1, 1, 1 );
 		this.specularColorMap = null;
 
-		this._sheen = 0.0;
+		this._anisotropy = 0;
 		this._clearcoat = 0;
 		this._iridescence = 0;
+		this._sheen = 0.0;
 		this._transmission = 0;
 
 		this.setValues( parameters );
 
 	}
 
-	get sheen() {
+	get anisotropy() {
 
-		return this._sheen;
+		return this._anisotropy;
 
 	}
 
-	set sheen( value ) {
+	set anisotropy( value ) {
 
-		if ( this._sheen > 0 !== value > 0 ) {
+		if ( this._anisotropy > 0 !== value > 0 ) {
 
 			this.version ++;
 
 		}
 
-		this._sheen = value;
+		this._anisotropy = value;
 
 	}
 
@@ -126,6 +130,24 @@ class MeshPhysicalMaterial extends MeshStandardMaterial {
 
 	}
 
+	get sheen() {
+
+		return this._sheen;
+
+	}
+
+	set sheen( value ) {
+
+		if ( this._sheen > 0 !== value > 0 ) {
+
+			this.version ++;
+
+		}
+
+		this._sheen = value;
+
+	}
+
 	get transmission() {
 
 		return this._transmission;
@@ -155,6 +177,10 @@ class MeshPhysicalMaterial extends MeshStandardMaterial {
 
 		};
 
+		this.anisotropy = source.anisotropy;
+		this.anisotropyRotation = source.anisotropyRotation;
+		this.anisotropyMap = source.anisotropyMap;
+
 		this.clearcoat = source.clearcoat;
 		this.clearcoatMap = source.clearcoatMap;
 		this.clearcoatRoughness = source.clearcoatRoughness;

+ 1 - 1
src/renderers/WebGLRenderer.js

@@ -1612,7 +1612,7 @@ class WebGLRenderer {
 			const colorSpace = ( _currentRenderTarget === null ) ? _this.outputColorSpace : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace );
 			const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment );
 			const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4;
-			const vertexTangents = !! material.normalMap && !! geometry.attributes.tangent;
+			const vertexTangents = !! geometry.attributes.tangent && ( !! material.normalMap || material.anisotropy > 0 );
 			const morphTargets = !! geometry.morphAttributes.position;
 			const morphNormals = !! geometry.morphAttributes.normal;
 			const morphColors = !! geometry.morphAttributes.color;

+ 26 - 3
src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js

@@ -1,9 +1,9 @@
 export default /* glsl */`
-#if defined( USE_ENVMAP )
+#ifdef USE_ENVMAP
 
 	vec3 getIBLIrradiance( const in vec3 normal ) {
 
-		#if defined( ENVMAP_TYPE_CUBE_UV )
+		#ifdef ENVMAP_TYPE_CUBE_UV
 
 			vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );
 
@@ -21,7 +21,7 @@ export default /* glsl */`
 
 	vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {
 
-		#if defined( ENVMAP_TYPE_CUBE_UV )
+		#ifdef ENVMAP_TYPE_CUBE_UV
 
 			vec3 reflectVec = reflect( - viewDir, normal );
 
@@ -42,5 +42,28 @@ export default /* glsl */`
 
 	}
 
+	#ifdef USE_ANISOTROPY
+
+		vec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) {
+
+			#ifdef ENVMAP_TYPE_CUBE_UV
+
+			  // https://google.github.io/filament/Filament.md.html#lighting/imagebasedlights/anisotropy
+				vec3 bentNormal = cross( bitangent, viewDir );
+				bentNormal = normalize( cross( bentNormal, bitangent ) );
+				bentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) );
+
+				return getIBLRadiance( viewDir, bentNormal, roughness );
+
+			#else
+
+				return vec3( 0.0 );
+
+			#endif
+
+		}
+
+	#endif
+
 #endif
 `;

+ 9 - 1
src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js

@@ -20,7 +20,15 @@ export default /* glsl */`
 
 #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )
 
-	radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness );
+	#ifdef USE_ANISOTROPY
+
+		radiance += getIBLAnisotropyRadiance( geometry.viewDir, geometry.normal, material.roughness, material.anisotropyB, material.anisotropy );
+
+	#else
+
+		radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness );
+
+	#endif
 
 	#ifdef USE_CLEARCOAT
 

+ 26 - 0
src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js

@@ -116,5 +116,31 @@ material.roughness = min( material.roughness, 1.0 );
 
 	#endif
 
+#endif
+
+#ifdef USE_ANISOTROPY
+
+	#ifdef USE_ANISOTROPYMAP
+
+		mat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x );
+		vec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb;
+		vec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b;
+
+	#else
+
+		vec2 anisotropyV = anisotropyVector;
+
+	#endif
+
+	material.anisotropy = length( anisotropyV );
+	anisotropyV /= material.anisotropy;
+	material.anisotropy = saturate( material.anisotropy );
+
+	// Roughness along the anisotropy bitangent is the material roughness, while the tangent roughness increases with anisotropy.
+	material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) );
+
+	material.anisotropyT = tbn[ 0 ] * anisotropyV.x - tbn[ 1 ] * anisotropyV.y;
+	material.anisotropyB = tbn[ 1 ] * anisotropyV.x + tbn[ 0 ] * anisotropyV.y;
+
 #endif
 `;

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

@@ -39,6 +39,13 @@ struct PhysicalMaterial {
 		vec3 attenuationColor;
 	#endif
 
+	#ifdef USE_ANISOTROPY
+		float anisotropy;
+		float alphaT;
+		vec3 anisotropyT;
+		vec3 anisotropyB;
+	#endif
+
 };
 
 // temporary
@@ -79,6 +86,32 @@ float D_GGX( const in float alpha, const in float dotNH ) {
 
 }
 
+// https://google.github.io/filament/Filament.md.html#materialsystem/anisotropicmodel/anisotropicspecularbrdf
+#ifdef USE_ANISOTROPY
+
+	float V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) {
+
+		float gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) );
+		float gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) );
+		float v = 0.5 / ( gv + gl );
+
+		return saturate(v);
+
+	}
+
+	float D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) {
+
+		float a2 = alphaT * alphaB;
+		highp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH );
+		highp float v2 = dot( v, v );
+		float w2 = a2 / v2;
+
+		return RECIPROCAL_PI * a2 * pow2 ( w2 );
+
+	}
+
+#endif
+
 #ifdef USE_CLEARCOAT
 
 	// GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
@@ -132,9 +165,26 @@ vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 norm
 
 	#endif
 
-	float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );
+	#ifdef USE_ANISOTROPY
+
+		float dotTL = dot( material.anisotropyT, lightDir );
+		float dotTV = dot( material.anisotropyT, viewDir );
+		float dotTH = dot( material.anisotropyT, halfDir );
+		float dotBL = dot( material.anisotropyB, lightDir );
+		float dotBV = dot( material.anisotropyB, viewDir );
+		float dotBH = dot( material.anisotropyB, halfDir );
+
+		float V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL );
+
+		float D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH );
+
+	#else
+
+		float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );
 
-	float D = D_GGX( alpha, dotNH );
+		float D = D_GGX( alpha, dotNH );
+
+	#endif
 
 	return F * ( V * D );
 

+ 1 - 1
src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js

@@ -19,7 +19,7 @@ float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;
 
 #endif
 
-#ifdef USE_NORMALMAP_TANGENTSPACE
+#if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY )
 
 	#ifdef USE_TANGENT
 

+ 1 - 1
src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js

@@ -12,7 +12,7 @@ export default /* glsl */`
 
 #endif
 
-#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) )
+#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) )
 
 	// Normal Mapping Without Precomputed Tangents
 	// http://www.thetenthplanet.de/archives/1180

+ 5 - 0
src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl.js

@@ -48,6 +48,11 @@ export default /* glsl */`
 
 	varying vec2 vRoughnessMapUv;
 
+#endif
+#ifdef USE_ANISOTROPYMAP
+
+	varying vec2 vAnisotropyMapUv;
+
 #endif
 #ifdef USE_CLEARCOATMAP
 

+ 5 - 0
src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl.js

@@ -63,6 +63,11 @@ export default /* glsl */`
 	uniform mat3 roughnessMapTransform;
 	varying vec2 vRoughnessMapUv;
 
+#endif
+#ifdef USE_ANISOTROPYMAP
+
+	varying vec2 vAnisotropyMapUv;
+
 #endif
 #ifdef USE_CLEARCOATMAP
 

+ 5 - 0
src/renderers/shaders/ShaderChunk/uv_vertex.glsl.js

@@ -53,6 +53,11 @@ export default /* glsl */`
 
 	vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy;
 
+#endif
+#ifdef USE_ANISOTROPYMAP
+
+	vAnisotropyMapUv = ( vec3( ANISOTROPYMAP_UV, 1 ) ).xy;
+
 #endif
 #ifdef USE_CLEARCOATMAP
 

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

@@ -343,7 +343,9 @@ ShaderLib.physical = {
 			specularColorMapTransform: { value: /*@__PURE__*/ new Matrix3() },
 			specularIntensity: { value: 1 },
 			specularIntensityMap: { value: null },
-			specularIntensityMapTransform: { value: /*@__PURE__*/ new Matrix3() }
+			specularIntensityMapTransform: { value: /*@__PURE__*/ new Matrix3() },
+			anisotropyVector: { value: /*@__PURE__*/ new Vector2() },
+			anisotropyMap: { value: null },
 		}
 	] ),
 

+ 8 - 0
src/renderers/shaders/ShaderLib/meshphysical.glsl.js

@@ -112,6 +112,14 @@ uniform float opacity;
 	#endif
 #endif
 
+#ifdef USE_ANISOTROPY
+	uniform vec2 anisotropyVector;
+
+	#ifdef USE_ANISOTROPYMAP
+		uniform sampler2D anisotropyMap;
+	#endif
+#endif
+
 varying vec3 vViewPosition;
 
 #include <common>

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

@@ -498,6 +498,18 @@ function WebGLMaterials( renderer, properties ) {
 
 		}
 
+		if ( material.anisotropy > 0 ) {
+
+			uniforms.anisotropyVector.value.set( material.anisotropy * Math.cos( material.anisotropyRotation ), material.anisotropy * Math.sin( material.anisotropyRotation ) );
+
+			if ( material.anisotropyMap ) {
+
+				uniforms.anisotropyMap.value = material.anisotropyMap;
+
+			}
+
+		}
+
 		uniforms.specularIntensity.value = material.specularIntensity;
 		uniforms.specularColor.value.copy( material.specularColor );
 

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

@@ -469,6 +469,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 			parameters.displacementMap ? '#define USE_DISPLACEMENTMAP' : '',
 			parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
 
+			parameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '',
+
 			parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',
 			parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',
 			parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
@@ -505,6 +507,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 			parameters.metalnessMapUv ? '#define METALNESSMAP_UV ' + parameters.metalnessMapUv : '',
 			parameters.roughnessMapUv ? '#define ROUGHNESSMAP_UV ' + parameters.roughnessMapUv : '',
 
+			parameters.anisotropyMapUv ? '#define ANISOTROPYMAP_UV ' + parameters.anisotropyMapUv : '',
+
 			parameters.clearcoatMapUv ? '#define CLEARCOATMAP_UV ' + parameters.clearcoatMapUv : '',
 			parameters.clearcoatNormalMapUv ? '#define CLEARCOAT_NORMALMAP_UV ' + parameters.clearcoatNormalMapUv : '',
 			parameters.clearcoatRoughnessMapUv ? '#define CLEARCOAT_ROUGHNESSMAP_UV ' + parameters.clearcoatRoughnessMapUv : '',
@@ -678,6 +682,9 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 			parameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '',
 			parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
 
+			parameters.anisotropy ? '#define USE_ANISOTROPY' : '',
+			parameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '',
+
 			parameters.clearcoat ? '#define USE_CLEARCOAT' : '',
 			parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',
 			parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',

+ 12 - 2
src/renderers/webgl/WebGLPrograms.js

@@ -121,11 +121,14 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 		const HAS_METALNESSMAP = !! material.metalnessMap;
 		const HAS_ROUGHNESSMAP = !! material.roughnessMap;
 
+		const HAS_ANISOTROPY = material.anisotropy > 0;
 		const HAS_CLEARCOAT = material.clearcoat > 0;
 		const HAS_IRIDESCENCE = material.iridescence > 0;
 		const HAS_SHEEN = material.sheen > 0;
 		const HAS_TRANSMISSION = material.transmission > 0;
 
+		const HAS_ANISOTROPYMAP = HAS_ANISOTROPY && !! material.anisotropyMap;
+
 		const HAS_CLEARCOATMAP = HAS_CLEARCOAT && !! material.clearcoatMap;
 		const HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !! material.clearcoatNormalMap;
 		const HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !! material.clearcoatRoughnessMap;
@@ -198,6 +201,9 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 			metalnessMap: HAS_METALNESSMAP,
 			roughnessMap: HAS_ROUGHNESSMAP,
 
+			anisotropy: HAS_ANISOTROPY,
+			anisotropyMap: HAS_ANISOTROPYMAP,
+
 			clearcoat: HAS_CLEARCOAT,
 			clearcoatMap: HAS_CLEARCOATMAP,
 			clearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP,
@@ -241,6 +247,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 			metalnessMapUv: HAS_METALNESSMAP && getChannel( material.metalnessMap.channel ),
 			roughnessMapUv: HAS_ROUGHNESSMAP && getChannel( material.roughnessMap.channel ),
 
+			anisotropyMapUv: HAS_ANISOTROPYMAP && getChannel( material.anisotropyMap.channel ),
+
 			clearcoatMapUv: HAS_CLEARCOATMAP && getChannel( material.clearcoatMap.channel ),
 			clearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel( material.clearcoatNormalMap.channel ),
 			clearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel( material.clearcoatRoughnessMap.channel ),
@@ -262,7 +270,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 
 			//
 
-			vertexTangents: HAS_NORMALMAP && !! geometry.attributes.tangent,
+			vertexTangents: !! geometry.attributes.tangent && ( HAS_NORMALMAP || HAS_ANISOTROPY ),
 			vertexColors: material.vertexColors,
 			vertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4,
 			vertexUv1s: HAS_ATTRIBUTE_UV1,
@@ -394,6 +402,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 		array.push( parameters.emissiveMapUv );
 		array.push( parameters.metalnessMapUv );
 		array.push( parameters.roughnessMapUv );
+		array.push( parameters.anisotropyMapUv );
 		array.push( parameters.clearcoatMapUv );
 		array.push( parameters.clearcoatNormalMapUv );
 		array.push( parameters.clearcoatRoughnessMapUv );
@@ -467,6 +476,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 			_programLayers.enable( 15 );
 		if ( parameters.vertexTangents )
 			_programLayers.enable( 16 );
+		if ( parameters.anisotropy )
+			_programLayers.enable( 17 );
 
 		array.push( _programLayers.mask );
 		_programLayers.disableAll();
@@ -607,5 +618,4 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 
 }
 
-
 export { WebGLPrograms };

部分文件因为文件数量过多而无法显示