precision highp float; #define colorlut_height (32.0) #define colorlut_width (colorlut_height * colorlut_height) varying vec2 v_TexCoords; uniform mat4 u_STMatrix; uniform sampler2D inputImage; uniform sampler2D colorlut; uniform vec2 u_RenderSize; uniform float uLutBlend; // apply a color LUT to the input color vec4 colorLUT(vec4 color, sampler2D lut) { // this should come from a real uniform from the engine // hard-coded here for now const vec4 uLutSize = vec4(colorlut_width, colorlut_height, 1.0/colorlut_width, 1.0/colorlut_height); vec3 scaledColor = color.xyz * (uLutSize.y - 1.0); float bFrac = fract(scaledColor.z); // offset by 0.5 pixel and fit within range [0.5, width-0.5] // to prevent bilinear filtering with adjacent colors vec2 texc = (0.5 + scaledColor.xy) * uLutSize.zw; // offset by the blue slice texc.x += (scaledColor.z - bFrac) * uLutSize.w; // sample the 2 adjacent blue slices vec3 b0 = texture2D(lut, texc).xyz; vec3 b1 = texture2D(lut, vec2(texc.x + uLutSize.w, texc.y)).xyz; // blend between the 2 adjacent blue slices color.xyz = mix(b0, b1, bFrac); return color; } void main() { vec2 uv = (u_STMatrix * vec4(v_TexCoords.x, v_TexCoords.y, 0.0, 1.0)).xy; vec4 base = texture2D(inputImage, uv); vec4 luttedColor = colorLUT(base, colorlut); vec3 finalColor = mix(base.rgb, luttedColor.rgb, uLutBlend); gl_FragColor = vec4(finalColor, 1.0); }