text: export default /* glsl */`
  vec3 packNormalToRGB( const in vec3 normal ) {
  	return normalize( normal ) * 0.5 + 0.5;
  }
  
  vec3 unpackRGBToNormal( const in vec3 rgb ) {
  	return 2.0 * rgb.xyz - 1.0;
  }
  
  const float PackUpscale = 256. / 255.; // fraction -> 0..1 (including 1)
  const float UnpackDownscale = 255. / 256.; // 0..1 -> fraction (excluding 1)
  
  const vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );
  const vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );
  
  const float ShiftRight8 = 1. / 256.;
  
  vec4 packDepthToRGBA( const in float v ) {
  	vec4 r = vec4( fract( v * PackFactors ), v );
  	r.yzw -= r.xyz * ShiftRight8; // tidy overflow
  	return r * PackUpscale;
  }
  
  float unpackRGBAToDepth( const in vec4 v ) {
  	return dot( v, UnpackFactors );
  }
  
  vec4 pack2HalfToRGBA( vec2 v ) {
  	vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));
  	return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);
  }
  vec2 unpackRGBATo2Half( vec4 v ) {
  	return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );
  }
  
  // NOTE: viewZ/eyeZ is < 0 when in front of the camera per OpenGL conventions
  
  float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {
  	return ( viewZ + near ) / ( near - far );
  }
  float orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {
  	return linearClipZ * ( near - far ) - near;
  }
  
  // NOTE: https://twitter.com/gonnavis/status/1377183786949959682
  
  float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {
  	return (( near + viewZ ) * far ) / (( far - near ) * viewZ );
  }
  float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {
  	return ( near * far ) / ( ( far - near ) * invClipZ - far );
  }
  `;
  