WebGL.3 --颜色与光照

如何在GL系统中实现光照

让我们先看一张图:

简单的光照有3种类:

他们是 平行光,点光,环境光

1.平行光:

顾名思义,平行光是平行的光,是一个距离我们无限远的点发出的光,比如说太阳. 我们可以用一个 方向(vec3) 和一个颜色(vec3) 来表示.(光没有alpha分量)

2.点光:

类似灯泡,是一个点发出的光,它可以照亮一片区域, 而且垂直在他下方的点会比其他点更亮一些. 我们可以用 一个光源位置(vec3),和一个颜色(vec3)来表示.

3.环境光:

类似于自然中 被粗糙表面反弹回来的光,他无处不在,(一般都比较弱) 我们一般用一个颜色就可以.

物体被光线照射

我们以前做的例子都是无光照的,没有光照的物体会丧失立体感,显得不太自然.

比如太阳光照在一张红纸上,人可以看到是红色 为什么呢? 原因是太阳光照在物体上,被物体反射的光射入眼睛,所以我们看到了颜色.

我们来分析一下:

太阳光 白光 vec(1,1,1) 红纸 vec(1,0,0) 怎么得到 红色的反射光呢?

很简单,相乘就可以 vec(1,1,1) × vec(1,0,0) = vec(1,0,0); 既是我们看到的红色.

照射角

照射角指的是平面法线(垂直于平面)与光照的角度.

日常生活中,正午的阳光会比黄昏的更刺眼,是因为太阳光与地球直射,所以说明了一个问题就是角度决定光照强度 .

90°最强 0°最弱.

照射角度怎么求得:

根据矢量点乘法则:

向量:a(x1,y1,z1),b(x2,y2,z2);

我们都知道 a·b=|a||b|·cosθ 也就是说 如果 |a| 和 |b| 都是 1的话 那么 a·b = cosθ 也就是说 a·b= x1x2 + y1y2 + z1z2 = cosθ;

也就是说 归一化的法向量 · 归一化的光照方向向量 = cosθ

这里涉及到一个 归一化的概念,所谓归一化就是将向量的长度换算成单位长度1, 也就是说 vec(1,1,1) 与 vec(100,100,100) 经过归一化后的值是一样的.

归一化的光照方向向量:

glsl提供了内置函数 normalize 使用如下 normalize(vec3(xxx));

归一化的法向量:

法向量是垂直于平面的向量,但是垂直于平面的向量有2个方向,这里我们遵循 右手法则.

平面的法向量有无数条,因为法向量只规定了与平面垂直,但过平面内一点的法线只有2条, 我们只使用右手法则下的那一条.

在webgl中法向量一般是伴随模型数据 一起传入 shader中..

比如下面 定义了一个带法向量的 CUBE:

  // Create a cube
  //    v6----- v5
  //   /|      /|
  //  v1------v0|
  //  | |     | |
  //  | |v7---|-|v4
  //  |/      |/
  //  v2------v3

  // Coordinates 定义面
  var vertices = new Float32Array([
     2.0, 2.0, 2.0,  -2.0, 2.0, 2.0,  -2.0,-2.0, 2.0,   2.0,-2.0, 2.0, // v0-v1-v2-v3 front
     2.0, 2.0, 2.0,   2.0,-2.0, 2.0,   2.0,-2.0,-2.0,   2.0, 2.0,-2.0, // v0-v3-v4-v5 right
     2.0, 2.0, 2.0,   2.0, 2.0,-2.0,  -2.0, 2.0,-2.0,  -2.0, 2.0, 2.0, // v0-v5-v6-v1 up
    -2.0, 2.0, 2.0,  -2.0, 2.0,-2.0,  -2.0,-2.0,-2.0,  -2.0,-2.0, 2.0, // v1-v6-v7-v2 left
    -2.0,-2.0,-2.0,   2.0,-2.0,-2.0,   2.0,-2.0, 2.0,  -2.0,-2.0, 2.0, // v7-v4-v3-v2 down
     2.0,-2.0,-2.0,  -2.0,-2.0,-2.0,  -2.0, 2.0,-2.0,   2.0, 2.0,-2.0  // v4-v7-v6-v5 back
  ]);

  // Colors 定义面上的颜色
  var colors = new Float32Array([
    1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front
    1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v3-v4-v5 right
    1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v5-v6-v1 up
    1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v1-v6-v7-v2 left
    1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v7-v4-v3-v2 down
    1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0     // v4-v7-v6-v5 back
 ]);

  // Normal 定义法线
  var 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,  // v0-v1-v2-v3 front
    1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right
    0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up
   -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left
    0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down
    0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 back
  ]);

  // Indices of the vertices 定义索引数据
  var indices = new Uint8Array([
     0, 1, 2,   0, 2, 3,    // front
     4, 5, 6,   4, 6, 7,    // right
     8, 9,10,   8,10,11,    // up
    12,13,14,  12,14,15,    // left
    16,17,18,  16,18,19,    // down
    20,21,22,  20,22,23     // back
 ]);

为立方体加上平行光

attribute vec4 a_Position; //顶点
attribute vec4 a_Color; //颜色
attribute vec4 a_Normal; //法向量
uniform mat4 u_MvpMatrix; //变换矩阵
uniform vec3 u_LightColor; //光色
uniform vec3 u_LightDirection; //光方向
varying vec4 v_Color; //计算后的颜色
void main() {
  gl_Position = u_MvpMatrix * a_Position ;
  vec3 normal = normalize(a_Normal.xyz);//归一化法向量
  float nDotL = max(dot(u_LightDirection, normal), 0.0); //计算cos
  vec3 diffuse = u_LightColor * a_Color.rgb * nDotL; //颜色 * 光 * cos
  v_Color = vec4(diffuse, a_Color.a); //最后的颜色
}

感觉更有立体感了,但右侧为什么是黑的?. 原因是光源在左侧,右侧面被挡住了.

但即便是被挡住了,那也不应该是纯黑色. 应该只是比迎光面暗一些才对.

这时候就需要另一种光, --- 环境光

环境光没有方向,可以简单理解他是来自四面八方的平行光,没有一个面能逃得过 环境光.

环境光平行光 相加,效果会更自然..

为立方体加上环境光

uniform vec3 u_AmbientLight;
...
v_Color = vec4(diffuse + ambient, a_Color.a);

这样效果:

为立方体加上点光源

点光源顾名思义,就是由空间中一点发出的光,它只能照亮一小块区域,而且亮度和照射角有关. 计算方式与平行光一样(色 x 光 x cosθ) 只不过点光源 的照射角需要计算.(因为每一顶点与光源的角度都不同)

...
vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));
...
float nDotL = max(dot(lightDirection, normal), 0.0);
vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;
vec3 ambient = u_AmbientLight * a_Color.rgb;
v_Color = vec4(diffuse + ambient, a_Color.a);

下图为点光源下的立方体

留言:

称呼:*

邮件:

网站:

内容: