1-调色ColorGrading

又开了个新坑,主要是给目前碰到的图形学算法(主渲染向)做一个类似索引的功能。

本文将介绍调色ColorGrading的原理,并在GAMES104作业里实现这个后处理算法。

原理

有关ColorGrading的介绍详见GAMES104相关文章。

如图,要想实现ColorGrading效果得通过LUT和片段着色器。对于处理前画面的每个像素:

  1. 根据它的RGB在LUT中查找对应的位置;
  2. 根据位置读取LUT中对应的颜色值;
  3. 使用读取的颜色值输出。

但早期着色器语言(如OpenGL ES 2.0)不支持3D纹理,因此需要将3D的LUT拆分成2D的,通过切分它的Z轴(即蓝色轴)实现。

为了查找2D的LUT,需要用蓝色通道值计算在第几个格子里采样,公式如下: \[ \nonumber cell = \left \lfloor{B \times (N - 1)} \right \rfloor \] 其中\(B\)是在[0, 1]内的蓝色通道值,\(N\)是2D LUT的格子总数。知道要查找哪个格子后,就能进行纹理采样了,采样的行为和设置的采样方式有关:

  • NEAREST采样:返回离采样坐标最近纹素的颜色值;
  • LINEAR采样:返回当前纹素和其周围一定距离纹素的颜色插值结果;

需要注意的是,由于蓝色通道值被用于计算采样格子序号,导致它总是整数,会使得结果有突兀。因此可以通过读取相邻的两个格子然后插值解决: \[ \nonumber cell_{low}=\left \lfloor{B \times (N - 1)} \right \rfloor \\ cell_{high} = \left \lceil{B \times (N - 1)} \right \rceil \] 插值的算法如下: \[ \nonumber color = color_{low} \times (1 - C_{frac}) + color_{high} \times C_{frac} \] 其中\(C_{frag}\)是原蓝色通道值的小数部分。

实践

GLSL (GAMES104)

直接从GAMES104的作业2里搬过来了:

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

#define ColorGridingIntensity 1.0

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()
{
    // 获取LUT大小以计算格子数
    highp ivec2 lut_tex_size = textureSize(color_grading_lut_texture_sampler, 0);
    highp float _COLORS      = float(lut_tex_size.x / lut_tex_size.y);

    // 上一阶段传过来的画面
    highp vec4 color       = subpassLoad(in_color).rgba;
    
    // 计算要采样第几个格子
    highp float blueColor = color.b * (_COLORS - 1.0);

    // 手动给蓝色值插值
    highp vec2 uv1 = vec2((color.r+ floor(blueColor))/_COLORS,color.g);
    highp vec2 uv2 = vec2((color.r+ ceil(blueColor))/_COLORS,color.g);

    highp vec4 c1 = texture(color_grading_lut_texture_sampler, uv1);
    highp vec4 c2 = texture(color_grading_lut_texture_sampler, uv2);
    
    highp vec4 newColor = mix(c1, c2, fract(blueColor));

    // 按调色强度系数处理最终结果
    out_color =  mix(color,newColor,ColorGridingIntensity);
}

效果如下:

我的OpenGL渲染器, TODO

参考资料

  • Grading shader tutorial (defold.com)
  • qyz3112746/Games104 at HW2 (github.com)