LYGIA Shader Library

lut (lygia/color/lut)

Use LUT textures to modify colors (vec4 and vec3) or a position in a gradient (vec2 and floats)

Dependencies:

Use:

lut(<SAMPLER_TYPE> texture, <vec4|vec3|vec2|float> value [, int row])

Check it on Github



#ifndef FNC_LUT
#define FNC_LUT

#ifdef LUT_SQUARE 

#ifdef LUT_FLIP_Y
#define SAMPLE2DCUBE_FLIP_Y
#endif

#ifndef SAMPLE_2DCUBE_CELLS_PER_SIDE
#ifdef LUT_CELLS_PER_SIDE
#define SAMPLE2DCUBE_CELLS_PER_SIDE LUT_CELLS_PER_SIDE
#else
#define SAMPLE2DCUBE_CELLS_PER_SIDE 8.0
#endif
#endif

vec4 lut(in SAMPLER_TYPE tex_lut, in vec4 color, in int offset) { 
    return sample2DCube(tex_lut, color.rgb); 
}

#else

#ifndef LUT_N_ROWS
#define LUT_N_ROWS 1
#endif

#ifndef LUT_CELL_SIZE
#define LUT_CELL_SIZE 32.0
#endif

#ifndef LUT_CELLS_PER_SIDE
#define LUT_CELLS_PER_SIDE 8.0
#endif


// Data about how the LUTs rows are encoded
const float LUT_WIDTH = LUT_CELL_SIZE*LUT_CELL_SIZE;
const float LUT_OFFSET = 1./ float( LUT_N_ROWS );
const vec4 LUT_SIZE = vec4(LUT_WIDTH, LUT_CELL_SIZE, 1./LUT_WIDTH, 1./LUT_CELL_SIZE);

// Apply LUT to a COLOR
// ------------------------------------------------------------
vec4 lut(in SAMPLER_TYPE tex_lut, in vec4 color, in int offset) {
    vec3 scaledColor = clamp(color.rgb, vec3(0.), vec3(1.)) * (LUT_SIZE.y - 1.);
    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 = (.5 + scaledColor.xy) * LUT_SIZE.zw;

    // offset by the blue slice
    texc.x += (scaledColor.z - bFrac) * LUT_SIZE.w;
    texc.y *= LUT_OFFSET;
    texc.y += float(offset) * LUT_OFFSET;
    #ifndef LUT_FLIP_Y
    texc.y = 1. - texc.y; 
    #endif

    // sample the 2 adjacent blue slices
    vec4 b0 = SAMPLER_FNC(tex_lut, texc);
    vec4 b1 = SAMPLER_FNC(tex_lut, vec2(texc.x + LUT_SIZE.w, texc.y));

    // blend between the 2 adjacent blue slices
    color = mix(b0, b1, bFrac);

    return color;
}
#endif

vec4 lut(in SAMPLER_TYPE tex_lut, in vec4 color) { return lut(tex_lut, color, 0); }
vec3 lut(in SAMPLER_TYPE tex_lut, in vec3 color, in int offset) { return lut(tex_lut, vec4(color, 1.), offset).rgb; }
vec3 lut(in SAMPLER_TYPE tex_lut, in vec3 color) { return lut(tex_lut, color, 0).rgb; }

#endif

Dependencies:

Use:

lut(<SAMPLER_TYPE> texture, <float4|float3|float2|float> value [, int row])

Check it on Github



#ifndef FNC_LUT
#define FNC_LUT

#ifdef LUT_SQUARE 

#ifdef LUT_FLIP_Y
#define SAMPLE_2DCUBE_FLIP_Y
#endif

#ifndef SAMPLE_2DCUBE_CELLS_PER_SIDE
#ifdef LUT_CELLS_PER_SIDE
#define SAMPLE_2DCUBE_CELLS_PER_SIDE LUT_CELLS_PER_SIDE
#else
#define SAMPLE_2DCUBE_CELLS_PER_SIDE 8.0
#endif
#endif

float4 lut(in SAMPLER_TYPE tex, in float4 color, in int offset) { 
    return sample2DCube(tex, color.rgb); 
}

#else
// Data about how the LUTs rows are encoded
static const float LUT_WIDTH = LUT_CELL_SIZE*LUT_CELL_SIZE;
static const float LUT_OFFSET = 1./ float( LUT_N_ROWS);
static const float4 LUT_SIZE = float4(LUT_WIDTH, LUT_CELL_SIZE, 1./LUT_WIDTH, 1./LUT_CELL_SIZE);

// Apply LUT to a COLOR
// ------------------------------------------------------------
float4 lut(in SAMPLER_TYPE tex, in float4 color, in int offset) {
    float3 scaledColor = clamp(color.rgb, float3(0., 0., 0.), float3(1., 1., 1.)) * (LUT_SIZE.y - 1.);
    float bFrac = frac(scaledColor.z);

    // offset by 0.5 pixel and fit within range [0.5, width-0.5]
    // to prevent bilinear filtering with adjacent colors
    float2 texc = (.5 + scaledColor.xy) * LUT_SIZE.zw;

    // offset by the blue slice
    texc.x += (scaledColor.z - bFrac) * LUT_SIZE.w;
    texc.y *= LUT_OFFSET;
    texc.y += float(offset) * LUT_OFFSET;
    #ifndef LUT_FLIP_Y
    texc.y = 1. - texc.y; 
    #endif

    // sample the 2 adjacent blue slices
    float4 b0 = SAMPLER_FNC(tex, texc);
    float4 b1 = SAMPLER_FNC(tex, float2(texc.x + LUT_SIZE.w, texc.y));

    // blend between the 2 adjacent blue slices
    color = lerp(b0, b1, bFrac);

    return color;
}
#endif

float4 lut(in SAMPLER_TYPE tex, in float4 color) { return lut(tex, color, 0); }
float3 lut(in SAMPLER_TYPE tex, in float3 color, in int offset) { return lut(tex, float4(color, 1.), offset).rgb; }
float3 lut(in SAMPLER_TYPE tex, in float3 color) { return lut(tex, color, 0).rgb; }

#endif

Dependencies:

Use:

lut(<SAMPLER_TYPE> texture, <float4|float3|float2|float> value [, int row])

Check it on Github



#ifndef FNC_LUT
#define FNC_LUT

#ifdef LUT_SQUARE 

#ifdef LUT_FLIP_Y
#define SAMPLE2DCUBE_FLIP_Y
#endif

#ifndef SAMPLE_2DCUBE_CELLS_PER_SIDE
#ifdef LUT_CELLS_PER_SIDE
#define SAMPLE2DCUBE_CELLS_PER_SIDE LUT_CELLS_PER_SIDE
#else
#define SAMPLE2DCUBE_CELLS_PER_SIDE 8.0
#endif
#endif

float4 lut(SAMPLER_TYPE tex_lut, float4 color, int offset) { 
    return sample2DCube(tex_lut, color.rgb); 
}

#else

#ifndef LUT_N_ROWS
#define LUT_N_ROWS 1
#endif

#ifndef LUT_CELL_SIZE
#define LUT_CELL_SIZE 32.0
#endif

#ifndef LUT_CELLS_PER_SIDE
#define LUT_CELLS_PER_SIDE 8.0
#endif


// Data about how the LUTs rows are encoded
const float LUT_WIDTH = LUT_CELL_SIZE*LUT_CELL_SIZE;
const float LUT_OFFSET = 1./ float( LUT_N_ROWS );
const float4 LUT_SIZE = float4(LUT_WIDTH, LUT_CELL_SIZE, 1./LUT_WIDTH, 1./LUT_CELL_SIZE);

// Apply LUT to a COLOR
// ------------------------------------------------------------
float4 lut(SAMPLER_TYPE tex_lut, float4 color, int offset) {
    float3 scaledColor = clamp(color.rgb, float3(0.), float3(1.)) * (LUT_SIZE.y - 1.);
    float bFrac = fract(scaledColor.z);

    // offset by 0.5 pixel and fit withrange [0.5, width-0.5]
    // to prevent bilinear filtering with adjacent colors
    float2 texc = (.5 + scaledColor.xy) * LUT_SIZE.zw;

    // offset by the blue slice
    texc.x += (scaledColor.z - bFrac) * LUT_SIZE.w;
    texc.y *= LUT_OFFSET;
    texc.y += float(offset) * LUT_OFFSET;
    #ifndef LUT_FLIP_Y
    texc.y = 1. - texc.y; 
    #endif

    // sample the 2 adjacent blue slices
    float4 b0 = SAMPLER_FNC(tex_lut, texc);
    float4 b1 = SAMPLER_FNC(tex_lut, float2(texc.x + LUT_SIZE.w, texc.y));

    // blend between the 2 adjacent blue slices
    color = mix(b0, b1, bFrac);

    return color;
}
#endif

float4 lut(SAMPLER_TYPE tex_lut, float4 color) { return lut(tex_lut, color, 0); }
float3 lut(SAMPLER_TYPE tex_lut, float3 color, int offset) { return lut(tex_lut, float4(color, 1.), offset).rgb; }
float3 lut(SAMPLER_TYPE tex_lut, float3 color) { return lut(tex_lut, color, 0).rgb; }

#endif

Examples

Licenses

LYGIA is dual-licensed under the Prosperity License and the Patron License for sponsors and contributors.

Sponsors and contributors are automatically added to the Patron License and they can ignore the any non-commercial rule of the Prosperity Licensed software (please take a look to the exception).

It's also possible to get a permanent comercial license hook to a single and specific version of LYGIA.

Get the latest news and releases

Sign up for the news letter bellow, joing the LYGIA's channel on Discord or follow the Github repository