1.1画布与坐标系

1.1.1画布的定义

  • 画布实际上可以理解为显示到屏幕上的画面,是一个像素矩阵,无论是3D场景抑或是2D场景,最后都要通过画布来呈现
  • 画布可以使用一个函数(会在编写着色器时遇到它)它接受一个坐标值,并绘制一个颜色。

1.1.2 坐标系

  • 坐标系量化排布数值的方式。我们这里讨论的主要是屏幕坐标系,在不同的平台坐标系的定义不一样,但它们都应该具备几个基本特征:
    • 坐标轴
    • 原点
    • 轴距
  • 比如在OpenGL中坐标是这样的,y轴正方向向上

  • image.png
    1.1.2.1 OpenGL坐标系

  • 而在DirectX中,坐标轴是这样的:
    *image.png
    1.1.2.2 DirectX坐标系
  • 我们可以通过变化轴来进行坐标系的变化。

1.1.3 左右手坐标系

  • 在计算机软件中,我们常常会接触到判断物体坐标系的情况,最常用的两个坐标系分别是左手坐标系和右手坐标系,它们最大的不同就是它们所对应的x轴方向不同
  • image.png
    1.1.2.3 左右手坐标系

  • 区别两个坐标系的方式是考虑他们的旋向性,两个坐标系无法通过旋转重合。
  • 如Unity使用的就是左手坐标系,它的特点是x轴向右。我们可以通过左右手法则来判断左右手坐标系
  • 左手坐标系下旋转方向是顺时针的,而右手法则下旋转方向是逆时针的,对应的大拇指的方向也是垂直方向同样是不一样的

  • image.png
    1.1.2.4 左右手法则

1.2 色彩与色彩空间

1.2.1色彩的感性理解

  • 色彩的冷暖,色彩的对比度,色彩的饱和度,色彩的色相

1.2.2色彩的理性理解

  • 光源产生的光线,进入人眼,在人眼细胞产生一系列化学反应,信号传入大脑,最终形成感知

1.2.3光源的定义

  • **发出光线的物体
  • 光的波长:
    • 理论上是无穷大的,人眼的可见光范围决定了我们对其颜色的定义。
    • image.png
      1.2.3.1 光波总览

  • 如何去定量描述光?
    • 光实际上是,波形可以叠加,我们通过功率单位(流明,瓦数等)描述。
    • 分光光度计:通过分光后对波长进行感知,最终得知光能量集中在550nm区域(图中绿色区域)
    • image.png
      1.2.3.2 人眼可见光范围

1.2.4 光传播的方式

  • 基本的传播方式:
    • 直射,折射,反射, 光线追踪方式
  • 反射过程中,光的能量会减少,因为材质吸收了一定能量的光而吸收了某种颜色的光就代表人眼无法再看到这部分光。
    因此我们能看到的就是它们补集,也就是反射出的光

1.2.5 光源的接受者

  • 光源的接受者实际上是摄像机视图,但最终呈现给的是人眼
  • 关于人眼的HDR:人眼可以调节自动曝光,分辨出高亮度的区域中不同亮度层次的区域。
  • 人眼感知色彩的细胞主要分为杆状细胞和锥状细胞
    • 前者主要负责对亮度的感知
    • 后者负责感知色彩
  • 我们主要关注感知色彩的锥状细胞,它又能分为分别感知红绿蓝三种颜色的SML三种细胞

  • image.png
    1.2.5 锥状细胞感受管线波长图
  • 很明显他们感知的光线光波长度不一样。

1.2.6 色彩的猜想

  • 人们猜想人的眼睛有数百种感光细胞感受不同颜色

  • 人们猜想人有三种细胞分别感受红绿蓝三种颜色

  • 人们猜想人分别有感知黑白,红绿,黄蓝这几种细胞

  • 后来第二种和第三种分别演变为了两种色彩模型


1.2.7 艺术家们的Munsell色彩系统

  • 来自于美国艺术家AlbertHenryMunsell
  • 是通过色卡描述色彩,旋转轴是色相,垂直是亮度,由内到外是饱和度

  • image.png
    1.2.7 Munsell 颜色描述系统

  • 更多是基于经验而不是物理,不过这种HSL(色相饱和亮度)的色彩方法仍然被大多数艺术家所接受,至今活跃于各大DCC软件平台中。(如Photoshop)

1.2.8 科学家们的 RGB CSS系统

  • 即RGB Color Specification System(RGB色彩规格系统)
  • 来自于CIE在1931年建立的色彩系统,从物理的方式客观描述量化色彩
  • 通过三原色打光进行观察对比,对于不同的光得到不同的参数值,最后的的结果如下:

  • image.png
    1.2.8 RGB CSS 描述系统波形图

  • 图中表示方式并不规范,因此科学家又对其进行了归一化操作。这个操作在图形学中很常见,主要是为了更加规范的量化数值,便于表述和调节,这里我们使用将rgb三个量相加并且分别被rgb的量所除去的方式建立这个归一化算式:
    • R' = R/RGB, G'= G/RGB, B' = B/RGB
  • 至此,利用此算式和其变式,我们就可以用其中两个已知数计算另外一个数的值。

1.2.9 基础的色域概念

  • 在此基础上,我们将r与g分别作为xy轴,就可以创建一个二维色彩空间,来描述一个颜色r与g部分的组成,前文我们可知,任何一个颜色都是可以由两个参数得出的,因此b就可以通过r与g计算得到。
    • image.png

  • XYZ色彩空间
    • 在上述二维色彩空间或称色域的基础上,为了避免出现负数,科学家又进行了一次迭代,这是用数学的方式做的一次更新,避免了负数的产生,但本身目的就是为了简化计算
    • image.png

  • 转换的方式:
    • XYZ转换使用了矩阵进行转化计算,矩阵相关运算我们在下一节进行,这里只需要知道,矩阵起到了转换空间坐标的作用:
    • image.png

  • 为了便于计算,人们同样对该矩阵进行了归一化操作:
  • image.png
    XYZ转换矩阵进行归一化操作

1.2.10 色域与Yxy色彩空间

  • 经过1.2.9的演变最终形成了色域马蹄图:
  • image.png
    色域马蹄图

  • 但这只是一张二维图片,虽然是人眼可见的色域范围,但并没有亮度表示,因此又将Y轴单独拿出来与xy组成了Yxy色彩空间,这个色彩空间中的Y轴是亮度:
  • image.png
    Yxy色彩空间

1.2.11 色彩空间的定义

  • 一个色彩空间所具备的基础
    • 色域(三个基色坐标,由此形成三角形)
    • 伽马(对三角形进行切分,一种采样方式)
    • 白点(色彩中心)
  • Gamma值
    • 简单来说就是色域切分片段的比例,主要分为
      • 均匀切割(Gamma = 1)便于计算
      • 非均匀切割(Gamma != 1)

  • image.png
  • 对暗部描述更多,对亮部描述更少。

1.2.12 sRGB空间

  • sRGB空间是一种常用的色彩空间,它的gamma值为2.2,由内而外切线越来越粗
  • 为何要用gamma?
    • 便于储存(远古)
    • 人对亮部信息感受少对暗部感受多
  • 目前大部分游戏都会使用线性空间。任何色彩空间都可以是线性的linear,但linear本身不是一个色彩空间,它只是一个伽马值。

1.2.13 颜色的表示以及处理法

  • 我们来看在计算机中如何表示一个颜色
  • 颜色通道
    • 我们通过八位二进制数来对颜色进行表示,八位二进制数最多能表示255个数值,一共三个通道24位二进制数,总共为1670万种。
    • 这种格式也被称为R8G8B8格式
  • 颜色深度
    • 颜色深度就是二进制数的总位数,如上述这种格式的颜色深度为24位
  • 对于颜色的处理
    • 我们可以对颜色进行一些处理,比如对颜色值乘以一个数值,或者对两个颜色值进行加减法运算,这些都是常见的处理方式。但我们限制单个颜色通道的范围为0~255之间。大于或小于这个数值都会被归为最大或最小值。

进阶 1.2.14 色彩空间的转换

  • 我们已经初步了解了色彩空间的概念,色彩空间有很多,我们可以通过程序方式,对色彩空间进行转换。各位可以运用自己的编程能力,完成一个RGB2HSV的转化运算。欢迎大家给出自己的解决方案!下面是提示:

  • RGB2HSV
  • image.png
  • HSV2RGBimage.png
  • 作为本章的小作业,我会在文末给出我的源码供大家参考!
    小提示:mod为取模运算,即取余数

1.3 颜色模型

1.3.1 什么是颜色模型

  • 前文我们提到了色彩空间,其中简单介绍了两种颜色模型,颜色模型有很多,但它们可以大体上分为两类:加色法模型和减色法模型

1.3.2 减色法模型(色料)

  • 减色法模型主要模拟现实中调颜料的过程,打印机的原理就是这样,我们之前讲过,颜色实际上是其材料发射出的颜色,而其余颜色都被吸收了,如果你去调一盘颜料,会发现最后混合在一起就会变为黑色(美术同学应该深有体会)

  • image.png

  • 但不管我们用什么颜料去调色也不应该会出现纯黑色,因此这个稍暗的颜色被称为黑色(K)
  • 而它对应的三原色是品红M青色C和黄色Y因此也被称作CMYK模型。

1.3.3 加色法模型(色光)

  • 与前文减色法相对,在计算机以及物理中,我们会用加色来模拟真正的光线,从纯黑一步步添加颜色,最后叠加在一起被称为白色,而构成它的三原色也就是我们熟悉的RGB三种颜色
  • image.png

总结

  • 介绍了计算机图形学中图形渲染的基本概念,从画布和坐标系的基本定义入手,到光源、色彩及色彩空间的深入解析,再到颜色模型的区分。最终提供了一个基础的RGB转HSV的算法实现

进阶作业案例

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <algorithm>

struct RGB {
int r;
int g;
int b;
};

struct HSV {
float h; // 色相
float s; // 饱和度
float v; // 价值 (亮度)
};

HSV rgbToHsv(const RGB& rgb) {
HSV hsv;
float r = rgb.r / 255.0;
float g = rgb.g / 255.0;
float b = rgb.b / 255.0;

float maxVal = std::max({r, g, b});
float minVal = std::min({r, g, b});
float delta = maxVal - minVal;

// 计算亮度
hsv.v = maxVal;

// 计算饱和度
if (maxVal != 0) {
hsv.s = delta / maxVal;
} else {
// 这里如果 maxVal 是 0,说明 RGB 都是 0
hsv.s = 0;
hsv.h = 0; // 设定为 0
return hsv;
}

// 计算色相
if (delta == 0) {
hsv.h = 0; // 如果没有颜色,则色相为 0
} else {
if (maxVal == r) {
hsv.h = 60 * fmod((g - b) / delta, 6);
} else if (maxVal == g) {
hsv.h = 60 * ((b - r) / delta + 2);
} else if (maxVal == b) {
hsv.h = 60 * ((r - g) / delta + 4);
}
if (hsv.h < 0) {
hsv.h += 360;
}
}

return hsv;
}

int main() {
RGB rgb;
std::cout << "请输入 RGB 值 (r, g, b) 范围在 [0, 255] 之间: ";
std::cin >> rgb.r >> rgb.g >> rgb.b;

// 输入有效性检查
if (rgb.r < 0 || rgb.r > 255 || rgb.g < 0 || rgb.g > 255 || rgb.b < 0 || rgb.b > 255) {
std::cerr << "RGB 值必须在 [0, 255] 之间!" << std::endl;
return 1;
}

HSV hsv = rgbToHsv(rgb);
std::cout << "对应的 HSV 值为: " << "H: " << hsv.h << ", S: " << hsv.s << ", V: " << hsv.v << std::endl;

return 0;
}

  • 欢迎批评指正!

参考资料以及链接