使用 LUT 实现 Color Grading 后处理效果

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));
}

效果

原图 结果
在这里插入图片描述 在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/onion974453462/article/details/126796913