注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正。
各种各样的光照
反射光的概念
从平行光源发出的扩散光的光照,通过光的方向(光向量)和面的方向(面法线向量),来计算这个面的扩散程度,从而实现光照。光照最强的地方就是模型的颜色,反之,没有被光照到的地方,就会变成暗色。
但是,像金属那样的质感,就是光泽的表现,只用扩散光就不够了。为什么呢,光照最强的部分,也只不过是显示了模型的原来的颜色,要想表现出光泽,则需要表现一下高亮这样的强光效果。
修改顶点着色器,只通过扩散光虽然也可以实现高亮效果,但是大部分场合都会感觉不自然。这是因为扩散光是不会考虑视线的。扩散光,只是考虑光的方向和面的方向。而反射光,则会考虑观看模型的视线和光的方向,表现出的高亮部分会非常的自然。
表示视线的向量和表示光的向量,再加上面法线向量,可以算出反射光的强度。想一下的话,就是从光源发出的光,撞到模型上发生反射,反射的光的方向如果正好和视线一致的话,这就是最强光了。如下图所示:
像这样模拟反射光,就不得不进行高负荷的计算。这里,有一个手法,可以用比较简单的处理来得到相似的结果,就是通过光向量和视线向量的中间向量来求反射光的类似效果。
使用中间向量得到的反射光的近似处理,首先求出光向量和视线向量的中间向量,然后求中间向量和面法线向量的内积,从而决定反射光的强度。
和面法线向量的内积在之前也做过了吧。在平行光源的计算的时候,就计算了光向量和面法线向量的内积。和这个处理流程是一样的,这次求一下中间向量和面法线向量的内积。这样,就可以简单的模拟反射光的效果了。
顶点着色器的修改
attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 mvpMatrix;
uniform mat4 invMatrix;
uniform vec3 lightDirection;
uniform vec3 eyeDirection;
uniform vec4 ambientColor;
varying vec4 vColor;
void main(void){
vec3 invLight = normalize(invMatrix * vec4(lightDirection, 0.0)).xyz;
vec3 invEye = normalize(invMatrix * vec4(eyeDirection, 0.0)).xyz;
vec3 halfLE = normalize(invLight + invEye);
float diffuse = clamp(dot(normal, invLight), 0.0, 1.0);
float specular = pow(clamp(dot(normal, halfLE), 0.0, 1.0), 50.0);
vec4 light = color * vec4(vec3(diffuse), 1.0) + vec4(vec3(specular), 1.0);
vColor = light + ambientColor;
gl_Position = mvpMatrix * vec4(position, 1.0);
}
修改javascript代码
// 将uniformLocation存入数组
var uniLocation = new Array();
uniLocation[0] = gl.getUniformLocation(prg, 'mvpMatrix');
uniLocation[1] = gl.getUniformLocation(prg, 'invMatrix');
uniLocation[2] = gl.getUniformLocation(prg, 'lightDirection');
uniLocation[3] = gl.getUniformLocation(prg, 'eyeDirection');
uniLocation[4] = gl.getUniformLocation(prg, 'ambientColor');
// 视图×投影坐标变换矩阵
m.lookAt([0.0, 0.0, 20.0], [0, 0, 0], [0, 1, 0], vMatrix);
m.perspective(45, c.width / c.height, 0.1, 100, pMatrix);
m.multiply(pMatrix, vMatrix, tmpMatrix);
// 平行光源的方向
var lightDirection = [-0.5, 0.5, 0.5];
// 视点向量
var eyeDirection = [0.0, 0.0, 20.0];
// 环境光的颜色
var ambientColor = [0.1, 0.1, 0.1, 1.0];
// (中间部分代码略)
// uniform变量的写入
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
gl.uniform3fv(uniLocation[2], lightDirection);
gl.uniform3fv(uniLocation[3], eyeDirection);
gl.uniform4fv(uniLocation[4], ambientColor);