颜色#
我们在前几章中简单地使用和处理了颜色,但从未对它们进行过正式的定义。在这里,我们将讨论颜色的概念,并为接下来的光照章节构建场景。
在现实世界中,颜色可以取任何已知的颜色值,每个物体都有其独特的颜色。而在数字世界中,我们需要将(无限的)真实颜色映射到(有限的)数字值,因此并非所有现实世界的颜色都能用数字方式表示。颜色通常使用 red 、 green 、 blue 三个分量进行数字表示,简写为 RGB 。通过对这三个值在 [0,1] 范围内进行不同的组合,我们几乎可以表示任何颜色。例如,要得到_珊瑚色_ ,我们可以定义一个颜色向量:
glm::vec3 coral(1.0f, 0.5f, 0.31f);我们在现实生活中看到的物体颜色并非它本身的颜色,而是物体反射的颜色。物体未吸收(反射)的颜色就是我们感知到的颜色。例如,太阳光被感知为白光,它是多种不同颜色混合而成的(如图所示)。如果我们用这种白光照射一个蓝色的玩具,它会吸收除蓝色以外的所有白色光。由于玩具不吸收蓝色光,所以蓝色光会被反射。这些反射光进入我们的眼睛,使我们觉得玩具是蓝色的。下图展示了一个珊瑚色玩具反射多种不同强度颜色的光:

你可以看到,白色的阳光是由所有可见颜色组成的,物体吸收了其中的大部分颜色。它只反射那些代表物体颜色的颜色,而这些颜色的组合就构成了我们所感知到的颜色(在本例中是珊瑚色)。
从技术上讲,这有点复杂,但我们会在 PBR 章节中讨论这个问题。
这些颜色反射规则直接适用于图形领域。在 OpenGL 中定义光源时,我们需要给它赋予颜色。上一段中我们用了白色,所以光源也用白色。如果我们把光源的颜色值乘以物体的颜色值,得到的颜色就是物体的反射颜色(也就是它的感知颜色)。让我们再来看看之前的例子(这次用珊瑚色),看看如何在图形领域计算它的感知颜色。我们通过将光源颜色向量和物体颜色向量逐个分量相乘来得到结果颜色向量:
glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);
我们可以看到,玩具的颜色 吸收了_大部分白光,但根据其自身的色值,反射出几种红色、绿色和蓝色光。这模拟了现实生活中颜色的运作方式。因此,我们可以将物体的颜色定义为_它从光源反射的各种颜色分量的量 。那么,如果我们使用绿光会发生什么呢?
glm::vec3 lightColor(0.0f, 1.0f, 0.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.0f, 0.5f, 0.0f);
正如我们所见,这个玩具本身不吸收或反射红光和蓝光。它吸收了光线中一半的绿色成分,但也反射了另一半绿色成分。因此,我们感知到的玩具颜色是深绿色。我们可以看到,如果使用绿光,只有绿色成分会被反射并被感知;红色和蓝色成分则无法被感知。结果,珊瑚物体突然变成了深绿色。让我们再用深橄榄绿色的光来做一个例子:
glm::vec3 lightColor(0.33f, 0.42f, 0.18f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f);
正如你所看到的,我们可以使用不同的光色来给物体赋予有趣的颜色。玩转色彩并不难,可以尽情发挥创意。
好了,关于颜色就说到这里,让我们开始搭建一个可以进行实验的场景吧。
灯光场景#
在接下来的章节中,我们将通过模拟真实世界的光照并大量运用色彩来创建有趣的视觉效果。由于现在我们将使用光源,因此我们需要将它们显示为场景中的视觉对象,并添加至少一个对象来模拟光照。
首先,我们需要一个用来照射物体的模型,我们将使用前几章中提到的那个著名的容器立方体。我们还需要一个光源模型来表示光源在 3D 场景中的位置。为了简单起见,我们也用一个立方体来表示光源(我们已经有了 顶点数据, 对吧?)。




