# 什么是LUT滤镜

LUT全称LookUpTable，也称为颜色查找表，它代表的是一种映射关系，通过LUT可以将输入的像素数组通过映射关系转换输出成另外的像素数组。

R2 = LUT(R1)
G2 = LUT(G1)
B2 = LUT(B1)

# 如何在Opengl中使用LUT滤镜

#version 300 es
precision mediump float;
in vec2 TexCoord;
uniform sampler2D ourTexture;
uniform sampler2D textureLUT;
out vec4 FragColor;
vec4 lookupTable(vec4 color){
float blueColor = color.b * 63.0;
//取与 B 分量值最接近的 2 个小方格的坐标
vec2 texPos1;
texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);
texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
vec2 texPos2;
texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);
texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
//取目标映射对应的像素值
vec4 newColor1 = texture(textureLUT, texPos1);
vec4 newColor2 = texture(textureLUT, texPos2);
// 颜色混合
vec4 newColor = mix(newColor1, newColor2, fract(blueColor));
return vec4(newColor.rgb, color.w);
}

void main()
{
// 原始图像的RGBA值
vec4 tmpColor = texture(ourTexture, TexCoord);
// 通过原始图像LUT映射
FragColor = lookupTable(tmpColor);
}

• 获取B分量

• 获取RG分量

(64-1)* textureColor.r 意思是首先将当前实际像素的r值映射到0-63的范围内，再除以512是转化为纹理坐标中实际的点，因为我们的LUT纹理图的分辨率为512*512。

Lut2DOpengl.cpp


#include "Lut2DOpengl.h"
#include "../utils/Log.h"

// 顶点着色器
static const char *ver = "#version 300 es\n"
"in vec4 aPosition;\n"
"in vec2 aTexCoord;\n"
"out vec2 TexCoord;\n"
"void main() {\n"
"  TexCoord = aTexCoord;\n"
"  gl_Position = aPosition;\n"
"}";
// 片元着色器
static const char *fragment = "#version 300 es\n"
"precision mediump float;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D ourTexture;\n"
"uniform sampler2D textureLUT;\n"
"out vec4 FragColor;\n"
"vec4 lookupTable(vec4 color){\n"
"    float blueColor = color.b * 63.0;\n"
"    quad1.y = floor(floor(blueColor) / 8.0);\n"
"    quad2.y = floor(ceil(blueColor) / 8.0);\n"
"    vec2 texPos1;\n"
"    texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\n"
"    texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\n"
"    vec2 texPos2;\n"
"    texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\n"
"    texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\n"
"    vec4 newColor1 = texture(textureLUT, texPos1);\n"
"    vec4 newColor2 = texture(textureLUT, texPos2);\n"
"    vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n"
"    return vec4(newColor.rgb, color.w);\n"
"}\n"
"\n"
"void main()\n"
"{\n"
"    vec4 tmpColor = texture(ourTexture, TexCoord);\n"
"    FragColor = lookupTable(tmpColor);\n"
"}";

const static GLfloat VERTICES_AND_TEXTURE[] = {
0.5f, -0.5f, // 右下
// 纹理坐标
1.0f,1.0f,
0.5f, 0.5f, // 右上
// 纹理坐标
1.0f,0.0f,
-0.5f, -0.5f, // 左下
// 纹理坐标
0.0f,1.0f,
-0.5f, 0.5f, // 左上
// 纹理坐标
0.0f,0.0f
};

// 真正的纹理坐标在图片的左下角
const static GLfloat FBO_VERTICES_AND_TEXTURE[] = {
1.0f, -1.0f, // 右下
// 纹理坐标
1.0f,0.0f,
1.0f, 1.0f, // 右上
// 纹理坐标
1.0f,1.0f,
-1.0f, -1.0f, // 左下
// 纹理坐标
0.0f,0.0f,
-1.0f, 1.0f, // 左上
// 纹理坐标
0.0f,1.0f
};

// 使用byte类型比使用short或者int类型节约内存
const static uint8_t indices[] = {
// 注意索引从0开始!
// 此例的索引(0,1,2,3)就是顶点数组vertices的下标，
// 这样可以由下标代表顶点组合成矩形
0, 1, 2, // 第一个三角形
1, 2, 3  // 第二个三角形
};

Lut2DOpengl::Lut2DOpengl() {
initGlProgram(ver,fragment);
positionHandle = glGetAttribLocation(program,"aPosition");
textureHandle = glGetAttribLocation(program,"aTexCoord");
textureSampler = glGetUniformLocation(program,"ourTexture");
lut_textureSampler = glGetUniformLocation(program,"textureLUT");

LOGD("program:%d",program);
LOGD("positionHandle:%d",positionHandle);
LOGD("textureHandle:%d",textureHandle);
LOGD("textureSample:%d",textureSampler);
// VAO
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

// vbo
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES_AND_TEXTURE), VERTICES_AND_TEXTURE, GL_STATIC_DRAW);

// stride 步长 每个顶点坐标之间相隔4个数据点，数据类型是float
glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *) 0);
// 启用顶点数据
glEnableVertexAttribArray(positionHandle);
// stride 步长 每个颜色坐标之间相隔4个数据点，数据类型是float，颜色坐标索引从2开始
glVertexAttribPointer(textureHandle, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
(void *) (2 * sizeof(float)));
// 启用纹理坐标数组
glEnableVertexAttribArray(textureHandle);

// EBO
glGenBuffers(1,&ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);

// 这个顺序不能乱啊，先解除vao，再解除其他的，不然在绘制的时候可能会不起作用，需要重新glBindBuffer才生效
// vao解除
glBindVertexArray(0);
// 解除绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 解除绑定
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
}

Lut2DOpengl::~Lut2DOpengl() noexcept {
glDeleteBuffers(1,&ebo);
glDeleteBuffers(1,&vbo);
glDeleteVertexArrays(1,&vao);
// ... 删除其他，例如fbo等
}

void Lut2DOpengl::setPixel(void *data, int width, int height, int length) {
imageWidth = width;
imageHeight = height;
glGenTextures(1, &imageTextureId);

// 绑定纹理
glBindTexture(GL_TEXTURE_2D, imageTextureId);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
// 生成mip贴图
glGenerateMipmap(GL_TEXTURE_2D);
// 解绑定
glBindTexture(GL_TEXTURE_2D, 0);
}

void Lut2DOpengl::setLutPixel(void *data, int width, int height, int length) {
glGenTextures(1, &lut_imageTextureId);

// 绑定纹理
glBindTexture(GL_TEXTURE_2D, lut_imageTextureId);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
// 生成mip贴图
glGenerateMipmap(GL_TEXTURE_2D);
// 解绑定
glBindTexture(GL_TEXTURE_2D, 0);
}

void Lut2DOpengl::onDraw() {
// 恢复绘制屏幕宽高
glViewport(0,0,eglHelper->viewWidth,eglHelper->viewHeight);

// 绘制到屏幕
// 清屏
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);

// 激活纹理
glActiveTexture(GL_TEXTURE1);
// 绑定纹理
glBindTexture(GL_TEXTURE_2D, imageTextureId);
glUniform1i(textureSampler, 1);

// 激活纹理 lut
glActiveTexture(GL_TEXTURE2);
// 绑定纹理
glBindTexture(GL_TEXTURE_2D, lut_imageTextureId);
glUniform1i(lut_textureSampler, 2);

checkError(program);

// VBO与VAO配合绘制
// 使用vao
glBindVertexArray(vao);
// 使用EBO
// 使用byte类型节省内存
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_BYTE,(void *)0);
glUseProgram(0);
// vao解除绑定
glBindVertexArray(0);
// 禁用顶点
glDisableVertexAttribArray(positionHandle);
if (nullptr != eglHelper) {
eglHelper->swapBuffers();
}

glBindTexture(GL_TEXTURE_2D, 0);
}

demo运行结果图

# 系列教程源码

https://github.com/feiflyer/NDK_OpenglES_Tutorial

# Opengl ES系列入门介绍

|
4月前
|
XML 小程序 Java
【Android App】三维投影OpenGL ES的讲解及着色器实现（附源码和演示 超详细）
【Android App】三维投影OpenGL ES的讲解及着色器实现（附源码和演示 超详细）
101 0
|

OpenGL ES 多目标渲染（MRT）
Opengl ES连载系列
286 0
|

Opengl ES之纹理数组
Opengl ES连载系列
234 0
|

Opengl ES之水印贴图
Opengl ES之连载系列
133 0
|
Java 数据安全/隐私保护 Android开发
Opengl ES之矩阵变换(下)
Opengl ES连载系列
112 0
|
Java API 数据安全/隐私保护
Opengl ES之矩阵变换(上)
Opengl ES连载系列
123 0
|

Opengl ES之FBO
Opengl ES连载系列
124 0
|

Opengl ES之踩坑记
Opengl ES之连载系列
124 0
|

Opengl ES之RGB转NV21
Opengl ES连载系列
141 0
|

Opengl ES之YUV数据渲染
Opengl ES连载系列
161 0