# 环境光和漫射光

screenshot

## 什么是光

Ambient

diffuse

specular

### 模拟光

Directional lighting

Point lighting

Spot lighting

### 环境照明

``````// 最终颜色 = 材质颜色 * 环境光颜色
final color = material color * ambient light color
``````

``````// 最终颜色 = 红色 * 暗白色 = 暗红色
final color = {1, 0, 0} * {0.1, 0.1, 0.1} = {0.1, 0.0, 0.0}
``````

### 漫射照明-点光源

#### 第一步：计算朗伯因子（lambert factor）

``````// 光线向量 = 光源位置 - 物体位置
light vector = light position - object position
// 余弦 = 物体法线和归一化后的光线向量的点积
cosine = dot product(object normal, normalize(light vector))
// 朗伯因子 = 取余弦和0中最大的
lambert factor = max(cosine, 0)
``````

``````// 光线向量
light vector = {0, 10, -10} - {0, 0, 0} = {0, 10, -10}
// 物体法线
object normal = {0, 1, 0}
``````

``````// 光线向量长度 = 平方根(0*0 + 10*10 + (-10 * -10)) = 平方根(200) = 14.14
light vector length = square root(0*0 + 10*10 + (-10 * -10)) = square root(200) = 14.14
// 归一化光线向量
normalize light vector = {0, 10/14.14, -10/14.14} = {0, 0.707, -0.707}
``````

``````// 点积
dot product({0, 1, 0}, {0, 0.707, -0.707}) = (0 * 0) + (1 * 0.707) + (0 * -0.707) = 0.707
``````

``````// 朗伯因子
lambert factor = max(0.707, 0) = 0.707
``````

OpenGL ES 2的着色器语言内置了对其中一些函数的支持，因此我们不需要手动完成所有数学运算，但它仍然有助于理解正在发生的事情。

#### 第二步：计算哀减系数

``````// 亮度 = 1 / 距离的平方
luminosity = 1 / (distance * distance)
``````

``````luminosity = 1 / (14.14 * 14.14) = 1 / 200 = 0.005
``````

#### 第三步：计算最终颜色

``````// 最终颜色 = 材质颜色 * （光的颜色 * 朗伯因子 * 亮度）
final color = material color * (light color * lambert factor * luminosity)
``````

``````final color = {1, 0, 0} * ({1, 1, 1} * 0.707 * 0.005) = {1, 0, 0} * {0.0035, 0.0035, 0.0035} = {0.0035, 0, 0}
``````

``````// 第一步
light vector = light position - object position
cosine = dot product(object normal, normalize(light vector))
lambert factor = mac(cosine, 0)

// 第二步
luminosity = 1 / (distance * distance)

// 第三步
final color = material color * (light color * lambert factor * luminosity)

``````

### 将这一切放到OpenGL ES 2着色器中

#### 顶点着色器

``````final String vertexShader =
"uniform mat4 u_MVPMatrix;      \n" + // 一个表示组合model、view、projection矩阵的常量
"uniform mat4 u_MVMatrix;       \n" + // 一个表示组合model、view矩阵的常量
"uniform vec3 u_LightPos;       \n" + // 光源在眼睛空间（相对于相机视角）的位置

"attribute vec4 a_Position;     \n" + // 我们将要传入的每个顶点的位置信息
"attribute vec4 a_Color;        \n" + // 我们将要传入的每个顶点的颜色信息
"attribute vec3 a_Normal;       \n" + // 我们将要传入的每个顶点的法线信息

"varying vec4 v_Color;          \n" + // 这将被传入片段着色器

"void main()                    \n" + // 顶点着色器入口
"{                              \n" +
// 将顶点转换成眼睛空间（相对于相机视角）
"   vec3 modelViewVertex = vec3(u_MVMatrix * a_Position);                \n" +
// 将法线的方向转换成眼睛空间（相对于相机视角）
"   vec3 modelViewNormal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));       \n" +
// 将用于哀减
"   float distance = length(u_LightPos - modelViewVertex);               \n" +
// 获取从光源到顶点方向的光线向量
"   vec3 lightVector = normalize(u_LightPos - modelViewVertex);          \n" +
// 计算光线矢量和顶点法线的点积，如果法线和光线矢量指向相同的方向，那么它将获得最大的照明
"   float diffuse = max(dot(modelViewNormal, lightVector), 0.1);         \n" +
// 根据距离哀减光线
"   diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance * distance)));    \n" +
// 将颜色乘以亮度，它将被插入三角形中
"   v_Color = a_Color * diffuse;                                         \n" +
// gl_Position是一个特殊的变量用来存储最终的位置
// 将顶点乘以矩阵得到标准化屏幕坐标的最终点
"   gl_Position = u_MVPMatrix * a_Position;                              \n" +
"}                                                                       \n";
``````

``````// 将顶点转换成眼睛空间（相对于相机视角）
"   vec3 modelViewVertex = vec3(u_MVMatrix * a_Position);                \n"
``````

``````// 将法线的方向转换成眼睛空间（相对于相机视角）
"   vec3 modelViewNormal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));       \n" +
``````

``````// 将用于哀减
"   float distance = length(u_LightPos - modelViewVertex);               \n"
``````

``````// 获取从光源到顶点方向的光线向量
"   vec3 lightVector = normalize(u_LightPos - modelViewVertex);          \n"
``````

``````// 计算光线矢量和顶点法线的点积，如果法线和光线矢量指向相同的方向，那么它将获得最大的照明
"   float diffuse = max(dot(modelViewNormal, lightVector), 0.1);         \n"
``````

``````// 根据距离哀减光线
"   diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance * distance)));    \n"
``````

``````// 将颜色乘以亮度，它将被插入三角形中
"   v_Color = a_Color * diffuse;                                         \n" +
// gl_Position是一个特殊的变量用来存储最终的位置
// 将顶点乘以矩阵得到标准化屏幕坐标的最终点
"   gl_Position = u_MVPMatrix * a_Position;                              \n"
``````

#### 像素着色器

``````final String fragmentShader =
"precision mediump float;       \n" + // 我们将默认精度设置为中等，我们不需要片段着色器中的高精度
"varying vec4 v_Color;          \n" + // 这是从三角形每个片段内插的顶点着色器的颜色
"void main()                    \n" + // 片段着色器入口
"{                              \n" +
"   gl_FragColor = v_Color;     \n" + // 直接将颜色传递
"}                              \n";
``````

## 正方体的构造

``````//X, Y, Z
final float[] cubePositionData = {
// 在OpenGL，逆时针绕组（下面的点事逆时针顺序）是默认的。
// 这意味着当我们在观察一个三角形时，如果这些电视逆时针的，那么我们正在看"前面"，如果不是我们则正在看背面
// OpenGL有一个优化，所有背面的三角形都会被剔除，因为它们通常代表一个物体的背面，无论如何都不可见
// 正面
-1.0F, 1.0F, 1.0F,
-1.0F, -1.0F, 1.0F,
1.0F, 1.0F, 1.0F,
-1.0F, -1.0F, 1.0F,
1.0F, -1.0F, 1.0F,
1.0F, 1.0F, 1.0F,
...
};

// R，G，B，A
final float[] cubeColorData = {
// 正面红色
1.0F, 0.0F, 0.0F, 1.0F,
1.0F, 0.0F, 0.0F, 1.0F,
1.0F, 0.0F, 0.0F, 1.0F,
1.0F, 0.0F, 0.0F, 1.0F,
1.0F, 0.0F, 0.0F, 1.0F,
1.0F, 0.0F, 0.0F, 1.0F,
...
};
``````

## 新的OpenGL flag

``````// 使用剔除去掉背面
GLES20.glEnable(GLES20.GL_CULL_FACE);
// 启用深度测试
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
``````

## 加载着色器程序的修改

``````GLES20.glGetProgramInfoLog(programHandle);
``````

## 光点的顶点和着色程序

``````// 定义一个简单的着色程序
"uniform mat4 u_MVPMatrix;                  \n" +
"attribute vec4 a_Position;                 \n" +
"void main()                                \n" +
"{                                          \n" +
"   gl_Position = u_MVPMatrix * a_Position; \n" +
"   gl_PointSize = 5.0;                     \n" +
"}                                          \n";
"precision mediump float;                   \n" +
"void main()                                \n" +
"{                                          \n" +
"   gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) \n" +
"}                                          \n";
``````

## 进一步练习

• 尝试删除“过渡饱和”看会发生什么
• 这里的照明方式存在缺陷，你能发现是什么吗？提示：我们做环境照明的方式的缺点是什么，以及alpha会放生什么？
• 如果将`gl_PointSize`添加到正方体着色器并使用`GL_POINTS`绘制它会发生什么？