Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

WebGL(拾) 镜面反射

  比起之前的漫反射处理,镜面反射就要复杂一些了。镜面反射需要通过反射方向向量与视线方向向量来计算出光线对眼睛的作用强度,而且算法还不止一种。物体本身还有个和反射有关的光泽度的参数。这些东西的存在就让计算变得麻烦起来。
  计算镜面反射我们需要两个参数,一个是视线方向,另一个是物体的光泽度。视线方向其实就是镜头方向,这个以后说镜头时再细说。从之前推导的投射矩阵中可以知道,在镜头不做任何变换的情况下,我们的视线方向是{0,0,-1};光泽度其实就是反射光的聚合度,光泽度为1时就是漫反射,光泽度无穷大时就是镜子。不过的镜子的效果我们不会用镜面反射来做,因为我们无法像上帝一样把顶点做到原子大小。
  计算镜面反射的方法很多,只要符合效果就行了。最常用的计算方法是:先通过光线的方向向量和物体的平面法向量来计算出光线的反射方向向量。然后再用光线的反射方向向量和视线的方向向量做点积来计算出光线对眼睛的作用强度。再把这个计算得到的作用强度以光泽度值作乘方运算来增加高光部分的汇聚性,减少扩散。最后再把计算结果乘上光线本身的颜色。
  另一种方法是:先把光线的方向向量和视线的方向向量相加,这会得到一个平分它们夹角的向量。再把这个向量标准化后与物体的平面法向量做点积来计算出之前得到夹角平分向量与物体的平面法向量的平行程度。之后的操作和第一种方法一样。
  我的例子中使用了第一种方法。第二种方法在计算光泽度前得到的数值比第一种散,所以需要设置更高的光泽度才能达到和第一种方法一样的效果。先来看看例子的着色器代码吧 //顶点着色器
attribute vec3 po;
attribute vec3 no;
attribute vec2 mp;
uniform mat4 tra;
uniform mat4 pro;
uniform float gs; //物体光泽度
uniform vec3 dif_di; //漫射光方向
uniform vec3 dif_co; //漫射光颜色
uniform vec3 spe_di; //反射光方向
uniform vec3 spe_co; //反射光颜色
uniform vec3 eye_di; //视线方向
varying vec2 mp_v;
varying vec3 dif;
varying vec3 spe;
//定义函数,计算两个单位向量的负点积,并把结果限制在0到1之间
float li_dot(vec3 v1,vec3 v2){
  return clamp(-dot(normalize(v1),normalize(v2)),0.0,1.0);
}
void main(){
  mp_v=mp;
  gl_Position=pro*tra*vec4(po,1.0);
  //旋转法向量
  vec3 nor=(tra*vec4(no,0.0)).xyz;
  //计算漫射效果
  dif=dif_co*li_dot(nor,dif_di);
  //计算反射效果
  vec3 ref=reflect(normalize(spe_di),nor);
  spe=spe_co*pow(li_dot(ref,eye_di),gs);
  //反射效果的另一种计算方法
  //spe=spe_co*pow(li_dot(nor,spe_di+eye_di),gs);
}
  这是顶点着色器的代码。由于使用的东西比较多,代码有点长了。而且在这个代码中我还是使用了自定义函数,不过这个很简单,稍微有点编程基础的都能理解,我也不说啥了。这个代码中使用了几个之前没说过的内置函数。normalize是把向量标准化,也就是把向量的长度变成1,方向不变。clamp是把一个值限制在某个范围内,超出范围就变成范围的最小或最大值,可以代替max+min来使用。reflect是计算反射向量的,传入一个方向向量和一个平面的法向量就可以得到方向向量在那个平面上的反射向量。pow这个函数就不用说了吧,几乎所有编程语言的乘方函数都是pow。接着是片段着色器的代码 //片段着色器
precision lowp float;
varying vec2 mp_v;
varying vec3 dif;
varying vec3 spe;
uniform vec3 amb; //环境光颜色
uniform sampler2D tex;
void main(){
  vec4 co=texture2D(tex,mp_v)*vec4(dif,1.0);
  gl_FragColor=co+vec4(spe,1.0)+vec4(amb,1.0);
}
  这里有个很关键的概念。镜面反射的最终计算颜色和环境光一样是与物体颜色相加的而不是相乘的,只有漫射光才是与物体颜色逐分量相乘的。我们之所以能看到物体的颜色是因为漫射,所以漫射光和物体本身的颜色是线性关系的。而镜面反射就不同,无论物体是什么颜色的,它反射的都是光线的颜色。上学的时候,我们偶尔会因为角度不对而看不到黑板上的字,这就是因为黑板反射的是光的颜色,和黑板上面的文字没关系。不过一般的物体镜面反射时都会有一些发散,因为它们的光泽度有限。这些发散的光会给物体本身的漫射提供亮度,让漫射的颜色看上去更亮一些。总之要记得,镜面反射在计算时候是加上其它颜色的而不是乘上。
  片段着色器中还有一句“precision lowp float”,由于片段着色器的浮点数需要设置精度,而这个例子使用的变量太多,所以就不逐个设置了,而是用这个语句统一设置了。

  程序代码其实没什么变化,只是传入这些参数而已,我就不单独贴出来了。最后是完整例子

  WebGL镜面反射
网名:
34.203.245.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^