A collection of tools for Foundry Mari that make it easy to produce CG textures. Here you are going to find:

If you want to know more, please consider vising the repository.

1. Adjustment Shaders

Layers that provide adjustment to other layers.

1.1. Normalize

This first example, shows a pretty simple adjustment layer that normalizes the colors of the stack below.

<!-- MariNormalize.xml -->
<Node>
  <ID>customNormalizeAdjustment</ID>
  <Tags>
    <Tag>_adjustment</Tag>
  </Tags>
  <Inputs>
    <Input Name="Input"></Input>
  </Inputs>
  <Attributes>
    <Attribute></Attribute>
  </Attributes>
  <Contexts>
    <Context Type="GLSL">
      <Shader>
        <Inputs>
          <Input name="Input"><Default>vec4(1, 1, 1, 1)</Default></Input>
        </Inputs>
        <Body><![CDATA[
          Output.rgb = normalize(#Input.rgb);
          Output.a = #Input.a;
        ]]></Body>
      </Shader>
    </Context>
  </Contexts>
</Node>

To register this shader, just include the following piece of code in the start-up script called init.py.

import mari

mari.gl_render.registerCustomAdjustmentLayerFromXMLFile(
  'Custom-Shaders/Normalize',
  'path/to/adjustment/MariNormalize.xml'
)

1.2. Saturation

For this second example, we are going to declare custom functions to be used in the shader.

Firstly, we have to declare the header file with the extension .glslh.

// adjustmentUtils.glslh

// ! Utils
float avrValue(vec3 input);
float avrValue(vec4 input);
float maxValue(vec3 input);
float maxValue(vec4 input);
float minValue(vec3 input);
float minValue(vec4 input);

// ! Saturation functions
float ccir601Value(vec3 input);
float ccir601Value(vec4 input);
float rec709Value(vec3 input);
float rec709Value(vec4 input);
float rec2020Value(vec3 input);
float rec2020Value(vec4 input);

The next step is to declare the function code file with the extension .glslc.

// adjustmentUtils.glslc

#version 330

// ! include | adjUtils.glslh

// ! Utils

float avrValue(vec3 input)
{
  return (input.r + input.g + input.b) / 3;
}

float avrValue(vec4 input)
{
  return (input.r + input.g + input.b) / 3;
}

float maxValue(vec3 input)
{
  return max(max(input.r, input.g), input.b);
}

float maxValue(vec4 input)
{
  return max(max(input.r, input.g), input.b);
}

float minValue(vec3 input)
{
  return min(min(input.r, input.g), input.b);
}

float minValue(vec4 input)
{
  return min(min(input.r, input.g), input.b);
}

// ! Saturation functions

float ccir601Value(vec3 input)
{
  float kb = 0.114;
  float kr = 0.299;
  float kg = 1 - kr - kb;
  return kr * input.r + kg * input.g + kb * input.b;
}

float ccir601Value(vec4 input)
{
  float kb = 0.114;
  float kr = 0.299;
  float kg = 1 - kr - kb;
  return kr * input.r + kg * input.g + kb * input.b;
}

float rec709Value(vec3 input)
{
  float kb = 0.0722;
  float kr = 0.2126;
  float kg = 1 - kr - kb;
  return kr * input.r + kg * input.g + kb * input.b;
}

float rec709Value(vec4 input)
{
  float kb = 0.0722;
  float kr = 0.2126;
  float kg = 1 - kr - kb;
  return kr * input.r + kg * input.g + kb * input.b;
}

float rec2020Value(vec3 input)
{
  float kb = 0.0593;
  float kr = 0.2627;
  float kg = 1 - kr - kb;
  return kr * input.r + kg * input.g + kb * input.b;
}

float rec2020Value(vec4 input)
{
  float kb = 0.0593;
  float kr = 0.2627;
  float kg = 1 - kr - kb;
  return kr * input.r + kg * input.g + kb * input.b;
}

Now we can finally write the shader and use the custom functions we declared.

<!-- MariSaturation.xml -->
<Node>
  <ID>customSaturationAdjustment</ID>
  <Tags>
    <Tag>_adjustment</Tag>
  </Tags>
  <Inputs>
    <Input Name="Input"></Input>
  </Inputs>
  <Attributes>
    <Attribute Name="type" PrettyName="luminance type" Type="stringlist">maximum,average,maximum,minimum,ccir601,rec709,rec2020</Attribute>
    <Attribute Name="saturation" Type="double" Min="0" Max="4">1</Attribute>
    <Attribute Name="absolute" PrettyName="absolute value" Type="bool">false</Attribute>
  </Attributes>
  <Contexts>
    <Context Type="GLSL">
      <Shader>
        <Inputs>
          <Input name="Input"><Default>vec4(1, 1, 1, 1)</Default></Input>
        </Inputs>
        <Body><![CDATA[
          float value;
          switch($type){
            case 0:
              value = avrValue(#Input);
              break;
            case 1:
              value = maxValue(#Input);
              break;
            case 2:
              value = minValue(#Input);
              break;
            case 3:
              value = ccir601Value(#Input);
              break;
            case 4:
              value = rec709Value(#Input);
              break;
            case 5:
              value = rec2020Value(#Input);
              break;
            default:
              Output.rgb = vec3(1);
          }
          Output.r = #Input.r*$saturation + value*(1 - $saturation);
          Output.g = #Input.g*$saturation + value*(1 - $saturation);
          Output.b = #Input.b*$saturation + value*(1 - $saturation);
          Output.a = 1;
          if($absolute){
            Output = abs(Output);
          }
        ]]></Body>
      </Shader>
    </Context>
  </Contexts>
</Node>

To register this shader, just include the following piece of code in the start-up script called init.py.

import mari

mari.gl_render.registerCustomHeaderFile(
  'ADJUSTMENT_UTILS_GLSLH',
  'path/to/adjustment/lib/adjustmentUtils.glslh'
)
mari.gl_render.registerCustomCodeFile(
  'ADJUSTMENT_UTILS_GLSLC',
  'path/to/adjustment/lib/adjustmentUtils.glslc'
)

mari.gl_render.registerCustomAdjustmentLayerFromXMLFile(
  'Custom-Shaders/Saturation',
  'path/to/adjustment/MariSaturation.xml'
)