6.1 光是如何使用的
6.1.1 光的作用
在现实生活中,我们很少见到一个完全纯色的物体,因为在光照的作用下,物体也会产生出不同的明暗的反应。
在这个立方体可以体现出,正对着光的平面会显得明亮一些,背对着光的平面就会显得暗淡一些。即使是一个完全纯色的物体也会在光照的作用下产生不同的反应,让我们看到物体真实的样子。
6.1.2 光的类型
1、点光源
(1)一个点向周围发出的光,如灯泡、火焰等。
(2)定义一个点光源光需要光源的位置、光线方向以及颜色。
(3)根据照射点的位置不同,光线的方向也不同。
目前立方体在光源的左下方,因此光线的方向也是往左下方的,如果立方体在光源的右下方,那么光线的方向也随之而改变。
2、平行光
(1)可以看成是无限远处的光源发出的光,如太阳光。
(2)因为离光源的位置特别远,所以到达被照物体时可以认为光线是平行的。
(3)只需要光照方向和光照颜色。
3、环境光
(1)也就是间接光,是指光源发出后,经过其他物体各种发射,然后照到物体表面上的光线。
(2)环境光的强度差距非常小,没有必要精确计算光线强度。
(3)环境光是均匀照射到物体表面的,只需要定义光照颜色。
6.1.3 反射
有了光有时候也不一定就能看到物体,因为有的物体是不发射光线的,但有些物体它反射光线非常强烈。反射包含以下两种类型:
1、环境反射
(1)环境反射是针对环境光而言的,在环境反射中,环境光照射物体是各方面均匀、强度相等的,反射的方向就是入射光的反方向。
(2)最终物体的颜色只跟入射光颜色和基底色有关。
(3)环境反射光颜色 = 入射光颜色 * 基底色
环境反射在反射的时候,因为物体表面是均匀的,所以反射出去之后也是均匀的。
2、漫反射
(1)在现实中大多数物体表面都是粗糙的,导致每一条光线的入射光的角度和反射角度都不一样
(2)漫反射中反射光的颜色除了取决于入射光的颜色、表面的基底色,还有入射光与物体表面的法向量形成的入射角。
(3)令入射角为α,漫反射光的颜色可以根据下式计算:
漫反射光颜色 = 入射光颜色 * 表面基底色 * cos(α)
(4)入射角α可以通过光线方向和法线方向的点积来计算:
cos(α) = 光线方向 * 法线方向
(5)最终漫反射光颜色的公式如下:
漫反射光颜色 = 入射光颜色 * 表面基底色 * (光线方向 * 法线方向)
(6)"光线方向"指的是入射方向的反方向,即从入射点指向光源方向
(7)当漫反射和环境反射同时存在时,将两者加起来,就会得到物体最终被观察到的颜色:
表面的反射光颜色 = 漫反射光颜色 + 环境反射光颜色
6.2 给场景添加光源
我们了解了光源最主要的组成是漫反射光颜色和环境反射光颜色,那我们只需要根据它们所需要的条件设置即可。
6.2.1 设置环境反射光颜色
环境反射光的颜色,是由入射光颜色和基底色组成的。入射光颜色也就是环境光颜色,基底色也就是物体表面的颜色。
环境反射光颜色 = 入射光颜色 * 基底色
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aNormal;
varying vec4 vColor;
uniform mat4 mat;
void main() {
// 环境光
vec3 uAmbientLightColor = vec3(0.2,0.2,0.2);
// 物体表面的颜色
vec4 aColor = vec4(1.0,0.0,0.0,1.0);
// 环境反射光颜色
vec3 ambient = uAmbientLightColor * vec3(aColor);
}
`; // 顶点着色器
6.2.2 设置漫反射光颜色
漫反射光的颜色,是由入射光颜色、表面基底色、光线方向、法线方向4者组成的。
漫反射光颜色 = 入射光颜色 * 表面基底色 * cos(α)(入射光与物体表面的法向量形成的入射角)
= 入射光颜色 * 表面基底色* (光线方向 * 法线方向)
1、设置法线方向(法向量)
以矩形为例子,它一共有6个面,因此我们要给它设置6个面的法向量,所谓法向量的意思是垂直于当前平面的方向,当然这个方向有两个,一个是向内,一个是向外,在这里是设置光源的反射光,那我们设置的是向外的法向量。
它们的法向量分别是:前(0.0,0.0,1.0)、后(0.0,0.0,-1.0)、左(-1.0,0.0,0.0)、右(1.0,0.0,0.0)、上(0.0,1.0,0.0)、下(0.0,-1.0,0.0)
注意:在设置法向量的时候,要注意每个方向的数量要跟每个面的点的数量一致,例如前面的法向量为(0.0,0.0,1.0),但因为一个面是由两个三角形(4个点)组成的,因此在设置的时候要写成0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0。
// 法向量
const normals = new Float32Array([
0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0, // 前面
0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0, // 后面
-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0, // 左面
1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0, // 右面
0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0, // 上面
0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0, // 下面
])
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aNormal)
2、设置漫反射光颜色
现在法向量已经准备就绪,它可以通过变量传入到着色器源码里。其余3个入射光颜色、表面基底色和光线方向分别给它们定义即可。
其中光线方向是从入射点指向光源方向,这个得先定义好光源的位置和点坐标的位置,通过光源位置与坐标位置的向量差可以得到光线方向。
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aNormal;
varying vec4 vColor;
uniform mat4 mat;
void main() {
// 定义点光源的颜色
vec3 uPointLightColor = vec3(1.0,1.0,0.0);
// 物体表面的颜色
vec4 aColor = vec4(1.0,0.0,0.0,1.0);
// 点光源的位置
vec3 uPointLightPosition = vec3(-5.0,6.0,10.0);
// 顶点的世界坐标
vec4 vertexPosition = mat * aPosition;
// 点光源的方向
vec3 lightDirection = normalize(uPointLightPosition - vec3(vertexPosition));
// 计算入射角 光线方向和法线方向的点积
float dotDeg = dot(lightDirection, vec3(aNormal));
// 漫反射光的颜色
vec3 diffuseColor = uPointLightColor * vec3(aColor) * dotDeg;
}
`;
// 法向量
const normals = new Float32Array([
0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0, // 前面
0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0, // 后面
-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0, // 左面
1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0, // 右面
0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0, // 上面
0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0, // 下面
])
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aNormal)
6.2.3 设置光源
至此已经获取环境反射光和漫反射光的颜色,最后把它们俩相加即可得到最终的光源。
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aNormal;
varying vec4 vColor;
uniform mat4 mat;
void main() {
// 环境光
vec3 uAmbientLightColor = vec3(0.2,0.2,0.2);
// 物体表面的颜色
vec4 aColor = vec4(1.0,0.0,0.0,1.0);
// 环境反射光颜色
vec3 ambient = uAmbientLightColor * vec3(aColor);
// 定义点光源的颜色
vec3 uPointLightColor = vec3(1.0,1.0,0.0);
// 点光源的位置
vec3 uPointLightPosition = vec3(-5.0,6.0,10.0);
// 顶点的世界坐标
vec4 vertexPosition = mat * aPosition;
// 点光源的方向
vec3 lightDirection = normalize(uPointLightPosition - vec3(vertexPosition));
// 计算入射角 光线方向和法线方向的点积
float dotDeg = dot(lightDirection, vec3(aNormal));
// 漫反射光的颜色
vec3 diffuseColor = uPointLightColor * vec3(aColor) * dotDeg;
gl_Position = vertexPosition;
vColor = vec4(ambient + diffuseColor, aColor.a);
}
`;
// 法向量
const normals = new Float32Array([
0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0, // 前面
0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0, // 后面
-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0, // 左面
1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0, // 右面
0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0, // 上面
0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0, // 下面
])
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aNormal)