LUT 查找表
LUT 可以用于后处理中的颜色矫正,它能将一个 RGB 颜色唯一地映射到另一个颜色。
它是一个三维的颜色查找表,但图形渲染中,由于图形 API 通常并不支持三维纹理的查找,所以我们会将三维 LUT 展开成二维的形式。
下图是一个标准的 16 * 256 LUT,每个 Block 由16*16 个像素组成。像素内使用 RG 通道索引,Block 通过 B 通道索引。
需要注意的是,这个 LUT 的原点是在左上角的,Vulkan 和 DX 的纹理坐标默认也是左上角,而 OpenGL 在计算 UV 的时候要将 V 坐标翻转一下。
实现
RGB 的色彩空间是 255 * 255 * 255,而 LUT 的色彩空间远小于此,因此在设置纹理的时候需要将纹理的 Filter 设置为线性插值以上。
VkSamplerCreateInfo samplerInfo {
};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
R 通道和 G 通道实际上是一个 Block 内的 偏移,Block 之间通过 B 通道索引。
具体的 Shader 代码如下:
#version 310 es
#extension GL_GOOGLE_include_directive : enable
#include "constants.h"
layout(input_attachment_index = 0, set = 0, binding = 0) uniform highp subpassInput in_color;
layout(set = 0, binding = 1) uniform sampler2D color_grading_lut_texture_sampler;
layout(location = 0) out highp vec4 out_color;
void main()
{
highp ivec2 lut_tex_size = textureSize(color_grading_lut_texture_sampler, 0);
highp float lenY = float(lut_tex_size.y);
highp float lenX = float(lut_tex_size.x);
// 原图像的 RGB
highp vec4 color = subpassLoad(in_color).rgba;
// floor(color.b * lenY) * lenY 先计算出每个 Block 的起始位置
// color.r * lenY 计算 Block 内的偏移
highp float u1 = (color.r * lenY + floor(color.b * lenY) * lenY) / lenX;
highp float u2 = (color.r * lenY + ceil(color.b * lenY) * lenY) / lenX;
// 由于我们的 LUT 是16 * 256,所以 v 直接就是 g 通道
highp float v = color.g;
// 由于我们 B 通道做了四舍五入,所以需要在两个 Block 之间插值
highp vec4 color1 = texture(color_grading_lut_texture_sampler, vec2(u1, v));
highp vec4 color2 = texture(color_grading_lut_texture_sampler, vec2(u2, v));
out_color = mix(color1, color2, fract(lenY * color.b));
}
效果
原图 | 结果 |
---|---|