[GAMES104]-作业2

因为一直要赶一堆莫名其妙的ddl和考试 task3没做 日后再补_(:з」∠)_

Color Grading与LUT

调色是增强画面表现力的一种很有效的手段. 本次作业我们处理的颜色在sRGB空间内, 红、绿、蓝三个颜色分别用一个\([0,1]\)的实数来表示. 将三原色分别用一个坐标轴来表示, 便构成了如图所示的色彩空间(图片来自defold.com):

color cube

在进行调色时, 我们对渲染结果的每个像素进行处理, 根据像素原本的RGB值将其映射到一个新的色彩空间. 这种映射一般采用LUT(look up table)图来进行. LUT有1D和3D之分, Pilot Engine使用的是3D的LUT, 它看上去像这个样子:

这张图片的长为\(1024\)个像素, 宽为\(32\)个像素, 可以看出它由\(32\)个小色块组成. 将这些小方块自上而下拼接在一起, 便组成了一个\(32 \times 32 \times 32\)的色彩空间. 我们需要做的便是根据每个像素的RGB分量找到它在这张图上对应的像素. 具体来说, 原像素的\(G\)分量指示了新像素在LUT上的纵坐标, \(B\)分量指示了新像素所属的色块编号, \(R\)分量指示了新像素在某个色块内部的横坐标.

编写Shader

我们需要编写的Shader文件是color_grading.frag, 可以在\engine\shader\glsl\中找到.

这个shader已经提供了原像素的颜色in_color和LUT的2D采样器color_grading_lut_texture_sampler. 我们需要根据原像素的颜色计算出调色后的颜色并将其赋值给out_color作为输出.

在计算前, 需要获取像素的颜色值:

1
highp vec4 color = subpassLoad(in_color).rgba;

颜色值是一个四维向量, 四个维度分别代表R、G、B和alpha, 均为\([0,1]\)的实数.

在计算出坐标后, 需要从LUT上获取对应的颜色:

1
highp vec4 color_sampled = texture(color_grading_lut_texture_sampler, uv);

其中color_grading_lut_texture_sampler已经提供, uv是一个二维向量, \(u\)代表横坐标, \(v\)代表纵坐标, 坐标原点在图片的左上角, \(x\)轴方向向右, \(y\)轴方向向下. \(u,v\)也是\([0,1]\)上的实数.

我们还需要获取LUT的长宽:

1
highp ivec2 lut_tex_size = textureSize(color_grading_lut_texture_sampler, 0);

有了如上函数, 不难计算出每个像素在LUT上的对应位置. 设\(C\)是LUT的宽, \(R,G,B\)分别为像素的原颜色分量, 则有: \[ u = \frac{\lfloor B \cdot C \rfloor + R}{C}\\ v = G \] 为了提高准确度和防止越界, 可以在LUT的边界上各留出0.5个像素, 将RGB值映射到\(C-1\)个像素中去. 具体细节见代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#version 310 es

#extension GL_GOOGLE_include_directive : enable
//#extension GL_KHR_vulkan_glsl : 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 COLORS = float(lut_tex_size.y);//LUT的每一维的颜色数量
highp vec4 color = subpassLoad(in_color).rgba;

highp float max_color = COLORS - 1.0;//允许使用的最大元素
highp float half_pix_u = 0.5 / float(lut_tex_size.x);//预留出半个像素
highp float half_pix_v = 0.5 / float(lut_tex_size.y);

highp float part = floor(color.b * max_color);//根据B计算出该像素所属的色块编号
highp float threshold = max_color / COLORS;//现在只有COLORS-1个颜色可用
highp float u = (half_pix_u + threshold * color.r + part) / COLORS;
highp float v = (half_pix_v + threshold * color.g);
//预留出0.5像素的边界.例如假设COLORS = 32,在G = 0时v = 0.5/32,G = 1时v = 31.5/32

highp vec2 uv = vec2(u,v);

highp vec4 color_sampled = texture(color_grading_lut_texture_sampler, uv);

out_color = color_sampled;
}

效果展示与自定义LUT

\engine\asset\texture\lut中存放着不同的LUT. 通过修改\engine\asset\global\rendering.global.json"color_grading_map"项可以替换不同的LUT.

在不进行调色时的效果是这样的:

image-20220529161702147

调色后的效果:

image-20220529162809564

要创建自定义的LUT, 只需在GIMP或者PhotoShop等工具编辑原始LUT图像(可以附一张截图方便观察调色效果).

负片效果的LUT:

image-20220529165605604

偏冷色调(好像太绿了):

image-20220529165831153

PS:调色后会出现看上去很奇怪的色块, 目前还不知道如何解决

image-20220529165922432

image-20220529165936292