[Done] MultiLUT GLSL Shader

I was wondering if we could get a retroarch port of Reshade’s multiLUT.fx, preferably to GLSL.

Also is there currently a shader that will run multiple LUTs in a single pass shader?

I’m not sure I understand why you would want to do such a thing? Is it just to be able to cycle through different LUTs at runtime? If so, I think there are much easier and less error-prone ways to handle that than cramming all of them into a single image in photoshop.

1 Like

Yeah I was just wanting to be able to “hot-swap” LUTs at runtime.

The following preset and shader will let load up to 8 LUTs and cycle through them via a parameter:

shaders = 1

shader0 = shaders/LUT/LUT.glsl

textures = "lut1;lut2;lut3;lut4;lut5;lut6;lut7;lut8"
lut1 = shaders/LUT/16.png
lut1 = true
lut2 = shaders/LUT/16.png
lut2 = true
lut3 = shaders/LUT/16.png
lut3 = true
lut4 = shaders/LUT/16.png
lut4 = true
lut5 = shaders/LUT/16.png
lut5 = true
lut6 = shaders/LUT/16.png
lut6 = true
lut7 = shaders/LUT/16.png
lut7 = true
lut8 = shaders/LUT/16.png
lut8 = true

and the shader:

#pragma parameter LUT_Size "LUT Size" 16.0 1.0 64.0 1.0
#pragma parameter LUT_Selector "LUT Selection" 1.0 1.0 8.0 1.0

#if defined(VERTEX)

#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying 
#define COMPAT_ATTRIBUTE attribute 
#define COMPAT_TEXTURE texture2D
#endif

#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif

COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;

vec4 _oPosition1; 
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;

// compatibility #defines
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)

void main()
{
    gl_Position = MVPMatrix * VertexCoord;
    TEX0.xy = TexCoord.xy;
}

#elif defined(FRAGMENT)

#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif

#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif

uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
uniform sampler2D lut1;
uniform sampler2D lut2;
uniform sampler2D lut3;
uniform sampler2D lut4;
uniform sampler2D lut5;
uniform sampler2D lut6;
uniform sampler2D lut7;
uniform sampler2D lut8;
COMPAT_VARYING vec4 TEX0;

// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy

#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)

#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float LUT_Size, LUT_Selector;
#else
#define LUT_Size 16.0
#define LUT_Selector 1.0
#endif

// This shouldn't be necessary but it seems some undefined values can
// creep in and each GPU vendor handles that differently. This keeps
// all values within a safe range
vec4 mixfix(vec4 a, vec4 b, float c)
{
	return (a.z < 1.0) ? mix(a, b, c) : a;
}

void main()
{
   
	vec4 imgColor = COMPAT_TEXTURE(Source, vTexCoord.xy);
	float red = ( imgColor.r * (LUT_Size - 1.0) + 0.4999 ) / (LUT_Size * LUT_Size);
	float green = ( imgColor.g * (LUT_Size - 1.0) + 0.4999 ) / LUT_Size;
	float blue1 = (floor( imgColor.b  * (LUT_Size - 1.0) ) / LUT_Size) + red;
	float blue2 = (ceil( imgColor.b  * (LUT_Size - 1.0) ) / LUT_Size) + red;
	float mixer = clamp(max((imgColor.b - blue1) / (blue2 - blue1), 0.0), 0.0, 32.0);
	vec4 color1, color2;
   int LUT = int(floor(LUT_Selector + 0.5));
   if(LUT == 1)
   {
      color1 = COMPAT_TEXTURE( lut1, vec2( blue1, green ));
      color2 = COMPAT_TEXTURE( lut1, vec2( blue2, green ));
   }
   else if(LUT == 2)
   {
      color1 = COMPAT_TEXTURE( lut2, vec2( blue1, green ));
      color2 = COMPAT_TEXTURE( lut2, vec2( blue2, green ));
   }
   else if(LUT == 3)
   {
      color1 = COMPAT_TEXTURE( lut3, vec2( blue1, green ));
      color2 = COMPAT_TEXTURE( lut3, vec2( blue2, green ));
   }
   else if(LUT == 4)
   {
      color1 = COMPAT_TEXTURE( lut4, vec2( blue1, green ));
      color2 = COMPAT_TEXTURE( lut4, vec2( blue2, green ));
   }
   else if(LUT == 5)
   {
      color1 = COMPAT_TEXTURE( lut5, vec2( blue1, green ));
      color2 = COMPAT_TEXTURE( lut5, vec2( blue2, green ));
   }
   else if(LUT == 6)
   {
      color1 = COMPAT_TEXTURE( lut6, vec2( blue1, green ));
      color2 = COMPAT_TEXTURE( lut6, vec2( blue2, green ));
   }
   else if(LUT == 7)
   {
      color1 = COMPAT_TEXTURE( lut7, vec2( blue1, green ));
      color2 = COMPAT_TEXTURE( lut7, vec2( blue2, green ));
   }
   else if(LUT == 8)
   {
      color1 = COMPAT_TEXTURE( lut8, vec2( blue1, green ));
      color2 = COMPAT_TEXTURE( lut8, vec2( blue2, green ));
   }

	FragColor = mixfix(color1, color2, mixer);//mix(color1, color2, mixer);
} 
#endif
2 Likes

Thanks, really appreciate you taking the time to do this.

np, hope it gets you fixed up :slight_smile:

2 Likes

This is looks exactly (coding) like what I wanted. Haven’t gotten a chance to test yet, but the code looks right, being able to switch between LUTs from the shader parameters menu.

My idea is to us this with Lottes mask’s and some different LUT profiles to go with the masks, like loading a Sony LUT with the Apeture Grille mask, etc.

Edit: I was able to test the shader today and it works great!