🎊 [WebGL编程指南] 8. 光照原理
光照原理
现实世界物体被光线照射时,会反射一部分光。当只有反射光线进入眼镜时,才能辨认出它的颜色。比如白盒子反射白光,眼睛看到盒子是白色的
光照射到物体上,发生了两个重要现象
三维图形学属于着色(shading)的含义是,根据光照条件重新构建“物体个表面明暗不一的效果”。物体向地面投下影子,又被称为阴影。
光源类型
反射类型
针对平行光或者点光源而言。漫反射的反射光在各个方向上均匀的。现实中大多数材质,比如纸张、岩石、塑料等,表面是粗糙的,反射光会以不固定角度反射出去,漫反射是这一种情况的理想模型。

漫反射的反射光,取决于入射光的颜色、表面的基地色、入射光与表面形成的入射角。

针对环境光而言。反射光的方向可以任务是入射光的反方向。由于环境光照是个方向平均、相等的,所以反射光也是各方向平均,相等的。

当漫反射和环境反射同时存在时,将两者颜色相加得到最终颜色

计算入射角
可以通过计算两个矢量的点积,来计算这两个矢量的夹角的余弦值

点积的运算:



则反射光的颜色等于:

注意:
法向量:

平行光下的漫反射
javascriptCopy
// 顶点着色器
const VSHADER_SOURCE =
"attribute vec4 a_Position;\n" +
"attribute vec4 a_Color;\n" +
"attribute vec4 a_Nromal;\n" + // 法向量
"uniform vec4 u_MvpMatrix;\n" +
"uniform vec3 u_LightColor;\n" + // 光线颜色
"uniform vec3 u_LightDirection;\n" + // 归一化的世界坐标
"varying vec4 v_Color;\n" +
"void main() {\n" +
" gl_Position = u_MvpMatrix * a_Position;\n" +
// 法向量归一化
" vec3 normal = normalize(vec3(a_normal));\n" +
// 计算光线方向和法向量点积
" float nDotL = max(dot(u_LightDirection, normal), 0.0);\n" +
// 计算漫反射光的颜色
" vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;\n" +
" v_Color = vec4(diffuse , a_Color.a);\n" +
"}\n";
// ...
const u_LightColor = gl.getUniformLocation(gl.program. 'u_LightColor')
const u_LightDirection= gl.getUniformLocation(gl.program. 'u_LightDirection')
// ...
// 设置光线颜色
gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0)
// 设置光线方向(三方库)
const lightDirection = new Vector3([0.5, 3.0, 4.0])
// 归一化
lightDirection.normalize()
gl.uniform3fv(u_LightDirection, lightDirection.elements)
// ...
// 法向量
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,
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
// ... 设置其他顶点的法向量
])
// 创建buffer
// 写入数据环境光下的漫反射
真实世界的物体,是被环境光和平行光一起照射的。上述程序只有平行光,会导致没有被照射的面呈现黑色

环境光由墙壁等其他物体反射产生,所以环境光通常比较弱,假设环境光是较弱的白光(0.2, 0.2, 0.2) 物体表面是红色(1.0 ,1.0 ,1.0) 则反射光的颜色就是暗红色(0.2, 0.0, 0.0)
javascriptCopy
// 顶点着色器
const VSHADER_SOURCE =
"attribute vec4 a_Position;\n" +
"attribute vec4 a_Color;\n" +
"attribute vec4 a_Nromal;\n" +
"uniform vec4 u_MvpMatrix;\n" +
"uniform vec3 u_LightColor;\n" +
"uniform vec3 u_LightDirection;\n" +
"uniform vec3 u_AmbientLight;\n" + // 环境光颜色
"varying vec4 v_Color;\n" +
"void main() {\n" +
" gl_Position = u_MvpMatrix * a_Position;\n" +
// 法向量归一化
" vec3 normal = normalize(vec3(a_normal));\n" +
// 计算光线方向和法向量点积
" float nDotL = max(dot(u_LightDirection, normal), 0.0);\n" +
// 计算漫反射光的颜色
" vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n" +
// 计算环境光颜色
" vec3 ambient = u_AmbientLight * a_Color.rgb * nDotL;\n" +
" v_Color = vec4(diffuse + ambient , a_Color.a);\n" +
"}\n";
// ...
const u_AmbientLight = gl.getUniformLocation(gl.program. 'u_AmbientLight')
gl.unifrom3f(u_AmbientLight , 0.2, 0.2, 0.2)运动物体的光照
物体运动时,每个表面的法向量也会随之变化。此时需要将法向量乘以逆转置矩阵。逆转置矩阵是逆矩阵的转置。
逆矩阵的含义是,如果矩阵M的逆矩阵是R,那么R * M 或M*R的结果都是单位矩阵。
求逆转置矩阵的步骤:
TODO
javascriptCopy
// 顶点着色器
const VSHADER_SOURCE =
"attribute vec4 a_Position;\n" +
"attribute vec4 a_Color;\n" +
"attribute vec4 a_Nromal;\n" +
"uniform vec4 u_NormalMatrix;\n" + // 用于变换法向量的矩阵
"uniform vec4 u_MvpMatrix;\n" +
"uniform vec3 u_LightColor;\n" +
"uniform vec3 u_LightDirection;\n" +
"uniform vec3 u_AmbientLight;\n" +
"varying vec4 v_Color;\n" +
"void main() {\n" +
" gl_Position = u_MvpMatrix * a_Position;\n" +
// 法向量归一化
" vec3 normal = normalize(vec3(a_normal * u_NormalMatrix));\n" +
// 计算光线方向和法向量点积
" float nDotL = max(dot(u_LightDirection, normal), 0.0);\n" +
// 计算漫反射光的颜色
" vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n" +
// 计算环境光颜色
" vec3 ambient = u_AmbientLight * a_Color.rgb * nDotL;\n" +
" v_Color = vec4(diffuse + ambient , a_Color.a);\n" +
"}\n";点光源
点光源对物体进行着色,需要计算每个入射点光源所处的方向。

javascriptCopy
// 顶点着色器
const VSHADER_SOURCE =
"attribute vec4 a_Position;\n" +
"attribute vec4 a_Color;\n" +
"attribute vec4 a_Nromal;\n" +
"uniform vec4 u_ModelMatrix;\n" + // 模型矩阵
"uniform vec4 u_NormalMatrix;\n" +
"uniform vec4 u_MvpMatrix;\n" +
"uniform vec3 u_LightColor;\n" +
"uniform vec3 u_LightPosition;\n" + // 光源位置(世界坐标系)
"uniform vec3 u_AmbientLight;\n" +
"varying vec4 v_Color;\n" +
"void main() {\n" +
" gl_Position = u_MvpMatrix * a_Position;\n" +
// 法向量归一化
" vec3 normal = normalize(vec3(a_normal * u_NormalMatrix));\n" +
// 计算顶点的世界坐标系
" vec4 vertexPostion = normalize(vec3(u_ModelMatrix * a_normal));\n" +
;// 计算光线方向并且归一化
" vec3 lightDirection = normalize(vec3(u_LightPosition - vec3(vertexPostion)));\n" +
// 计算光线方向和法向量点积
" float nDotL = max(dot(lightDirection , normal), 0.0);\n" +
// 计算漫反射光的颜色
" vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n" +
// 计算环境光颜色
" vec3 ambient = u_AmbientLight * a_Color.rgb * nDotL;\n" +
" v_Color = vec4(diffuse + ambient , a_Color.a);\n" +
"}\n";
// ...
// 模型矩阵(三方库)
const modelMatrix = new Matrix4()
modelMatrix.setRotate(...)
const u_ModelMatrix = gl.getUniformLocation(gl.program. 'u_ModelMatrix ')
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements)
// ...
逐片元光照
javascriptCopy
// 顶点着色器
const FSHADER_SOURCE =
"attribute vec4 a_Position;\n" +
"attribute vec4 a_Nromal;\n" +
"attribute vec4 a_Color;\n" +
// ...
"uniform vec4 u_ModelMatrix;\n" + // 模型矩阵
"uniform vec4 u_NormalMatrix;\n" +
"varying vec4 v_Normal;\n" +
"varying vec4 v_Color;\n" +
"varying vec4 v_Color;\n" +
"void main() {\n" +
" gl_Position = u_MvpMatrix * a_Position;\n" +
" v_Position = vec3(u_ModelMatrix * a_Position);\n" +
" v_Color = a_Color;\n" +
"}\n";
// 片元着色器
const FSHADER_SOURCE =
// ...
"uniform vec3 u_MvpMatrix;\n" +
"uniform vec3 u_LightColor;\n" +
"uniform vec3 u_LightPosition;\n" + // 光源位置
"uniform vec3 u_AmbientLight;\n" +
"varying vec3 v_Normal;\n" +
"varying vec3 v_Position;\n" +
"varying vec4 v_Color;\n" +
"void main() {\n" +
// 归一化(内插之后长度不一定为1.-)
" vec3 normal = normalize(v_Normal);\n" +
;// 计算光线方向并且归一化
" vec3 lightDirection = normalize(u_LightPosition - v_Position);\n" +
// 计算光线方向和法向量点积
" float nDotL = max(dot(lightDirection , normal), 0.0);\n" +
// 计算漫反射光的颜色
" vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n" +
// 计算环境光颜色
" vec3 ambient = u_AmbientLight * v_Color.rgb * nDotL;\n" +
" gl_FragColor = vec4(diffuse + ambient , v_Color.a);\n" +
"}\n";