Saltallica

Need help with Color grading / Color LUT shader with Pixi

Recommended Posts

Hi there - I'm going down a rabbit hole trying to implement a color grading / LUT shader for PIXI.  Color grading is where you can you use a sprite as a lookup table to quickly transform one set of colors to another - this is handy for applying realtime contrast and color adjustments. I'm using this as a reference:

https://www.defold.com/tutorials/grading/ 

I've created a filter/shader using the code in the link above:

 

var ColorGradingShader = function(LUTSprite) {

  var my = this;
  
  var code = `
  precision lowp float;
  uniform vec4 filterArea;

  varying vec2 vTextureCoord;
  uniform sampler2D uSampler;
  uniform sampler2D lut;

  #define MAXCOLOR 15.0
  #define COLORS 16.0
  #define WIDTH 256.0
  #define HEIGHT 16.0
 

void main() {

    vec4 px = texture2D(uSampler, vTextureCoord.xy);

    float cell = px.b * MAXCOLOR;

    float cell_l = floor(cell); 
    float cell_h = ceil(cell);
    
    float half_px_x = 0.5 / WIDTH;
    float half_px_y = 0.5 / HEIGHT;
    float r_offset = half_px_x + px.r / COLORS * (MAXCOLOR / COLORS);
    float g_offset = half_px_y + px.g * (MAXCOLOR / COLORS);
    
    vec2 lut_pos_l = vec2(cell_l / COLORS + r_offset, g_offset); 
    vec2 lut_pos_h = vec2(cell_h / COLORS + r_offset, g_offset);

    vec4 graded_color_l = texture2D(lut, lut_pos_l); 
    vec4 graded_color_h = texture2D(lut, lut_pos_h);

    
    vec4 graded_color = mix(graded_color_l, graded_color_h, fract(cell));

    gl_FragColor = graded_color;

}

`;
  
  PIXI.Filter.call(my, null, code);    

  my.uniforms.lut = LUTSprite.texture;
    
}

ColorGradingShader.prototype = Object.create(PIXI.Filter.prototype);
ColorGradingShader.prototype.constructor = ColorGradingShader;
  
export default ColorGradingShader;

 

I then add this to my top level container:

//relevant code from a wrapping class

this.colorGradingSprite = new PIXI.Sprite.fromImage('/img/lut16.png');

this.pixiContainer.filters = [
	this.colorGradingFilter
];

 

When using any LUT image, including the default without any color adjustments:

lut16.png.cc3d04f5f2c7df98c6013ebb8da3d4a2.png

 

 I go from this:

image.thumb.png.1a282aa03ab562d1ee6d391e78095d07.png

 

to this:

 

image.thumb.png.bbc93fe2eab2a6efd0c177fce3b9f32b.png

 

I'm assuming there are some adjustments necessary to either the shader code, or how the lut sprite itself is being loaded - I have no clue..

Any help would be greatly appreciated!

 

And for those curious, here's my end goal:

image.thumb.png.af046049a41ae4f48dfa76db4432ee78.png

 

Thanks,

Sean

Share this post


Link to post
Share on other sites

Thanks for reviewing, what more can I provide? 

Using Pixi 4.6.2...

Running under latest Chrome...

here's the vertex and fragments shaders as passed to gl:

 

precision lowp float;
  uniform vec4 filterArea;

  varying vec2 vTextureCoord;
  uniform sampler2D uSampler;
  uniform sampler2D lut;

  #define MAXCOLOR 15.0
  #define COLORS 16.0
  #define WIDTH 256.0
  #define HEIGHT 16.0
 

void main() {

    vec4 px = texture2D(uSampler, vTextureCoord.xy);

    float cell = px.b * MAXCOLOR;

    float cell_l = floor(cell); 
    float cell_h = ceil(cell);
    
    float half_px_x = 0.5 / WIDTH;
    float half_px_y = 0.5 / HEIGHT;
    float r_offset = half_px_x + px.r / COLORS * (MAXCOLOR / COLORS);
    float g_offset = half_px_y + px.g * (MAXCOLOR / COLORS);
    
    vec2 lut_pos_l = vec2(cell_l / COLORS + r_offset, g_offset); 
    vec2 lut_pos_h = vec2(cell_h / COLORS + r_offset, g_offset);

    vec4 graded_color_l = texture2D(lut, lut_pos_l); 
    vec4 graded_color_h = texture2D(lut, lut_pos_h);

    
    vec4 graded_color = mix(graded_color_l, graded_color_h, fract(cell));

    gl_FragColor = graded_color;

}
 precision highp float;
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat3 projectionMatrix;
uniform mat3 filterMatrix;
varying vec2 vTextureCoord;
varying vec2 vFilterCoord;
void main(void){
   gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
   vFilterCoord = ( filterMatrix * vec3( aTextureCoord, 1.0)  ).xy;
   vTextureCoord = aTextureCoord ;
}

Anything else you are looking for?

Share this post


Link to post
Share on other sites

:: giant thumbs up emoji ::

 Didn't consider mipmapping causing issues :D

I do notice the that the LUT sprite itself, when the filter is applied, looks like it's flipping upside down.

So that leads me to think it could be one of two things (maybe?)

  • The math for the shader needs be adjusted to flip the Y axis
  • Flipping the LUT image PNG upside down might work?

Thanks for looking in to this!

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.