Browse Source

Merge pull request #17079 from arobertson0/dev

Added clear coat normal maps
Mr.doob 6 years ago
parent
commit
1bb818e835
35 changed files with 609 additions and 67 deletions
  1. 1 0
      examples/files.js
  2. BIN
      examples/textures/nvidia_tentacle/tentacle_object_space.png
  3. BIN
      examples/textures/nvidia_tentacle/tentacle_tangent_space.png
  4. 397 0
      examples/webgl_materials_clearcoat_normalmap.html
  5. 3 3
      examples/webgl_materials_envmaps_parallax.html
  6. 10 0
      src/materials/MeshPhysicalMaterial.d.ts
  7. 10 0
      src/materials/MeshPhysicalMaterial.js
  8. 13 0
      src/renderers/WebGLRenderer.js
  9. 3 0
      src/renderers/shaders/ShaderChunk.d.ts
  10. 6 0
      src/renderers/shaders/ShaderChunk.js
  11. 7 7
      src/renderers/shaders/ShaderChunk/bsdfs.glsl.js
  12. 7 0
      src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_begin.glsl.js
  13. 18 0
      src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_maps.glsl.js
  14. 8 0
      src/renderers/shaders/ShaderChunk/clearcoat_normalmap_pars_fragment.glsl.js
  15. 6 0
      src/renderers/shaders/ShaderChunk/common.glsl.js
  16. 1 1
      src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js
  17. 2 2
      src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl.js
  18. 1 1
      src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl.js
  19. 3 3
      src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js
  20. 1 1
      src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js
  21. 6 0
      src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js
  22. 12 2
      src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js
  23. 49 17
      src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js
  24. 5 0
      src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js
  25. 1 1
      src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js
  26. 21 20
      src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js
  27. 1 1
      src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl.js
  28. 1 1
      src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl.js
  29. 1 1
      src/renderers/shaders/ShaderChunk/uv_vertex.glsl.js
  30. 4 1
      src/renderers/shaders/ShaderLib.js
  31. 3 0
      src/renderers/shaders/ShaderLib/meshphysical_frag.glsl.js
  32. 1 1
      src/renderers/shaders/ShaderLib/normal_frag.glsl.js
  33. 2 2
      src/renderers/shaders/ShaderLib/normal_vert.glsl.js
  34. 3 1
      src/renderers/webgl/WebGLProgram.js
  35. 2 1
      src/renderers/webgl/WebGLPrograms.js

+ 1 - 0
examples/files.js

@@ -147,6 +147,7 @@ var files = {
 		"webgl_materials_bumpmap_skin",
 		"webgl_materials_bumpmap_skin",
 		"webgl_materials_cars",
 		"webgl_materials_cars",
 		"webgl_materials_channels",
 		"webgl_materials_channels",
+		"webgl_materials_clearcoat_normalmap",
 		"webgl_materials_compile",
 		"webgl_materials_compile",
 		"webgl_materials_cubemap",
 		"webgl_materials_cubemap",
 		"webgl_materials_cubemap_balls_reflection",
 		"webgl_materials_cubemap_balls_reflection",

BIN
examples/textures/nvidia_tentacle/tentacle_object_space.png


BIN
examples/textures/nvidia_tentacle/tentacle_tangent_space.png


+ 397 - 0
examples/webgl_materials_clearcoat_normalmap.html

@@ -0,0 +1,397 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+	<title>three.js webgl - materials - clearcoat normal</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">
+	<style>
+		#vt {
+			display: none
+		}
+
+		#vt,
+		#vt a {
+			color: orange;
+		}
+	</style>
+</head>
+
+<body>
+	<div id="info">
+		<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl - materials - clearcoat normal
+		demo.<br />
+	</div>
+
+	<script type="module">
+
+		import * as THREE from '../build/three.module.js';
+
+		import Stats from './jsm/libs/stats.module.js';
+
+		import { GUI } from './jsm/libs/dat.gui.module.js';
+
+		import { OrbitControls } from './jsm/controls/OrbitControls.js';
+		import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
+		import { PMREMGenerator } from './jsm/pmrem/PMREMGenerator.js';
+		import { PMREMCubeUVPacker } from './jsm/pmrem/PMREMCubeUVPacker.js';
+
+		var container, stats;
+
+		var camera, scene, renderer;
+
+		var guiParams = {
+			metalness: 0.0,
+			roughness: 1.0,
+			clearCoat: 1.0,
+			clearCoatRoughness: 0.0,
+			reflectivity: 0.0,
+		};
+
+		var guiNormalMapNames = [
+			"face",
+			"tentacle"
+		];
+
+		var guiMatParams = {
+			normalMap: "face",
+			normalScale: 1.0,
+			clearCoatNormalMap: "tentacle",
+			clearCoatNormalScale: 1.0,
+		};
+
+		var particleLight;
+		var meshes = [];
+		var mats = [];
+		var normalMaps = {};
+
+		const sphereSize = 80;
+		const sphereSpacing = 1.25 * sphereSize * 2;
+
+		const variationsX = [
+			( mat, scale, map ) => { },
+			( mat, scale, map ) => {
+
+				mat.clearCoatNormalScale = new THREE.Vector2( scale, scale );
+				if ( mat.clearCoatNormalMap !== map ) {
+
+					mat.clearCoatNormalMap = map;
+					mat.needsUpdate = true;
+
+				}
+
+			},
+		];
+
+		const variationsY = [
+			( mat, scale, map ) => { },
+			( mat, scale, map ) => {
+
+				mat.normalScale = new THREE.Vector2( scale, scale );
+				if ( mat.normalMap !== map ) {
+
+					mat.normalMap = map;
+					mat.needsUpdate = true;
+
+				}
+
+			},
+		];
+
+		const maxI = variationsX.length;
+		const maxJ = variationsY.length;
+
+		new THREE.FontLoader()
+			.load( 'fonts/gentilis_regular.typeface.json',
+				function ( font ) {
+
+					init( font );
+					animate();
+
+				}
+			);
+
+
+		function init( font ) {
+
+			container = document.createElement( 'div' );
+			document.body.appendChild( container );
+
+			camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 1, 10000 );
+			camera.position.z = 1500;
+
+			var genCubeUrls = function ( prefix, postfix ) {
+
+				return [
+					prefix + 'px' + postfix, prefix + 'nx' + postfix,
+					prefix + 'py' + postfix, prefix + 'ny' + postfix,
+					prefix + 'pz' + postfix, prefix + 'nz' + postfix
+				];
+
+			};
+
+			scene = new THREE.Scene();
+
+			var hdrUrls = genCubeUrls( './textures/cube/pisaHDR/', '.hdr' );
+
+			new HDRCubeTextureLoader()
+				.setType( THREE.UnsignedByteType )
+				.load( hdrUrls,
+					function ( hdrCubeMap ) {
+
+						var pmremGenerator = new PMREMGenerator( hdrCubeMap );
+						pmremGenerator.update( renderer );
+
+						var pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
+						pmremCubeUVPacker.update( renderer );
+
+						var hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+
+						var geometry = new THREE.SphereBufferGeometry( 80, 64, 32 );
+
+						var textureLoader = new THREE.TextureLoader();
+
+						normalMaps.tentacle = textureLoader
+							.load( "textures/nvidia_tentacle/tentacle_tangent_space.png" );
+
+						normalMaps.face = textureLoader
+							.load( "models/gltf/LeePerrySmith/Infinite-Level_02_Tangent_SmoothUV.jpg" );
+
+
+						var matParams = {
+							envMap: hdrCubeRenderTarget.texture,
+						};
+
+
+						for ( var ii = 0; ii < maxI; ii ++ ) {
+
+							if ( ! mats[ ii ] ) {
+
+								mats[ ii ] = [];
+
+							}
+
+							for ( var jj = 0; jj < maxJ; jj ++ ) {
+
+								var mat = mats[ ii ][ jj ] = new THREE.MeshPhysicalMaterial( matParams );
+
+								variationsX[ ii ]( mat, 1, normalMaps.tentacle );
+								variationsY[ jj ]( mat, 1, normalMaps.face );
+								var mesh = new THREE.Mesh( geometry, mat );
+								meshes.push( mesh );
+								mesh.position.x = ( ii - ( maxI - 1 ) / 2 ) * sphereSpacing;
+								mesh.position.y = ( jj - ( maxJ - 1 ) / 2 ) * sphereSpacing;
+								scene.add( mesh );
+
+							}
+
+						}
+
+						hdrCubeMap.magFilter = THREE.LinearFilter;
+						hdrCubeMap.needsUpdate = true;
+						scene.background = hdrCubeMap;
+
+						pmremGenerator.dispose();
+						pmremCubeUVPacker.dispose();
+
+					}
+
+				);
+
+			function addLabel( name, location, fontSize ) {
+
+				fontSize = fontSize | 20;
+				var textGeo = new THREE.TextBufferGeometry( name,
+					{
+
+						font: font,
+						size: fontSize,
+						height: 1,
+						curveSegments: 1
+
+					}
+				);
+
+				var textMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff } );
+				var textMesh = new THREE.Mesh( textGeo, textMaterial );
+
+				// Center the bounding box on the specified position w/ translation of -half diagonal
+				textGeo.computeBoundingBox();
+				var bb = textGeo.boundingBox.clone();
+				var displace = bb.max.sub( bb.min ).divide( new THREE.Vector3( 2, 2, 2 ) );
+
+				textMesh.position.copy( location.sub( displace ) );
+				scene.add( textMesh );
+
+			}
+
+
+			addLabel( "Normal Map", new THREE.Vector3( -350, 0, 0 ), 30 );
+			addLabel( "None", new THREE.Vector3( -250, -100, 0 ) );
+			addLabel( "Map", new THREE.Vector3( -300, 100, 0 ) );
+
+			addLabel( "Clearcoat Normal Map", new THREE.Vector3( 0, 260, 0 ), 30 );
+			addLabel( "None", new THREE.Vector3( -100, 200, 0 ) );
+			addLabel( "Map", new THREE.Vector3( 100, 200, 0 ) );
+
+			// LIGHTS
+
+			particleLight = new THREE.Mesh( new THREE.SphereBufferGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
+			scene.add( particleLight );
+
+			{
+
+				var ambientLight = new THREE.AmbientLight( 0x444444 );
+				scene.add( ambientLight );
+
+			}
+
+			{
+
+				var pointLight = new THREE.PointLight( 0xffffff, 1.25, 1000 );
+				particleLight.add( pointLight );
+
+			}
+
+			{
+
+				var directionalLight = new THREE.DirectionalLight( 0xffffff );
+				directionalLight.position.set( 1, - 0.5, - 1 );
+				scene.add( directionalLight );
+
+			}
+
+			renderer = new THREE.WebGLRenderer();
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			container.appendChild( renderer.domElement );
+
+			//
+
+			renderer.gammaInput = true;
+			renderer.gammaOutput = true;
+
+			//
+
+			stats = new Stats();
+			container.appendChild( stats.dom );
+
+			// EVENTS
+
+			var controls = new OrbitControls( camera, renderer.domElement );
+
+			window.addEventListener( 'resize', onWindowResize, false );
+
+			var gui = new GUI();
+
+			{
+
+				var materialGui = gui.addFolder( 'Physical Material' );
+				materialGui.add( guiParams, 'metalness', 0, 1, 0.01 );
+				materialGui.add( guiParams, 'roughness', 0, 1, 0.01 );
+				materialGui.add( guiParams, 'reflectivity', 0, 1, 0.01 );
+				materialGui.open();
+
+			}
+
+			{
+
+				var clearCoatGui = gui.addFolder( 'ClearCoat' );
+				clearCoatGui.add( guiParams, 'clearCoat', 0, 1, 0.01 );
+				clearCoatGui.add( guiParams, 'clearCoatRoughness', 0, 1, 0.01 );
+				clearCoatGui.open();
+
+			}
+
+			{
+
+				var normalGui = gui.addFolder( 'Normal Maps' );
+				normalGui.add( guiMatParams, 'normalMap', guiNormalMapNames );
+				normalGui.add( guiMatParams, 'normalScale', 0, 1, 0.01 );
+				normalGui.add( guiMatParams, 'clearCoatNormalMap', guiNormalMapNames );
+				normalGui.add( guiMatParams, 'clearCoatNormalScale', 0, 1, 0.01 );
+				normalGui.open();
+
+			}
+
+			gui.open();
+
+		}
+
+		//
+
+		function onWindowResize() {
+
+			var width = window.innerWidth;
+			var height = window.innerHeight;
+
+			camera.aspect = width / height;
+			camera.updateProjectionMatrix();
+
+			renderer.setSize( width, height );
+
+		}
+
+		//
+
+		function animate() {
+
+			requestAnimationFrame( animate );
+
+			render();
+
+			stats.update();
+
+		}
+
+		function render() {
+
+			var matColor = new THREE.Color().setHSL( 0.0, 0.5, 0.25 );
+
+			for ( var ii = 0; ii < maxI; ii ++ ) {
+
+				if ( mats[ ii ] ) { // avoid exceptions on first render before materials are set. TODO: lauch animation only after setup
+
+					for ( var jj = 0; jj < maxJ; jj ++ ) {
+
+						var mat = mats[ ii ][ jj ];
+						mat.color = matColor;
+						for ( var matParam in guiParams ) {
+
+							mat[ matParam ] = guiParams[ matParam ];
+
+						}
+
+						var normalMap = normalMaps[ guiMatParams.normalMap ];
+						var clearCoatNormalMap = normalMaps[ guiMatParams.clearCoatNormalMap ];
+
+						variationsX[ ii ]( mat, guiMatParams.clearCoatNormalScale, clearCoatNormalMap );
+						variationsY[ jj ]( mat, guiMatParams.normalScale, normalMap );
+
+					}
+
+				}
+
+			}
+
+			var timer = Date.now() * 0.00025;
+			particleLight.position.x = Math.sin( timer * 7 ) * 300;
+			particleLight.position.y = Math.cos( timer * 5 ) * 400;
+			particleLight.position.z = Math.cos( timer * 3 ) * 300;
+
+
+			for ( var mesh of meshes ) {
+
+				mesh.rotation.y += 0.01;
+
+			}
+
+
+			renderer.render( scene, camera );
+
+		}
+
+
+	</script>
+</body>
+</html>

+ 3 - 3
examples/webgl_materials_envmaps_parallax.html

@@ -156,15 +156,15 @@
 					return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );
 					return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );
 				}
 				}
 
 
-				vec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {
+				vec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float blinnShininessExponent, const in int maxMIPLevel ) {
 
 
 					#ifdef ENVMAP_MODE_REFLECTION
 					#ifdef ENVMAP_MODE_REFLECTION
 
 
-						vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );
+						vec3 reflectVec = reflect( -viewDir, normal );
 
 
 					#else
 					#else
 
 
-						vec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );
+						vec3 reflectVec = refract( -viewDir, normal, refractionRatio );
 
 
 					#endif
 					#endif
 
 

+ 10 - 0
src/materials/MeshPhysicalMaterial.d.ts

@@ -1,3 +1,5 @@
+import { Texture } from './../textures/Texture';
+import { Vector2 } from './../math/Vector2';
 import {
 import {
 	MeshStandardMaterialParameters,
 	MeshStandardMaterialParameters,
 	MeshStandardMaterial,
 	MeshStandardMaterial,
@@ -5,9 +7,14 @@ import {
 
 
 export interface MeshPhysicalMaterialParameters
 export interface MeshPhysicalMaterialParameters
 	extends MeshStandardMaterialParameters {
 	extends MeshStandardMaterialParameters {
+
 	reflectivity?: number;
 	reflectivity?: number;
 	clearCoat?: number;
 	clearCoat?: number;
 	clearCoatRoughness?: number;
 	clearCoatRoughness?: number;
+
+	clearCoatNormalScale?: Vector2;
+	clearCoatNormalMap?: Texture;
+
 }
 }
 
 
 export class MeshPhysicalMaterial extends MeshStandardMaterial {
 export class MeshPhysicalMaterial extends MeshStandardMaterial {
@@ -19,4 +26,7 @@ export class MeshPhysicalMaterial extends MeshStandardMaterial {
 	clearCoat: number;
 	clearCoat: number;
 	clearCoatRoughness: number;
 	clearCoatRoughness: number;
 
 
+	clearCoatNormalScale: Vector2;
+	clearCoatNormalMap: Texture | null;
+
 }
 }

+ 10 - 0
src/materials/MeshPhysicalMaterial.js

@@ -1,3 +1,4 @@
+import { Vector2 } from '../math/Vector2.js';
 import { MeshStandardMaterial } from './MeshStandardMaterial.js';
 import { MeshStandardMaterial } from './MeshStandardMaterial.js';
 
 
 /**
 /**
@@ -7,6 +8,9 @@ import { MeshStandardMaterial } from './MeshStandardMaterial.js';
  *  reflectivity: <float>
  *  reflectivity: <float>
  *  clearCoat: <float>
  *  clearCoat: <float>
  *  clearCoatRoughness: <float>
  *  clearCoatRoughness: <float>
+ *
+ *  clearCoatNormalScale: <Vector2>,
+ *  clearCoatNormalMap: new THREE.Texture( <Image> ),
  * }
  * }
  */
  */
 
 
@@ -23,6 +27,9 @@ function MeshPhysicalMaterial( parameters ) {
 	this.clearCoat = 0.0;
 	this.clearCoat = 0.0;
 	this.clearCoatRoughness = 0.0;
 	this.clearCoatRoughness = 0.0;
 
 
+	this.clearCoatNormalScale = new Vector2( 1, 1 );
+	this.clearCoatNormalMap = null;
+
 	this.setValues( parameters );
 	this.setValues( parameters );
 
 
 }
 }
@@ -43,6 +50,9 @@ MeshPhysicalMaterial.prototype.copy = function ( source ) {
 	this.clearCoat = source.clearCoat;
 	this.clearCoat = source.clearCoat;
 	this.clearCoatRoughness = source.clearCoatRoughness;
 	this.clearCoatRoughness = source.clearCoatRoughness;
 
 
+	this.clearCoatNormalMap = source.clearCoatNormalMap;
+	this.clearCoatNormalScale.copy( source.clearCoatNormalScale );
+
 	return this;
 	return this;
 
 
 };
 };

+ 13 - 0
src/renderers/WebGLRenderer.js

@@ -2273,6 +2273,19 @@ function WebGLRenderer( parameters ) {
 		uniforms.clearCoat.value = material.clearCoat;
 		uniforms.clearCoat.value = material.clearCoat;
 		uniforms.clearCoatRoughness.value = material.clearCoatRoughness;
 		uniforms.clearCoatRoughness.value = material.clearCoatRoughness;
 
 
+		if ( material.clearCoatNormalMap ) {
+
+			uniforms.clearCoatNormalScale.value.copy( material.clearCoatNormalScale );
+			uniforms.clearCoatNormalMap.value = material.clearCoatNormalMap;
+
+			if ( material.side === BackSide ) {
+
+				uniforms.clearCoatNormalScale.value.negate();
+
+			}
+
+		}
+
 	}
 	}
 
 
 	function refreshUniformsMatcap( uniforms, material ) {
 	function refreshUniformsMatcap( uniforms, material ) {

+ 3 - 0
src/renderers/shaders/ShaderChunk.d.ts

@@ -83,6 +83,9 @@ export let ShaderChunk: {
 	normal_fragment_maps: string;
 	normal_fragment_maps: string;
 	normal_vert: string;
 	normal_vert: string;
 	normalmap_pars_fragment: string;
 	normalmap_pars_fragment: string;
+	clearcoat_normal_fragment_begin: string;
+	clearcoat_normal_fragment_maps: string;
+	clearcoat_normalmap_pars_fragment: string;
 	packing: string;
 	packing: string;
 	points_frag: string;
 	points_frag: string;
 	points_vert: string;
 	points_vert: string;

+ 6 - 0
src/renderers/shaders/ShaderChunk.js

@@ -61,6 +61,9 @@ import morphtarget_vertex from './ShaderChunk/morphtarget_vertex.glsl.js';
 import normal_fragment_begin from './ShaderChunk/normal_fragment_begin.glsl.js';
 import normal_fragment_begin from './ShaderChunk/normal_fragment_begin.glsl.js';
 import normal_fragment_maps from './ShaderChunk/normal_fragment_maps.glsl.js';
 import normal_fragment_maps from './ShaderChunk/normal_fragment_maps.glsl.js';
 import normalmap_pars_fragment from './ShaderChunk/normalmap_pars_fragment.glsl.js';
 import normalmap_pars_fragment from './ShaderChunk/normalmap_pars_fragment.glsl.js';
+import clearcoat_normal_fragment_begin from './ShaderChunk/clearcoat_normal_fragment_begin.glsl.js';
+import clearcoat_normal_fragment_maps from './ShaderChunk/clearcoat_normal_fragment_maps.glsl.js';
+import clearcoat_normalmap_pars_fragment from './ShaderChunk/clearcoat_normalmap_pars_fragment.glsl.js';
 import packing from './ShaderChunk/packing.glsl.js';
 import packing from './ShaderChunk/packing.glsl.js';
 import premultiplied_alpha_fragment from './ShaderChunk/premultiplied_alpha_fragment.glsl.js';
 import premultiplied_alpha_fragment from './ShaderChunk/premultiplied_alpha_fragment.glsl.js';
 import project_vertex from './ShaderChunk/project_vertex.glsl.js';
 import project_vertex from './ShaderChunk/project_vertex.glsl.js';
@@ -183,6 +186,9 @@ export var ShaderChunk = {
 	normal_fragment_begin: normal_fragment_begin,
 	normal_fragment_begin: normal_fragment_begin,
 	normal_fragment_maps: normal_fragment_maps,
 	normal_fragment_maps: normal_fragment_maps,
 	normalmap_pars_fragment: normalmap_pars_fragment,
 	normalmap_pars_fragment: normalmap_pars_fragment,
+	clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin,
+	clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps,
+	clearcoat_normalmap_pars_fragment: clearcoat_normalmap_pars_fragment,
 	packing: packing,
 	packing: packing,
 	premultiplied_alpha_fragment: premultiplied_alpha_fragment,
 	premultiplied_alpha_fragment: premultiplied_alpha_fragment,
 	project_vertex: project_vertex,
 	project_vertex: project_vertex,

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

@@ -125,15 +125,15 @@ 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 GeometricContext geometry, 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 specularColor, const in float roughness ) {
 
 
 	float alpha = pow2( roughness ); // UE4's roughness
 	float alpha = pow2( roughness ); // UE4's roughness
 
 
-	vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );
+	vec3 halfDir = normalize( incidentLight.direction + viewDir );
 
 
-	float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );
-	float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
-	float dotNH = saturate( dot( geometry.normal, halfDir ) );
+	float dotNL = saturate( dot( normal, incidentLight.direction ) );
+	float dotNV = saturate( dot( normal, viewDir ) );
+	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( specularColor, dotLH );
@@ -264,9 +264,9 @@ vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in m
 // End Rect Area Light
 // End Rect Area Light
 
 
 // ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
 // ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
-vec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {
+vec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {
 
 
-	float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
+	float dotNV = saturate( dot( normal, viewDir ) );
 
 
 	vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
 	vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
 
 

+ 7 - 0
src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_begin.glsl.js

@@ -0,0 +1,7 @@
+export default /* glsl */`
+#ifdef USE_CLEARCOAT_NORMALMAP
+
+  vec3 clearCoatNormal = geometryNormal;
+
+#endif
+`;

+ 18 - 0
src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_maps.glsl.js

@@ -0,0 +1,18 @@
+export default /* glsl */`
+#ifdef USE_CLEARCOAT_NORMALMAP
+
+	#ifdef USE_TANGENT
+
+		mat3 vTBN = mat3( tangent, bitangent, clearCoatNormal );
+		vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;
+		mapN.xy = clearCoatNormalScale * mapN.xy;
+		clearCoatNormal = normalize( vTBN * mapN );
+
+	#else
+
+		clearCoatNormal = perturbNormal2Arb( - vViewPosition, clearCoatNormal, clearCoatNormalScale, clearCoatNormalMap );
+
+	#endif
+
+#endif
+`;

+ 8 - 0
src/renderers/shaders/ShaderChunk/clearcoat_normalmap_pars_fragment.glsl.js

@@ -0,0 +1,8 @@
+export default /* glsl */`
+#ifdef USE_CLEARCOAT_NORMALMAP
+
+	uniform sampler2D clearCoatNormalMap;
+	uniform vec2 clearCoatNormalScale;
+
+#endif
+`;

+ 6 - 0
src/renderers/shaders/ShaderChunk/common.glsl.js

@@ -39,6 +39,12 @@ struct GeometricContext {
 	vec3 position;
 	vec3 position;
 	vec3 normal;
 	vec3 normal;
 	vec3 viewDir;
 	vec3 viewDir;
+
+	#ifdef USE_CLEARCOAT_NORMALMAP
+
+		vec3 clearCoatNormal;
+
+	#endif
 };
 };
 
 
 vec3 transformDirection( in vec3 dir, in mat4 matrix ) {
 vec3 transformDirection( in vec3 dir, in mat4 matrix ) {

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

@@ -1,7 +1,7 @@
 export default /* glsl */`
 export default /* glsl */`
 #ifdef USE_ENVMAP
 #ifdef USE_ENVMAP
 
 
-	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )
+	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( PHONG )
 
 
 		vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );
 		vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );
 
 

+ 2 - 2
src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl.js

@@ -6,7 +6,7 @@ export default /* glsl */`
 
 
 #ifdef USE_ENVMAP
 #ifdef USE_ENVMAP
 
 
-	#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )
+	#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( PHONG ) )
 		varying vec3 vWorldPosition;
 		varying vec3 vWorldPosition;
 	#endif
 	#endif
 
 
@@ -18,7 +18,7 @@ export default /* glsl */`
 	uniform float flipEnvMap;
 	uniform float flipEnvMap;
 	uniform int maxMipLevel;
 	uniform int maxMipLevel;
 
 
-	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )
+	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )
 		uniform float refractionRatio;
 		uniform float refractionRatio;
 	#else
 	#else
 		varying vec3 vReflect;
 		varying vec3 vReflect;

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

@@ -1,7 +1,7 @@
 export default /* glsl */`
 export default /* glsl */`
 #ifdef USE_ENVMAP
 #ifdef USE_ENVMAP
 
 
-	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )
+	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( PHONG )
 		varying vec3 vWorldPosition;
 		varying vec3 vWorldPosition;
 
 
 	#else
 	#else

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

@@ -54,15 +54,15 @@ export default /* glsl */`
 
 
 	}
 	}
 
 
-	vec3 getLightProbeIndirectRadiance( /*const in SpecularLightProbe specularLightProbe,*/ const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {
+	vec3 getLightProbeIndirectRadiance( /*const in SpecularLightProbe specularLightProbe,*/ const in vec3 viewDir, const in vec3 normal, const in float blinnShininessExponent, const in int maxMIPLevel ) {
 
 
 		#ifdef ENVMAP_MODE_REFLECTION
 		#ifdef ENVMAP_MODE_REFLECTION
 
 
-			vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );
+		  vec3 reflectVec = reflect( -viewDir, normal );
 
 
 		#else
 		#else
 
 
-			vec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );
+		  vec3 reflectVec = refract( -viewDir, normal, refractionRatio );
 
 
 		#endif
 		#endif
 
 

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

@@ -1,7 +1,7 @@
 export default /* glsl */`
 export default /* glsl */`
 #ifdef USE_ENVMAP
 #ifdef USE_ENVMAP
 
 
-	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )
+	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( PHONG )
 
 
 		vWorldPosition = worldPosition.xyz;
 		vWorldPosition = worldPosition.xyz;
 
 

+ 6 - 0
src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js

@@ -20,6 +20,12 @@ geometry.position = - vViewPosition;
 geometry.normal = normal;
 geometry.normal = normal;
 geometry.viewDir = normalize( vViewPosition );
 geometry.viewDir = normalize( vViewPosition );
 
 
+#ifdef USE_CLEARCOAT_NORMALMAP
+
+	geometry.clearCoatNormal = clearCoatNormal;
+
+#endif
+
 IncidentLight directLight;
 IncidentLight directLight;
 
 
 #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )
 #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )

+ 12 - 2
src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js

@@ -25,10 +25,20 @@ export default /* glsl */`
 
 
 #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )
 #if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )
 
 
-	radiance += getLightProbeIndirectRadiance( /*specularLightProbe,*/ geometry, Material_BlinnShininessExponent( material ), maxMipLevel );
+  radiance += getLightProbeIndirectRadiance( /*specularLightProbe,*/ geometry.viewDir, geometry.normal, Material_BlinnShininessExponent( material ), maxMipLevel );
 
 
 	#ifndef STANDARD
 	#ifndef STANDARD
-		clearCoatRadiance += getLightProbeIndirectRadiance( /*specularLightProbe,*/ geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );
+
+		#ifdef USE_CLEARCOAT_NORMALMAP
+
+			clearCoatRadiance += getLightProbeIndirectRadiance( /*specularLightProbe,*/ geometry.viewDir, geometry.clearCoatNormal, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );
+
+		#else
+
+			clearCoatRadiance += getLightProbeIndirectRadiance( /*specularLightProbe,*/ geometry.viewDir, geometry.normal, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );
+
+		#endif
+
 	#endif
 	#endif
 
 
 #endif
 #endif

+ 49 - 17
src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js

@@ -77,21 +77,41 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 	#endif
 	#endif
 
 
 	#ifndef STANDARD
 	#ifndef STANDARD
-		float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );
-	#else
-		float clearCoatDHR = 0.0;
-	#endif
 
 
-	reflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );
+		#ifdef USE_CLEARCOAT_NORMALMAP
 
 
-	reflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
+			float ccDotNL = saturate( dot( geometry.clearCoatNormal, directLight.direction ) );
 
 
-	#ifndef STANDARD
+			vec3 ccIrradiance = ccDotNL * directLight.color;
+
+			#ifndef PHYSICALLY_CORRECT_LIGHTS
+
+				ccIrradiance *= PI; // punctual light
+
+			#endif
+
+			float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, ccDotNL );
 
 
-		reflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );
+			reflectedLight.directSpecular += ccIrradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearCoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );
+
+		#else
+
+			float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );
+
+			reflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );
+
+		#endif
+
+	#else
+
+		float clearCoatDHR = 0.0;
 
 
 	#endif
 	#endif
 
 
+	reflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness );
+
+	reflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
+
 }
 }
 
 
 void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
 void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
@@ -109,11 +129,28 @@ void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricCo
 void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
 void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
 
 
 	#ifndef STANDARD
 	#ifndef STANDARD
-		float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
-		float dotNL = dotNV;
-		float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );
+
+		#ifdef USE_CLEARCOAT_NORMALMAP
+
+			float ccDotNV = saturate( dot( geometry.clearCoatNormal, geometry.viewDir ) );
+
+			reflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearCoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );
+
+		#else
+
+			float ccDotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
+
+			reflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.normal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );
+
+		#endif
+
+		float ccDotNL = ccDotNV;
+		float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, ccDotNL );
+
 	#else
 	#else
+
 		float clearCoatDHR = 0.0;
 		float clearCoatDHR = 0.0;
+
 	#endif
 	#endif
 
 
 	float clearCoatInv = 1.0 - clearCoatDHR;
 	float clearCoatInv = 1.0 - clearCoatDHR;
@@ -136,15 +173,10 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia
 
 
 	#else
 	#else
 
 
-		reflectedLight.indirectSpecular += clearCoatInv * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );
+		reflectedLight.indirectSpecular += clearCoatInv * radiance * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness );
 
 
 	#endif
 	#endif
 
 
-	#ifndef STANDARD
-
-		reflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );
-
-	#endif
 }
 }
 
 
 #define RE_Direct				RE_Direct_Physical
 #define RE_Direct				RE_Direct_Physical

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

@@ -31,5 +31,10 @@ export default /* glsl */`
 
 
 	#endif
 	#endif
 
 
+#endif
+#ifdef USE_CLEARCOAT_NORMALMAP
+
+	vec3 geometryNormal = normal;
+
 #endif
 #endif
 `;
 `;

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

@@ -30,7 +30,7 @@ export default /* glsl */`
 
 
 		#else
 		#else
 
 
-			normal = perturbNormal2Arb( -vViewPosition, normal );
+			normal = perturbNormal2Arb( -vViewPosition, normal, normalScale, normalMap );
 
 
 		#endif
 		#endif
 
 

+ 21 - 20
src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js

@@ -8,37 +8,38 @@ export default /* glsl */`
 
 
 		uniform mat3 normalMatrix;
 		uniform mat3 normalMatrix;
 
 
-	#else
+	#endif
+#endif
 
 
-		// Per-Pixel Tangent Space Normal Mapping
-		// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
+#if ( defined ( USE_NORMALMAP ) && !defined ( OBJECTSPACE_NORMALMAP )) || defined ( USE_CLEARCOAT_NORMALMAP )
 
 
-		vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {
+	// Per-Pixel Tangent Space Normal Mapping
+	// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
 
 
-			// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
+	vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec2 normalScale, in sampler2D normalMap ) {
 
 
-			vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
-			vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
-			vec2 st0 = dFdx( vUv.st );
-			vec2 st1 = dFdy( vUv.st );
+		// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
 
 
-			float scale = sign( st1.t * st0.s - st0.t * st1.s ); // we do not care about the magnitude
+		vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
+		vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
+		vec2 st0 = dFdx( vUv.st );
+		vec2 st1 = dFdy( vUv.st );
 
 
-			vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );
-			vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );
-			vec3 N = normalize( surf_norm );
-			mat3 tsn = mat3( S, T, N );
+		float scale = sign( st1.t * st0.s - st0.t * st1.s ); // we do not care about the magnitude
 
 
-			vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;
+		vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );
+		vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );
+		vec3 N = normalize( surf_norm );
+		mat3 tsn = mat3( S, T, N );
 
 
-			mapN.xy *= normalScale;
-			mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );
+		vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;
 
 
-			return normalize( tsn * mapN );
+		mapN.xy *= normalScale;
+		mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );
 
 
-		}
+		return normalize( tsn * mapN );
 
 
-	#endif
+	}
 
 
 #endif
 #endif
 `;
 `;

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

@@ -1,5 +1,5 @@
 export default /* glsl */`
 export default /* glsl */`
-#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )
+#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )
 
 
 	varying vec2 vUv;
 	varying vec2 vUv;
 
 

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

@@ -1,5 +1,5 @@
 export default /* glsl */`
 export default /* glsl */`
-#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )
+#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )
 
 
 	varying vec2 vUv;
 	varying vec2 vUv;
 	uniform mat3 uvTransform;
 	uniform mat3 uvTransform;

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

@@ -1,5 +1,5 @@
 export default /* glsl */`
 export default /* glsl */`
-#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )
+#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )  || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )
 
 
 	vUv = ( uvTransform * vec3( uv, 1 ) ).xy;
 	vUv = ( uvTransform * vec3( uv, 1 ) ).xy;
 
 

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

@@ -1,5 +1,6 @@
 import { ShaderChunk } from './ShaderChunk.js';
 import { ShaderChunk } from './ShaderChunk.js';
 import { mergeUniforms } from './UniformsUtils.js';
 import { mergeUniforms } from './UniformsUtils.js';
+import { Vector2 } from '../../math/Vector2.js';
 import { Vector3 } from '../../math/Vector3.js';
 import { Vector3 } from '../../math/Vector3.js';
 import { UniformsLib } from './UniformsLib.js';
 import { UniformsLib } from './UniformsLib.js';
 import { Color } from '../../math/Color.js';
 import { Color } from '../../math/Color.js';
@@ -273,7 +274,9 @@ ShaderLib.physical = {
 		ShaderLib.standard.uniforms,
 		ShaderLib.standard.uniforms,
 		{
 		{
 			clearCoat: { value: 0 },
 			clearCoat: { value: 0 },
-			clearCoatRoughness: { value: 0 }
+			clearCoatRoughness: { value: 0 },
+			clearCoatNormalScale: { value: new Vector2( 1, 1 ) },
+			clearCoatNormalMap: { value: null },
 		}
 		}
 	] ),
 	] ),
 
 

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

@@ -48,6 +48,7 @@ varying vec3 vViewPosition;
 #include <shadowmap_pars_fragment>
 #include <shadowmap_pars_fragment>
 #include <bumpmap_pars_fragment>
 #include <bumpmap_pars_fragment>
 #include <normalmap_pars_fragment>
 #include <normalmap_pars_fragment>
+#include <clearcoat_normalmap_pars_fragment>
 #include <roughnessmap_pars_fragment>
 #include <roughnessmap_pars_fragment>
 #include <metalnessmap_pars_fragment>
 #include <metalnessmap_pars_fragment>
 #include <logdepthbuf_pars_fragment>
 #include <logdepthbuf_pars_fragment>
@@ -70,6 +71,8 @@ void main() {
 	#include <metalnessmap_fragment>
 	#include <metalnessmap_fragment>
 	#include <normal_fragment_begin>
 	#include <normal_fragment_begin>
 	#include <normal_fragment_maps>
 	#include <normal_fragment_maps>
+	#include <clearcoat_normal_fragment_begin>
+	#include <clearcoat_normal_fragment_maps>
 	#include <emissivemap_fragment>
 	#include <emissivemap_fragment>
 
 
 	// accumulation
 	// accumulation

+ 1 - 1
src/renderers/shaders/ShaderLib/normal_frag.glsl.js

@@ -3,7 +3,7 @@ export default /* glsl */`
 
 
 uniform float opacity;
 uniform float opacity;
 
 
-#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )
+#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) ) || defined( USE_CLEARCOAT_NORMALMAP )
 
 
 	varying vec3 vViewPosition;
 	varying vec3 vViewPosition;
 
 

+ 2 - 2
src/renderers/shaders/ShaderLib/normal_vert.glsl.js

@@ -1,7 +1,7 @@
 export default /* glsl */`
 export default /* glsl */`
 #define NORMAL
 #define NORMAL
 
 
-#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )
+#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) ) || defined( USE_CLEARCOAT_NORMALMAP )
 
 
 	varying vec3 vViewPosition;
 	varying vec3 vViewPosition;
 
 
@@ -58,7 +58,7 @@ void main() {
 	#include <logdepthbuf_vertex>
 	#include <logdepthbuf_vertex>
 	#include <clipping_planes_vertex>
 	#include <clipping_planes_vertex>
 
 
-#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )
+#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) ) || defined( USE_CLEARCOAT_NORMALMAP )
 
 
 	vViewPosition = - mvPosition.xyz;
 	vViewPosition = - mvPosition.xyz;
 
 

+ 3 - 1
src/renderers/webgl/WebGLProgram.js

@@ -118,7 +118,7 @@ function generateExtensions( extensions, parameters, rendererExtensions ) {
 	extensions = extensions || {};
 	extensions = extensions || {};
 
 
 	var chunks = [
 	var chunks = [
-		( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || ( parameters.normalMap && ! parameters.objectSpaceNormalMap ) || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
+		( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || ( parameters.normalMap && ! parameters.objectSpaceNormalMap ) || parameters.clearCoatNormalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
 		( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',
 		( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',
 		( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '',
 		( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '',
 		( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : ''
 		( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : ''
@@ -389,6 +389,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 			parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 			parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 			parameters.normalMap ? '#define USE_NORMALMAP' : '',
 			parameters.normalMap ? '#define USE_NORMALMAP' : '',
 			( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '',
 			( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_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.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
@@ -505,6 +506,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 			parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 			parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 			parameters.normalMap ? '#define USE_NORMALMAP' : '',
 			parameters.normalMap ? '#define USE_NORMALMAP' : '',
 			( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '',
 			( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '',
+			parameters.clearCoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
 			parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
 			parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',

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

@@ -29,7 +29,7 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 
 	var parameterNames = [
 	var parameterNames = [
 		"precision", "supportsVertexTextures", "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding",
 		"precision", "supportsVertexTextures", "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding",
-		"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "displacementMap", "specularMap",
+		"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "clearCoatNormalMap", "displacementMap", "specularMap",
 		"roughnessMap", "metalnessMap", "gradientMap",
 		"roughnessMap", "metalnessMap", "gradientMap",
 		"alphaMap", "combine", "vertexColors", "vertexTangents", "fog", "useFog", "fogExp",
 		"alphaMap", "combine", "vertexColors", "vertexTangents", "fog", "useFog", "fogExp",
 		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
 		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
@@ -153,6 +153,7 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 			bumpMap: !! material.bumpMap,
 			bumpMap: !! material.bumpMap,
 			normalMap: !! material.normalMap,
 			normalMap: !! material.normalMap,
 			objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap,
 			objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap,
+			clearCoatNormalMap: !! material.clearCoatNormalMap,
 			displacementMap: !! material.displacementMap,
 			displacementMap: !! material.displacementMap,
 			roughnessMap: !! material.roughnessMap,
 			roughnessMap: !! material.roughnessMap,
 			metalnessMap: !! material.metalnessMap,
 			metalnessMap: !! material.metalnessMap,