[WebGL入门]二十二,从环境光源发出的光

注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正。



本次的demo的运行结果

平行光源的弱点

上次挑战了一下从平行光源发出的光。平行光源的光的方向是固定的。而且,为了模拟这些,需要用到模型变换矩阵的逆矩阵,以及需要向模型数据中加入法线情报等等。
平行光源的计算负担比较小,在一定程度上模拟了光照效果,在3D模拟世界中经常被用到。但是,平行光源也有弱点,阴面的部分,就是没有被光照到的部分是无法完美的模拟。
比如上次的demo中的定点着色器,仔细观察的话,获取光向量和法线的内积的部分,实际上是取巧的。

>上次的demo中的一部分代码

float diffuse  = clamp(dot(normal, invLight), 0.1, 1.0);
这里使用了GLSL的内置函数clamp,这个函数是将参数的数值限制到指定的范围之内,上面的代码,结果会限制在0.1 ~ 1.0之间。

但是,获取光向量和法线的内积,根据使用场合的不同,可能会出现负数,而使用了clamp函数,即使有负数,也会被指定的最小值0.1代替,假设把clamp的范围设置为0.0 ~ 1.0之间会是什么效果呢,试着运行一下的话,出现下图效果。


这样,完全没有和光发生碰撞的地方会变成完全的黑色。这样会导致无法分清楚模型的轮廓,这就是平行光源的缺点。

就像上次的demo那样,将光照系数的范围设定的大一些,在一定程度上可以解决上述问题。但是使用环境光源的话,可以彻底解决这个问题。


什么是环境光源

环境光是模拟现实世界中的自然光的漫反射。现实世界中,从太阳或是照明器械等发射出的光,遇到物体或者大气中的灰尘等遮挡而发生反射,将世界照亮。比如说,在一个漆黑的屋子里面,只需要一个灯泡,背对着灯泡的话,就会看到自己的影子映射到床或者墙壁上,而自己的身体虽然没有直接被光照到,但是也应该能看得见吧。

由于墙壁和屋顶,以及床和大气中的灰尘等对灯泡发出的光的反射,即使是没有直接被光照到的部分也会受到光的影响。这样,呈现光的漫反射的就是环境光源了。

环境光源用于照亮三维空间中的所有部分。就是说,不是根据顶点的不同处理attribute变量,而是向着色器中传递uniform变量。进一步说,环境光最终影响的是在context中输出的颜色,处理包含四个元素的颜色情报。

>定义环境光的例子

var ambientColor = [0.1,0.1,0.1,1.0];

使用环境光源的时候,需要注意颜色的亮度。环境光照的是全部,比如上面的代码中指定的0.1,如果全都换成1.0的话,模型就会变成全白了。和平行光源不一样,所以要注意。

环境光的颜色,最好是限制在0.2左右以下,这次的demo使用的是0.1。


顶点着色器和javascript的修改

接着,看一下各个代码部分的修改。先从顶点着色器开始看。

>顶点着色器代码

attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform   mat4 mvpMatrix;
uniform   mat4 invMatrix;
uniform   vec3 lightDirection;
uniform   vec4 ambientColor;
varying   vec4 vColor;

void main(void){
    vec3  invLight = normalize(invMatrix * vec4(lightDirection, 0.0)).xyz;
    float diffuse  = clamp(dot(normal, invLight), 0.0, 1.0);
    vColor         = color * vec4(vec3(diffuse), 1.0) + ambientColor;
    gl_Position    = mvpMatrix * vec4(position, 1.0);
}

上次追加了一个uniform变量,就是vec4型的变量ambientColor。环境光是在平行光源等一连串的计算结束之后,最后输出颜色的阶段开始添加的。

这里,如果不使用加法而使用乘法的话,整个画面都会变暗,所以要特别注意。

接下来,修改主程序。

说起来,只是把环境光作为参数传给顶点着色器,追加的东西还是挺少的。

首先在程序中定义环境光的参数。

>增加环境光参数

var ambientColor = [0.1,0.1,0.1,1.0];

接着,为了正确的传给顶点着色器,追加获取着色器的uniformLocation的部分。

// 将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, ‘ambientColor‘);

接着,作为uniform变量在持续循环的时候传给着色器就行了。

>向着色器中传送环境光的参数

gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
gl.uniform3fv(uniLocation[2], lightDirection);
gl.uniform4fv(uniLocation[3], ambientColor);

这样,就完成了顶点着色器和javascript程序的修改了。

其实,因为这次引入了环境光源,所以平行光源部分的光照系数设定成了0.0 ~ 1.0,没有被平行光源照到的部分,就会使用纯粹的环境光来照射。


总结

环境光,模拟了自然界的光的漫反射,弥补了平行光源的缺点。一般,这两种光会同时使用。只使用环境光的话,无法表现出模型的凹凸,只使用平行光源的话,阴影过于严重无法分清模型的轮廓。

3D模拟中的扩散光的代表就是环境光和平行光。这次的demo也实现到了这一步。下次会介绍一下反射光。


点击下面的连接,可以确认一下今天的内容。

同时使用平行光源和环境光源照射的圆环体

http://wgld.org/s/sample_010/


转载请注明:转自lufy_legend的博客http://blog.csdn.net/lufy_legend

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。