Skip to content

之前的章节我们学习了一个自由转动的立方体,本节我们讲解如何给物体增加光照效果,有了光照,物体之间才会有层次感,才会显得更真实。

什么是颜色

现实生活中,当我们看一个物体的时候,很容易地能分辨出它的颜色。但大家有没有想过,颜色到底是什么?

其实颜色并不是客观存在的东西,只是一个视觉效果,决定这个视觉效果的关键因素是有三个:光、物体、视觉系统。

大家都知道,在有光线存在的时候,我们能够看到物体,准确分辨它们的颜色。但是到了晚上伸手不见五指的时候,这时候已经没了光线,我们是看不到任何东西的。

光是什么呢?光是一种电磁波,电磁波中的一部分能够被人眼所感知,这部分被称为可见光。

当光线照射到物体上时,物体能够吸收可见光的一部分,并反射不能吸收的那部分,反射出来的这部分可见光会刺激人眼,经过视神经传到大脑,形成对物体的色彩信息,这就是我们所说的颜色

所以颜色的形成离不开这三个因素。

比如,当白色的太阳光照射在一个红色的物体上时,该物体吸收红色以外的光线,反射剩余的光线(红色光),所以我们看到了红色的物体。

假如我们有一盏蓝色的灯(r:0, g:0, b:1),照射在一个红色的物体(r:1, g:0, b:0)上,该物体吸收红色以外的光线,反射剩余的光线(r:0, g:0, b:0),黑色,所以我们看到的是一个黑色的物体。

简单一句话就是,人眼看到的物体是什么颜色,就代表这个物体反射该颜色。

颜色在计算机中的表示

那么,在计算机中,我们表示物体的颜色时,其实就是设置该物体能够反射的可见光。比如我们为一个物体指定蓝色,本质就是让该物体吸收除了蓝色以外的光线,只反射蓝色光线,这样我们看到的物体就是蓝色的。

我们前面的例子,都是只设置了物体的颜色,并没有加入光照因素。如果我们加入光照因素的话,该如何计算光照效果呢?

加入光照因素后,会影响进入人眼的颜色,所以,我们仍然是通过设置物体的颜色来表达加入光照后的效果。

当我们在计算机中创建一个光源时,需要给光源设置一个颜色(光源也是有颜色的哦),我们给光源设置为白色:

以下的代码部分为 GLSL 语法。

glsl
vec3 light = vec3(1, 1, 1);

假设我们有一个物体是红色的:

glsl
vec3 color = vec3(1, 0, 0);

在计算机领域中,将光源颜色的各个分量物体颜色的各个分量相乘,得到的就是物体所反射的颜色,即该物体在该光源照射下进入人眼的颜色:

glsl
vec3 resultColor = light * color

在 GLSL 语言中,vec3 与 vec3 相乘的实质是将两个 vec3 的分量分别相乘,得到一个新的 vec3。

得到的结果是 vec3(1 * 1, 1 * 0, 1 * 0) = vec3(1, 0, 0),很明显,是红色,这也和现实生活中的表现一致。

前面讲了,如果蓝色的光线照射到红色的物体上,进入人眼的颜色是黑色,我们验证一下:

glsl
vec3 light = vec3(0, 0, 1);
vec3 color = vec3(1, 0, 0);
vec3 resultColor = light * color;

将光线的 rgb分量 和 物体颜色的 rgb 分量相乘:

resultColor = (0 * 1, 0 * 0, 1 * 0) = (0, 0, 0)

最终结果是黑色,很明显,和现实生活中的表现一致。

这就是在计算机中光照作用下物体颜色的计算原理。

环境光

在现实世界中,物体由于有本身材质的不同,对光线的反射效果也不同。材质粗糙的物体会将光线向各个方向进行反射,即漫反射,这也是现实生活中最为常见的反射类型,当漫反射的光线碰到另一个物体时,还会再次进行漫反射,所以,即使在没有光线照射到某个物体的情况下,其他物体的漫反射光也能照射到该物体,所以我们能够看到它。

那么在计算机中,如果想真实地模拟现实生活中没有光源直接照射物体时,通过其他物体的漫反射我们仍然能够看到该物体的情况,耗费的算力特别大,所以定义一种环境光的概念,来近似模拟这种效果。

请注意,虽然在环境光中多次提到了漫反射的概念,但是环境光要模拟的并不是有光线照射下的漫反射,而是多个物体的漫反射互相作用的光线效果。

那么环境光,如何设置呢?

通常,我们使用一个较小的常量乘以光的颜色来模拟环境光。

环境光的计算

假设有一个光源,发出的光线是白色光:

glsl
vec3 lightColor = vec3(1, 1, 1);

我们定义环境光的常量因子为 0.1

glsl
float ambientFactor = 0.1;

那么环境光的计算如下:

glsl
vec3 ambientColor = ambientFactor * lightColor;

GLSL中浮点数和 vec 向量相乘的实质是将该浮点数分别与vec向量的各个分量相乘,并返回新的 vec向量

计算出的环境光是: ambientColor = (1 * 0.1, 0.1 * 1, 0.1 * 1) = (0.1, 0.1, 0.1)

给物体增加环境光

之前的章节例子中,我们并没有提到光的概念,事实上我们默认有一个白色的环境光在里面的,所以我们能够看到它们。

看看我们之前的片元着色器

glsl
gl_FragColor = v_Color;

其实可以理解为一个强度因子为 1 的白色光源:

glsl
vec3 ambientFactor = 1.0;
vec3 lightColor = vec3(1, 1, 1);
vec3 ambientColor = ambientFactor * lightColor;
gl_FragColor = vec4(ambientColor, 1) * v_Color;

那这次,我们要改变强度因子,同时改变光线颜色,所以我们要定义两个常量,强度因子u_AmbientFactor和光源颜色u_LightColor

增加了环境光的片元着色器如下:

glsl
    precision mediump float;
    varying vec4 v_Color;
    //光源颜色
    uniform vec3 u_LightColor;
    //环境光强度因子
    uniform float u_AmbientFactor;
    void main(){
      vec3 ambientColor = u_AmbientFactor * u_LightColor;
      gl_FragColor = vec4(ambientColor, 1) * v_Color;
    }

接下来我们需要通过 JavaScript 给片元着色器传递这两个常量:

javascript
var u_AmbientFactor = gl.getUniformLocation(program, 'u_AmbientFactor');
var u_LightColor = gl.getUniformLocation(program, 'u_LightColor');

找到这两个常量位置,我们需要为他们传递强度因子和光线颜色,这里我们使用滑块来改变强度因子,并使用颜色选择器改变光线颜色,强度因子默认值是 0.2,光线颜色默认是白色:

html
<div>
    环境光因子:
    <input id="ambientFactor" class="range" type="range" min="0" max="1" step="0.01" value="0.2" /> 
</div>
<div>
  	光线颜色:
  	<input id="lightColor" class="color" type="color" value="#FFFFFF" />
</div>

我们看下效果:

大家可以通过调节 1 处的滑块来改变强度因子,观察台体的亮度变化,调节 2 处的颜色选块来改变光线颜色,观察台体的在不同颜色光线照射下的变化。

为了便于观察,通过程序自动改变光线颜色:

可以看到,在不同颜色的光线照射下,人眼观察到的物体颜色也会不同。

回顾

本节讲解了计算机如何模拟现实生活中的颜色以及如何给物体增加环境光,下一节我们学习如何在计算机中模拟真实世界中的光照效果。