平行光源:如太阳光源,类似于灯源在无穷远处,照射物体的各个地方光照强度相同,光照方向也相同。
点光源:离物体有限距离,且灯光会随着距离衰减,物体表面光照方向不同。
聚光灯:如 手电筒照在一个圆圈上,周围衰减。(这里光照计算暂不介绍)
上一篇给大家讲了,opengl光照模型得设置 灯源环境光 材质环境光。物体反射的环境光为这两者的乘积, 2种光源计算方法一
vec4 ambientColor=U_LightAmbient*U_AmbientMaterial;
1、平行光源
由图可见,太阳是平行光源,我们的反光照强度与入射光与法向量的夹角有关,因此可以利用向量AB 和 AN的乘积(绿色箭头向量,入射方向的反方向)来表示反射光大小,当然 最小值为0,即没有反射光。
unitform vec4 U_ligthtPos;
uniform vec4 u_diffuseLightColor;
uniform vec4 UdiffuseMaterial;
varying vec4 V_diffuseColor;
void main(){
vec3 L = U_lightPos.xyz;
L = normalize(L);
vec3 n = normalize(mat3(NM)*normal);
float diffuseIntensity = max(0.0 , dot(L,n));
V_diffuseColor = U_diffuseLightColor * UdiffuseMaterial * diffuseIndensity;
}
2、点光源
1、点光源与平行光源不同,它的L向量计算方法不同,是由平面上点 和 灯源位置组成L向量
vec4 specularColor=vec4(0.0,0.0,0.0,0.0);
if(diffuseIndensity>0.0){
vec3 reflectDir=normalize(reflect(-L,n));// 反射方向
vec3 viewDir=normalize(U_CameraPos.xyz-V_WorldPosition.xyz);// 观看方向
// 计算镜面反射强度
specularColor=U_LightSpecular*U_SpecularMaterial*pow(max(0.0,dot(viewDir,reflectDir)),32.0);
}
2、衰减系数 有3个 constantFactor、linearFactor、expFactor这些是常量值,靠经验值来设置,后面2个常量分别与灯光的距离和距离的平方乘积构成衰减系数。
float attenuation=1.0;
//light attribute
float constantFactor=0.5;
float linearFactor=0.3;
float expFactor=0.1;
L=U_LightPos.xyz-V_WorldPos;
distance=length(L);
attenuation=1.0/(constantFactor+linearFactor*distance+expFactor*distance*distance);
L=normalize(L);
//N vector
vec3 n=normalize(V_Normal);
float diffuseIntensity=max(0.0,dot(L,n));
vec4 diffuseColor=U_DiffuseLightColor*U_DiffuseMaterial*diffuseIntensity*attenuation;
平行光 和 点光源的计算方法除了上面L的计算法方法不一样外,其余的一样。计算方法在下面的blin-phong讲到了,就是反射光向量与点和眼睛的向量乘积作为底数,32为常量设置。见blin的图像 AC 和 AD的乘积
反射光线的向量利用reflect函数来计算得到,参数为入射光的方向向量 和 法向量 (都归一化了)。
// 计算镜面反射
vec4 specularColor=vec4(0.0,0.0,0.0,0.0);
if(diffuseIndensity>0.0){
vec3 reflectDir=normalize(reflect(-L,n));// 反射方向
vec3 viewDir=normalize(U_CameraPos.xyz-V_WorldPosition.xyz);// 观看方向
// 计算镜面反射强度 第二个参数32 一般是2的指数次
float specularColorIndensity=pow(max(0.0,dot(viewDir,reflectDir)),32.0);
specularColor=U_LightSpecular*U_SpecularMaterial*specularColorIndensity;
}
reflect 计算反射向量复杂度比较高,blin-phong模型就是针对这里进行的一个简化计算。
该模型主要是针对求镜面反射光线向量来简化,如下图
蓝色D为眼睛的位置,然后镜面反射的强度为 反射光纤 与 眼睛和点A的夹角来表示,越小强度越大,因此用向量AD与AC的乘机表示,但反射角计算量大,这里利用 AB 与 AN的和 计算得到 中间的AE, 用 AE乘以AN来代替 AC乘以AD。attenuation为衰减系数,跟上面的点光源的attenuation是一样的。
//reflection
vec3 halfVector=L+viewDir;
halfVector=normalize(halfVector);
specularIntensity=pow(max(0.0,dot(n,halfVector)),128.0);
vec4 specularColor=U_SpecularLightColor*U_SpecularMaterial*specularIntensity*attenuation;
这里给出一个完整的iblind-phone shader的模型,其中利用U_LightPos.w==0.0 来判断是否为平行光还是点光源的。
uniform vec4 U_LightPos;
uniform vec3 U_EyePos;
uniform vec4 U_AmbientLightColor;
uniform vec4 U_AmbientMaterial;
uniform vec4 U_DiffuseLightColor;
uniform vec4 U_DiffuseMaterial;
uniform vec4 U_SpecularLightColor;
uniform vec4 U_SpecularMaterial;
varying vec3 V_Normal;
varying vec3 V_WorldPos;
void main()
{
//ambient
vec4 ambientColor=U_AmbientLightColor*U_AmbientMaterial;
//diffuse
//L vector
vec3 L=vec3(0.0);
float distance=0.0;
float attenuation=1.0;
//light attribute
float constantFactor=0.5;
float linearFactor=0.3;
float expFactor=0.1;
if(U_LightPos.w==0.0)
{
//direction light
L=U_LightPos.xyz;
}
else
{
//model point -> light pos
//point light / spot light
L=U_LightPos.xyz-V_WorldPos;
distance=length(L);
attenuation=1.0/(constantFactor+linearFactor*distance+expFactor*distance*distance);
}
L=normalize(L);
//N vector
vec3 n=normalize(V_Normal);
float diffuseIntensity=max(0.0,dot(L,n));
vec4 diffuseColor=U_DiffuseLightColor*U_DiffuseMaterial*diffuseIntensity*attenuation;
//specular
//inverse view direction : object->eye
vec3 viewDir=U_EyePos-V_WorldPos;
viewDir=normalize(viewDir);
//reflection
vec3 halfVector=L+viewDir;
halfVector=normalize(halfVector);
float specularIntensity=0.0;
if(diffuseIntensity==0.0)
{
specularIntensity=0.0;
}
else
{
//shiness
specularIntensity=pow(max(0.0,dot(n,halfVector)),128.0);
}
vec4 specularColor=U_SpecularLightColor*U_SpecularMaterial*specularIntensity*attenuation;
gl_FragColor=ambientColor+diffuseColor+specularColor;
}