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'
)