Color Correction
Demonstration of Color Correction Implementation in PIXI WebGL. Also known as Gradient Mapping
by adil_invideo
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.3.3/pixi.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/pixi-filters.js"></script>
<div class="header">
<h2>Color Correction</h2>
<div class="checkbox-wrapper">
<label class="switch">
<input type="checkbox" id="toggle" checked>
<span class="slider round"></span>
</label>
<span>Gradient map</span>
</div>
</div>
<div class="content">
<div class="row">
<div class="cell"><span class="title">Duotone</span></div>
<div class="cell"><span class="title">Duotone2</span></div>
</div>
<div class="row">
<div class="cell"><span class="title">Tritone</span></div>
<div class="cell"><span class="title">Tritone3</span></div>
</div>
</div>
SCSS
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
display: flex;
flex-direction: column;
margin: 0;
}
#canvas {
border: 1px solid black;
width: 200px;
height: 300px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px;
.checkbox-wrapper {
display: flex;
align-items: center;
span {
font-weight: 500;
font-size: 20px;
}
}
}
.content {
display: flex;
flex-direction: column;
width: 100vw;
height: calc(100vh - 100px);
border-top: 1px solid black;
.row {
display: flex;
flex: 1;
.cell {
border-right: 1px solid black;
border-bottom: 1px solid black;
flex: 1;
position: relative;
.title {
position: absolute;
left: 8px;
bottom: 8px;
font-size: 35px;
font-weight: 600;
color: #000;
z-index: 1;
}
.canvas {
top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
background: #F0F0F0;
border-radius: 20px;
}
}
}
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
margin-right: 10px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked+.slider {
background-color: #2196F3;
}
input:focus+.slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked+.slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius:...
JavaScript
/*
References:
https://forum.openframeworks.cc/t/gradient-map-on-image/16312/4
https://forum.openframeworks.cc/t/gradient-map-on-image/16312/4
https://halisavakis.com/my-take-on-shaders-gradient-mapper/
*/
const cells = document.getElementsByClassName("cell");
const placeholder = [];
/* ****************** DUOTONE***************************** */
const duotoneFrag = `
varying highp vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform lowp vec4 color1;
uniform lowp vec4 color2;
const mat4 greyScaleMatrix = mat4(
0.2126, 0.7152, 0.0722, 0,
0.2126, 0.7152, 0.0722, 0,
0.2126, 0.7152, 0.0722, 0,
0, 0, 0, 1
);
void main()
{
lowp vec4 textureColor = texture2D(uSampler, vTextureCoord);
lowp vec4 greyScaleTexture = textureColor * greyScaleMatrix; // grey scale
float grayscaleValue = dot(textureColor.rgb, vec3(0.2126, 0.7152, 0.0722));
lowp vec4 mixColor = mix(
color1,
color2,
grayscaleValue);
gl_FragColor = mixColor * greyScaleTexture;
}
`;
// 5, 21, 39
// 248, 228, 120
class DuotoneFilter extends PIXI.Filter {
constructor(color1, color2) {
const uniforms = {
color1: [color1[0] / 255.0, color1[1] / 255.0, color1[2] / 255.0, 1.0],
color2: [color2[0] / 255.0, color2[1] / 255.0, color2[2] / 255.0, 1.0],
};
super(null, duotoneFrag, uniforms);
}
}
/* ****************** TRITONE****************************** */
const tritoneFrag = `
varying highp vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
void main()
{
lowp vec4 textureColor = texture2D(uSampler, vTextureCoord);
textureColor.rgb /= textureColor.a;
float grayscaleValue = dot(textureColor.rgb, vec3(0.299, 0.587, 0.114));
vec3 map = vec3(0);
if(grayscaleValue < 0.5) {
float t = grayscaleValue / 0.5;
map.rgb = mix(color1, color2, t);
}else{
float t = (grayscaleValue - 0.5) /...