text: export default /* glsl */ `
  struct PhysicalMaterial {
  
  	vec3 diffuseColor;
  	float specularRoughness;
  	vec3 specularColor;
  
  #ifdef CLEARCOAT
  	float clearcoat;
  	float clearcoatRoughness;
  #endif
  #ifdef USE_SHEEN
  	vec3 sheenColor;
  #endif
  
  };
  
  #define MAXIMUM_SPECULAR_COEFFICIENT 0.16
  #define DEFAULT_SPECULAR_COEFFICIENT 0.04
  
  // Clear coat directional hemishperical reflectance (this approximation should be improved)
  float clearcoatDHRApprox( const in float roughness, const in float dotNL ) {
  
  	return DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );
  
  }
  
  #if NUM_RECT_AREA_LIGHTS > 0
  
  	void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
  
  		vec3 normal = geometry.normal;
  		vec3 viewDir = geometry.viewDir;
  		vec3 position = geometry.position;
  		vec3 lightPos = rectAreaLight.position;
  		vec3 halfWidth = rectAreaLight.halfWidth;
  		vec3 halfHeight = rectAreaLight.halfHeight;
  		vec3 lightColor = rectAreaLight.color;
  		float roughness = material.specularRoughness;
  
  		vec3 rectCoords[ 4 ];
  		rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; // counterclockwise; light shines in local neg z direction
  		rectCoords[ 1 ] = lightPos - halfWidth - halfHeight;
  		rectCoords[ 2 ] = lightPos - halfWidth + halfHeight;
  		rectCoords[ 3 ] = lightPos + halfWidth + halfHeight;
  
  		vec2 uv = LTC_Uv( normal, viewDir, roughness );
  
  		vec4 t1 = texture2D( ltc_1, uv );
  		vec4 t2 = texture2D( ltc_2, uv );
  
  		mat3 mInv = mat3(
  			vec3( t1.x, 0, t1.y ),
  			vec3(    0, 1,    0 ),
  			vec3( t1.z, 0, t1.w )
  		);
  
  		// LTC Fresnel Approximation by Stephen Hill
  		// http://blog.selfshadow.com/publications/s2016-advances/s2016_ltc_fresnel.pdf
  		vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );
  
  		reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );
  
  		reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );
  
  	}
  
  #endif
  
  void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
  
  	float dotNL = saturate( dot( geometry.normal, directLight.direction ) );
  
  	vec3 irradiance = dotNL * directLight.color;
  
  	#ifndef PHYSICALLY_CORRECT_LIGHTS
  
  		irradiance *= PI; // punctual light
  
  	#endif
  
  	#ifdef CLEARCOAT
  
  		float ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );
  
  		vec3 ccIrradiance = ccDotNL * directLight.color;
  
  		#ifndef PHYSICALLY_CORRECT_LIGHTS
  
  			ccIrradiance *= PI; // punctual light
  
  		#endif
  
  		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 );
  
  	#else
  
  		float clearcoatDHR = 0.0;
  
  	#endif
  
  	#ifdef USE_SHEEN
  		reflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(
  			material.specularRoughness,
  			directLight.direction,
  			geometry,
  			material.sheenColor
  		);
  	#else
  		reflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);
  	#endif
  
  	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 ) {
  
  	reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
  
  }
  
  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) {
  
  	#ifdef CLEARCOAT
  
  		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 );
  
  		float ccDotNL = ccDotNV;
  		float clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );
  
  	#else
  
  		float clearcoatDHR = 0.0;
  
  	#endif
  
  	float clearcoatInv = 1.0 - clearcoatDHR;
  
  	// Both indirect specular and indirect diffuse light accumulate here
  
  	vec3 singleScattering = vec3( 0.0 );
  	vec3 multiScattering = vec3( 0.0 );
  	vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;
  
  	BRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );
  
  	vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );
  
  	reflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;
  	reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;
  
  	reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;
  
  }
  
  #define RE_Direct				RE_Direct_Physical
  #define RE_Direct_RectArea		RE_Direct_RectArea_Physical
  #define RE_IndirectDiffuse		RE_IndirectDiffuse_Physical
  #define RE_IndirectSpecular		RE_IndirectSpecular_Physical
  
  // ref: https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
  float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {
  
  	return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );
  
  }
  `;
  