text: export default /* glsl */`
  #ifdef USE_SHADOWMAP
  
  	#if NUM_DIR_LIGHT_SHADOWS > 0
  
  		uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];
  		varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];
  
  		struct DirectionalLightShadow {
  			float shadowBias;
  			float shadowNormalBias;
  			float shadowRadius;
  			vec2 shadowMapSize;
  		};
  
  		uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];
  
  	#endif
  
  	#if NUM_SPOT_LIGHT_SHADOWS > 0
  
  		uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];
  		varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];
  
  		struct SpotLightShadow {
  			float shadowBias;
  			float shadowNormalBias;
  			float shadowRadius;
  			vec2 shadowMapSize;
  		};
  
  		uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];
  
  	#endif
  
  	#if NUM_POINT_LIGHT_SHADOWS > 0
  
  		uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];
  		varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];
  
  		struct PointLightShadow {
  			float shadowBias;
  			float shadowNormalBias;
  			float shadowRadius;
  			vec2 shadowMapSize;
  			float shadowCameraNear;
  			float shadowCameraFar;
  		};
  
  		uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];
  
  	#endif
  
  	/*
  	#if NUM_RECT_AREA_LIGHTS > 0
  
  		// TODO (abelnation): create uniforms for area light shadows
  
  	#endif
  	*/
  
  	/*
  	#if NUM_TEXTURE_AREA_LIGHTS > 0
  
  		// TODO (abelnation): create uniforms for area light shadows
  
  
  	#endif
  	*/
  
  
  	float texture2DCompare( sampler2D depths, vec2 uv, float compare ) {
  
  		return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );
  
  	}
  
  	vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {
  
  		return unpackRGBATo2Half( texture2D( shadow, uv ) );
  
  	}
  
  	float VSMShadow (sampler2D shadow, vec2 uv, float compare ){
  
  		float occlusion = 1.0;
  
  		vec2 distribution = texture2DDistribution( shadow, uv );
  
  		float hard_shadow = step( compare , distribution.x ); // Hard Shadow
  
  		if (hard_shadow != 1.0 ) {
  
  			float distance = compare - distribution.x ;
  			float variance = max( 0.00000, distribution.y * distribution.y );
  			float softness_probability = variance / (variance + distance * distance ); // Chebeyshevs inequality
  			softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); // 0.3 reduces light bleed
  			occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );
  
  		}
  		return occlusion;
  
  	}
  
  	float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
  
  		float shadow = 1.0;
  
  		shadowCoord.xyz /= shadowCoord.w;
  		shadowCoord.z += shadowBias;
  
  		// if ( something && something ) breaks ATI OpenGL shader compiler
  		// if ( all( something, something ) ) using this instead
  
  		bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );
  		bool inFrustum = all( inFrustumVec );
  
  		bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );
  
  		bool frustumTest = all( frustumTestVec );
  
  		if ( frustumTest ) {
  
  		#if defined( SHADOWMAP_TYPE_PCF )
  
  			vec2 texelSize = vec2( 1.0 ) / shadowMapSize;
  
  			float dx0 = - texelSize.x * shadowRadius;
  			float dy0 = - texelSize.y * shadowRadius;
  			float dx1 = + texelSize.x * shadowRadius;
  			float dy1 = + texelSize.y * shadowRadius;
  			float dx2 = dx0 / 2.0;
  			float dy2 = dy0 / 2.0;
  			float dx3 = dx1 / 2.0;
  			float dy3 = dy1 / 2.0;
  
  			shadow = (
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )
  			) * ( 1.0 / 17.0 );
  
  		#elif defined( SHADOWMAP_TYPE_PCF_SOFT )
  
  			vec2 texelSize = vec2( 1.0 ) / shadowMapSize;
  			float dx = texelSize.x;
  			float dy = texelSize.y;
  
  			vec2 uv = shadowCoord.xy;
  			vec2 f = fract( uv * shadowMapSize + 0.5 );
  			uv -= f * texelSize;
  
  			shadow = (
  				texture2DCompare( shadowMap, uv, shadowCoord.z ) +
  				texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +
  				texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +
  				mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), 
  					 texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),
  					 f.x ) +
  				mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), 
  					 texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),
  					 f.x ) +
  				mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), 
  					 texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),
  					 f.y ) +
  				mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), 
  					 texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),
  					 f.y ) +
  				mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), 
  						  texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),
  						  f.x ),
  					 mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), 
  						  texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),
  						  f.x ),
  					 f.y )
  			) * ( 1.0 / 9.0 );
  
  		#elif defined( SHADOWMAP_TYPE_VSM )
  
  			shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );
  
  		#else // no percentage-closer filtering:
  
  			shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );
  
  		#endif
  
  		}
  
  		return shadow;
  
  	}
  
  	// cubeToUV() maps a 3D direction vector suitable for cube texture mapping to a 2D
  	// vector suitable for 2D texture mapping. This code uses the following layout for the
  	// 2D texture:
  	//
  	// xzXZ
  	//  y Y
  	//
  	// Y - Positive y direction
  	// y - Negative y direction
  	// X - Positive x direction
  	// x - Negative x direction
  	// Z - Positive z direction
  	// z - Negative z direction
  	//
  	// Source and test bed:
  	// https://gist.github.com/tschw/da10c43c467ce8afd0c4
  
  	vec2 cubeToUV( vec3 v, float texelSizeY ) {
  
  		// Number of texels to avoid at the edge of each square
  
  		vec3 absV = abs( v );
  
  		// Intersect unit cube
  
  		float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );
  		absV *= scaleToCube;
  
  		// Apply scale to avoid seams
  
  		// two texels less per square (one texel will do for NEAREST)
  		v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );
  
  		// Unwrap
  
  		// space: -1 ... 1 range for each square
  		//
  		// #X##		dim    := ( 4 , 2 )
  		//  # #		center := ( 1 , 1 )
  
  		vec2 planar = v.xy;
  
  		float almostATexel = 1.5 * texelSizeY;
  		float almostOne = 1.0 - almostATexel;
  
  		if ( absV.z >= almostOne ) {
  
  			if ( v.z > 0.0 )
  				planar.x = 4.0 - v.x;
  
  		} else if ( absV.x >= almostOne ) {
  
  			float signX = sign( v.x );
  			planar.x = v.z * signX + 2.0 * signX;
  
  		} else if ( absV.y >= almostOne ) {
  
  			float signY = sign( v.y );
  			planar.x = v.x + 2.0 * signY + 2.0;
  			planar.y = v.z * signY - 2.0;
  
  		}
  
  		// Transform to UV space
  
  		// scale := 0.5 / dim
  		// translate := ( center + 0.5 ) / dim
  		return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );
  
  	}
  
  	float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {
  
  		vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );
  
  		// for point lights, the uniform @vShadowCoord is re-purposed to hold
  		// the vector from the light to the world-space position of the fragment.
  		vec3 lightToPosition = shadowCoord.xyz;
  
  		// dp = normalized distance from light to fragment position
  		float dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); // need to clamp?
  		dp += shadowBias;
  
  		// bd3D = base direction 3D
  		vec3 bd3D = normalize( lightToPosition );
  
  		#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )
  
  			vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;
  
  			return (
  				texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +
  				texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +
  				texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +
  				texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +
  				texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +
  				texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +
  				texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +
  				texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +
  				texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )
  			) * ( 1.0 / 9.0 );
  
  		#else // no percentage-closer filtering
  
  			return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );
  
  		#endif
  
  	}
  
  #endif
  `;
  