1.什么是 LUT
LUT 是 Look Up Table 的简称,称作颜色查找表,是一种针对色彩空间的管理和转换技术。它可以分为一维 LUT(1D LUT) 和 三维 LUT(3D LUT),其中三维 LUT 比较常用。简单来讲,LUT 就是一个 RGB 组合到另一个 RGB 组合的映射关系表。
LUT 滤镜是一种比较经典的滤镜,本质上属于独立像素点替换,即根据 OpenGL 采样器对纹理进行采样得到的像素点,再基于像素点的(R,G,B)分量查表,获得 LUT 映射的(R1,G1,B1),替换原来的输出。
2.LUT图
一般 RGB 像素占用 3 个字节,包含 3 个分量,每个分量有 256 种取值,那么三维 LUT 模板就可以包含 256 X 256 X 256 种情况,占用 48MB 内存空间。这样一个 LUT 模板内存占用过大同时也降低了查找的效率,通常会采取下采样方式来降低数据量。
可以对三维 LUT 模板每个分量分别进行 64 次采样,这样就获得一个 64 X 64 X 64 大小的映射关系表,对于不在表内的颜色值可以进行插值获得其相似结果。
三维 LUT 模板,即 64 X 64 X 64 大小的映射关系表,通常是用一张分辨率为 512 X 512 的二维图片表示,称为 LUT 图。
LUT 图在横竖方向上被分成了 8 X 8 一共 64 个小方格,每一个小方格内的 B(Blue)分量为一个定值,64 个小方格一共表示了 B 分量的 64 种取值。
对于每一个小方格,横竖方向又各自分为 64 个小格,以左下角为原点,横向小格的 R(Red)分量依次增加,纵向小格的 G(Green)分量依次增加。
至此可以根据原始采样像素 RGB 中的 B 分量值,确定要选用 LUT 图中的第几个小格,然后再根据(R,G)分量值为纵横坐标,确定映射的 RGB 组合。
3.LUTl滤镜实现
3.1顶点着色器
#version 300 es
layout(location = 0) in vec3 attr_position;
layout(location = 1) in vec2 attr_uv;
uniform mat4 uni_mat;
out vec2 v_texcoord;
void main(void)
{
v_texcoord = attr_uv;
gl_Position = uni_mat* vec4(attr_position,1.0);
}
mvp矩阵和材质顶点
3.2 片元着色器
#version 300 es
precision mediump float;
//precision highp float;
//Lut 采样器
uniform sampler2D s_LutTexture;
uniform sampler2D uni_textureY;
uniform sampler2D uni_textureU;
uniform sampler2D uni_textureV;
in vec2 v_texcoord;
out vec4 fragColor;
uniform float u_offset;//偏移量
uniform vec2 texSize;//纹理尺寸
vec4 YuvToRgb(vec2 uv){
vec3 yuv;
vec3 rgb;
yuv.x = texture(uni_textureY, uv).r;
yuv.y = texture(uni_textureU, uv).r - 0.5;
yuv.z = texture(uni_textureV, uv).r - 0.5;
rgb = mat3( 1,1,1, 0,-0.39465,2.03211,1.13983,-0.58060,0) * yuv;
return vec4(rgb, 1);
}
vec4 LutFilter(vec2 texCoord)
{
//原始采样像素的 RGBA 值
vec4 textureColor = YuvToRgb(texCoord);
//获取 B 分量值,确定 LUT 小方格的 index, 取值范围转为 0~63
float blueColor = textureColor.b * 63.0;
//取与 B 分量值最接近的 2 个小方格的坐标
vec2 quad1;
quad1.y = floor(floor(blueColor) / 8.0);
quad1.x = floor(blueColor) - (quad1.y * 8.0);
vec2 quad2;
quad2.y = floor(ceil(blueColor) / 7.9999);
quad2.x = ceil(blueColor) - (quad2.y * 8.0);
//通过 R 和 G 分量的值确定小方格内目标映射的 RGB 组合的坐标,然后归一化,转化为纹理坐标。
vec2 texPos1;
texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
vec2 texPos2;
texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
//取目标映射对应的像素值
vec4 newColor1 = texture(s_LutTexture, texPos1);
vec4 newColor2 = texture(s_LutTexture, texPos2);
//使用 Mix 方法对 2 个边界像素值进行混合
vec4 newColor = mix(newColor1, newColor2, fract(blueColor));
return mix(textureColor, vec4(newColor.rgb, textureColor.w), 1.0);
}
void main(void)
{
//为了方便查看效果,做了分屏处理
if(v_texcoord.x > 0.5)
{
fragColor = LutFilter(v_texcoord);
}
else
{
fragColor = YuvToRgb(v_texcoord);
}
}
拿到原始的RGB颜色值,然后获取B分量,对B分量的RG分量进行处理,重新采样得到新的颜色,然后生成新的RGB颜色进行返回。
为了查看效果,做了左右分屏处理。
3.3 渲染部分
void MSDynamicGridLine::Render(MSGLCamera *pCamera) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if(m_bUpdateData == false){
return;
}
static MSVertex triangleVert[] = {
{-1, 1, 1, 0,0},
{-1, -1, 1, 0,1},
{1, 1, 1, 1,0},
{1, -1, 1, 1,1},
};
glm::mat4x4 objectMat = glm::mat4x4(1.0);
glm::mat4x4 objectTransMat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -5));
objectMat = objectMat * objectTransMat;
objectMat = pCamera->projectionMatrix * pCamera->viewMatrix * objectMat ;
m_pOpenGLShader->Bind();
m_pOpenGLShader->SetUniformValue("uni_mat",objectMat);
m_pOpenGLShader->EnableAttributeArray("attr_position");
m_pOpenGLShader->SetAttributeBuffer("attr_position",GL_FLOAT,triangleVert,3,sizeof(MSVertex));
m_pOpenGLShader->EnableAttributeArray("attr_uv");
m_pOpenGLShader->SetAttributeBuffer("attr_uv",GL_FLOAT,&triangleVert[0].u,2,sizeof(MSVertex));
m_PeriodicFrameIndex++;
float progress = GetFrameProgress();
m_pOpenGLShader->SetUniformValue("u_offset",0.2f * progress);
// LOGD("m_nVideoW is %d,progress is %f",m_nVideoW,progress);
if (m_nVideoW>0){
m_pOpenGLShader->SetUniformValue("texSize",glm::vec2(m_nVideoW,m_nVideoH));
}else{
m_pOpenGLShader->SetUniformValue("texSize",glm::vec2(720,1280));
}
m_pOpenGLShader->SetUniformValue("uni_textureY",0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_textures[0]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW, m_nVideoH, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pBufYuv420p);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
m_pOpenGLShader->SetUniformValue("uni_textureU",1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_textures[1]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (char*)(m_pBufYuv420p+m_yFrameLength));
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
m_pOpenGLShader->SetUniformValue("uni_textureV",2);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, m_textures[2]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (char*)(m_pBufYuv420p+m_yFrameLength+m_uFrameLength));
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//增加 Lut滤镜采样器
m_pOpenGLShader->SetUniformValue("s_LutTexture",3);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D,m_texID);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
m_pOpenGLShader->DisableAttributeArray("attr_position");
m_pOpenGLShader->DisableAttributeArray("attr_uv");
m_pOpenGLShader->Release();
return;
}
渲染部分,除了增加了Lut采样器之外其他的都是常规操作。