光照效果是由发射光,环境光,漫反射光以及镜面高光四部分组成,这四部分各自独立计算,然后再累加起来得到最终的光照效果。
1、光特性发射光(emission):由物体自身发射的光。如果物体本身不发光,则无此属性。
计算方法:
发射颜色 = 物体的发射材质颜色2、环境光(ambient):就是哪些在环境中进行了充分散射的光,而无法分辨其方向的光。光线在物体表面上向各个方向上均匀泛射,场景中的物体都会泛射光,这些泛射光又会照射到其他物体上继续被泛射,直到光子能量耗尽为止,这样整个场景中散布着这样的泛射光。在编程时,可通过设置一个颜色常量来表示环境光或使用 ambient occlusion map (环境闭包贴图)来处理环境光。在 OpenGL 中,全局环境光的强度为 (0.2, 0.2, 0.2, 1.0),这弱弱的白色全局环境光确保即使没有额外的光源,场景中的物体依然是可见的。
计算方法:
3、漫反射光(diffuse):光线来自某个方向,但在物体表面上向各个方向上反射,无论在何处观察,散射光看上去亮度都相同。我们之所以能看到物体,就是因为物体将入射的光然后向各个方向反射(所以称之为漫反射)。物体的漫反射材质属性对物体的颜色起着决定性作用。
计算方法:
漫反射颜色 = 光源的漫反射光颜色 × 物体的漫反射材质颜色 × DiffuseFactor
DiffuseFactor = max(0, dot(N, L))
其中漫反射因子 DiffuseFactor 是光线与顶点法线向量的点积。在图形学中,点积几何意义其实就是表示两个向量之间夹角的 cos 值。因此漫反射的规律是:光线与定点法线夹角越小时,漫反射效果越强。
4、镜面高光(specular):光线来自一个特定的方向,然后在物体表面上以一个特定的方向反射出去(镜面反射)。正是由于镜面高光让物体看起来有光泽,如台球,金属表面的光泽。镜面反射与物体的镜面反射材质属性有很大关系,如金属表面能产生很高的镜面发射,而地毯几乎没有镜面反射。在 OpenGL 中,镜面光的强度可通过光泽度(shininess)来调节。
计算方法:
镜面反射颜色 = 光源的镜面光颜色 × 物体的镜面材质颜色 × SpecularFactor
SpecularFactor = power(max(0, dot(N, H)), shininess)H = normalise(L + E)
镜面反射受观察者的位置影响,这一点在上面的计算公式中可以清楚地看出来。H 向量是视线向量 E 与光线向量 L 的半向量(注意:它经过规划化的),其几何意义就是视线与光线夹角的平分线。而 H 和 N 的点积的几何意义就是说这个平分线与法线的夹角的 cos 值,然后将这个 cos 值进行 shininess 次乘方计算得到最终的镜面反射因子 Specularfactor。因此镜面反射规律:当视线与光线在表面处的反射光线夹角(可看成是N与H的夹角)越少时,镜面反射效果最明显(角度越小,cos 值越大)。
首先知道几个函数的意思:
dot(v1,v2) 向量点积
normalize(v) 向量单位化
mod(v1,v2) v1/v2求余
max(v1,v2) 求最大值
pow(v1,v2) 求v1的v2次幂
1、顶点渲染
uniform mat4 uMVPMatrix; //总变换矩阵 uniform mat4 uMMatrix; //变换矩阵 uniform vec3 uCamera; //摄像机位置 uniform vec3 uLightLocationSun; //太阳光源位置 attribute vec3 aPosition; //顶点位置 attribute vec2 aTexCoor; //顶点纹理坐标 attribute vec3 aNormal; //法向量 varying vec2 vTextureCoord; //用于传递给片元着色器的变量 varying vec4 vAmbient; varying vec4 vDiffuse; varying vec4 vSpecular; //定位光光照计算的方法 void pointLight( in vec3 normal, //法向量 inout vec4 ambient, //环境光最终强度 inout vec4 diffuse, //散射光最终强度 inout vec4 specular, //镜面光最终强度 in vec3 lightLocation, //光源位置 in vec4 lightAmbient, //环境光强度 in vec4 lightDiffuse, //散射光强度 in vec4 lightSpecular //镜面光强度 ){ //环境光是完全散射的光照部分,取平均值即可 ambient=lightAmbient; //直接得出环境光的最终强度 vec3 normalTarget=aPosition+normal; //计算变换后的法向量 vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz-(uMMatrix*vec4(aPosition,1)).xyz; newNormal=normalize(newNormal); //对法向量单位化 //散射光:法向量与光线角度 //计算从表面点到光源位置的向量vp vec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz); vp=normalize(vp); float nDotViewPosition=max(0.0,dot(newNormal,vp)); //求法向量与vp的点积与0的最大值 diffuse=lightDiffuse*nDotViewPosition; //计算散射光的最终强度 //表面光:视线+光线+法线 //计算从表面点到摄像机的向量 vec3 eye= normalize(uCamera-(uMMatrix*vec4(aPosition,1)).xyz); vec3 halfVector=normalize(vp+eye); //求视线与光线的半向量 float nDotViewHalfVector=dot(newNormal,halfVector); //法线与半向量的点积 float shininess=50.0; //光泽度,越小越光滑 float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess)); //镜面反射光强度因子 specular=lightSpecular*powerFactor; //计算镜面光的最终强度 } void main() { gl_Position = uMVPMatrix * vec4(aPosition,1); //根据总变换矩阵计算此次绘制此顶点位置 vec4 ambientTemp=vec4(0.0,0.0,0.0,0.0); vec4 diffuseTemp=vec4(0.0,0.0,0.0,0.0); vec4 specularTemp=vec4(0.0,0.0,0.0,0.0); pointLight(normalize(aNormal),ambientTemp,diffuseTemp,specularTemp,uLightLocationSun,vec4(0.05,0.05,0.05,1.0),vec4(1.0,1.0,1.0,1.0),vec4(0.3,0.3,0.3,1.0)); vAmbient=ambientTemp; vDiffuse=diffuseTemp; vSpecular=specularTemp; //将顶点的纹理坐标传给片元着色器 vTextureCoord=aTexCoor; }
precision mediump float; varying vec2 vTextureCoord; //接收从顶点着色器过来的参数 varying vec4 vAmbient; varying vec4 vDiffuse; varying vec4 vSpecular; uniform sampler2D sTextureDay; //纹理内容数据 void main() { //给此片元从纹理中采样出颜色值 vec4 finalColor; finalColor= texture2D(sTextureDay, vTextureCoord); finalColor = finalColorDay*vAmbient+finalColorDay*vSpecular+finalColorDay*vDiffuse; gl_FragColor=finalColor; }