function getPickMetadataShaderProgram(
  context,
  shaderProgram,
  pickedMetadataInfo,
) {
  const schemaId = pickedMetadataInfo.schemaId;
  const className = pickedMetadataInfo.className;
  const propertyName = pickedMetadataInfo.propertyName;
  const keyword = `pickMetadata-${schemaId}-${className}-${propertyName}`;
  const shader = context.shaderCache.getDerivedShaderProgram(
    shaderProgram,
    keyword,
  );
  if (defined(shader)) {
    return shader;
  }

  const metadataProperty = pickedMetadataInfo.metadataProperty;
  const classProperty = pickedMetadataInfo.classProperty;
  const glslType = getGlslType(classProperty);

  // Define the components that will go into the output `metadataValues`.
  // This will be the 'color' that is written into the frame buffer,
  // meaning that the values should be in [0.0, 1.0], and will become
  // values in [0, 255] in the frame buffer.
  // By default, all of them are 0.0.
  const sourceValueStrings = ["0.0", "0.0", "0.0", "0.0"];
  const componentCount = getComponentCount(classProperty);
  if (componentCount === 1) {
    // When the property is a scalar, store the source value
    // string directly in `metadataValues.x`
    sourceValueStrings[0] = getSourceValueStringScalar(
      classProperty,
      metadataProperty,
    );
  } else {
    // When the property is an array, store the array elements
    // in `metadataValues.x/y/z/w`
    const componentNames = ["x", "y", "z", "w"];
    for (let i = 0; i < componentCount; i++) {
      sourceValueStrings[i] = getSourceValueStringComponent(
        classProperty,
        metadataProperty,
        componentNames[i],
      );
    }
  }

  const newDefines = shaderProgram.fragmentShaderSource.defines.slice();
  newDefines.push(MetadataPickingPipelineStage.METADATA_PICKING_ENABLED);

  // Replace the defines of the shader, using the type, property
  // access, and value components  that have been determined
  replaceDefine(
    newDefines,
    MetadataPickingPipelineStage.METADATA_PICKING_VALUE_TYPE,
    glslType,
  );
  replaceDefine(
    newDefines,
    MetadataPickingPipelineStage.METADATA_PICKING_VALUE_STRING,
    `metadata.${propertyName}`,
  );
  replaceDefine(
    newDefines,
    MetadataPickingPipelineStage.METADATA_PICKING_VALUE_COMPONENT_X,
    sourceValueStrings[0],
  );
  replaceDefine(
    newDefines,
    MetadataPickingPipelineStage.METADATA_PICKING_VALUE_COMPONENT_Y,
    sourceValueStrings[1],
  );
  replaceDefine(
    newDefines,
    MetadataPickingPipelineStage.METADATA_PICKING_VALUE_COMPONENT_Z,
    sourceValueStrings[2],
  );
  replaceDefine(
    newDefines,
    MetadataPickingPipelineStage.METADATA_PICKING_VALUE_COMPONENT_W,
    sourceValueStrings[3],
  );

  const newFragmentShaderSource = new ShaderSource({
    sources: shaderProgram.fragmentShaderSource.sources,
    defines: newDefines,
  });
  const newShader = context.shaderCache.createDerivedShaderProgram(
    shaderProgram,
    keyword,
    {
      vertexShaderSource: shaderProgram.vertexShaderSource,
      fragmentShaderSource: newFragmentShaderSource,
      attributeLocations: shaderProgram._attributeLocations,
    },
  );
  return newShader;
}
Creates a new `ShaderProgram` from the given input that renders metadata
values into the frame buffer, according to the given picked metadata info.

This will update the `defines` of the fragment shader of the given shader
program, by setting `METADATA_PICKING_ENABLED`, and updating the
`METADATA_PICKING_VALUE_*` defines so that they reflect the components
of the metadata that should be written into the RGBA (vec4) that
ends up as the 'color' in the frame buffer.

The RGBA values will eventually be converted back into an actual metadata
value in `Picking.js`, by calling `MetadataPicking.decodeMetadataValues`.

@param {Context} context The context
@param {ShaderProgram} shaderProgram The shader program
@param {PickedMetadataInfo} pickedMetadataInfo The picked metadata info
@returns {ShaderProgram} The new shader program
@private