平行光
平行光(Directional Light)给定一个固定的光源方向direction,因为光照计算需要从片段至光源的光线方向,所有需要取反。
struct DirLight
{
vec3 color;
vec3 direction; //光照方向
vec3 ambient; //环境光
vec3 diffuse; //漫反射
vec3 specular; //高光反射
};
//计算平行光
vec3 CalcDirLight(DirLight light,vec3 normal, vec3 viewDir)
{
vec4 diffuseTex = texture(material.diffuse, TexCoords);
vec4 specularTex = texture(material.specular, TexCoords);
vec3 lightDir = normalize(-light.direction); //光照计算需求一个从片段至光源的光线方向,所以取反
vec3 reflectDir = reflect(-lightDir, normal);
//环境光
vec3 ambient = light.ambient * vec3(diffuseTex) * light.color;
//漫反射
float diff = max(dot(normal, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * vec3(diffuseTex)* light.color;
//高光反射
float specularShininess = 32;
float spec = pow(max(dot(viewDir,reflectDir),0.0),specularShininess);
vec3 specular = light.specular * spec * vec3(specularTex)* light.color;
return ambient + diffuse + specular;
}
点光源
点光源(Point Light)需要一个随着光线传播距离的增长逐渐削减光的强度衰减(attenuation)。
在这里d
代表了片段距光源的距离。常数项Kc
、一次项Kl
和二次项Kq
。
- 常数项通常保持为1.0,它的主要作用是保证分母永远不会比1小,否则的话在某些距离上它反而会增加强度,这肯定不是我们想要的效果。
- 一次项会与距离值相乘,以线性的方式减少强度。
- 二次项会与距离的平方相乘,让光源以二次递减的方式减少强度。二次项在距离比较小的时候影响会比一次项小很多,但当距离值比较大的时候它就会比一次项更大了。
光在近距离的时候有着最高的强度,但随着距离增长,它的强度明显减弱,并缓慢地在距离大约100的时候强度接近0。
距离 | 常数项 | 一次项 | 二次项 |
---|---|---|---|
7 | 1.0 | 0.7 | 1.8 |
13 | 1.0 | 0.35 | 0.44 |
20 | 1.0 | 0.22 | 0.20 |
32 | 1.0 | 0.14 | 0.07 |
50 | 1.0 | 0.09 | 0.032 |
65 | 1.0 | 0.07 | 0.017 |
100 | 1.0 | 0.045 | 0.0075 |
160 | 1.0 | 0.027 | 0.0028 |
200 | 1.0 | 0.022 | 0.0019 |
325 | 1.0 | 0.014 | 0.0007 |
600 | 1.0 | 0.007 | 0.0002 |
3250 | 1.0 | 0.0014 | 0.000007 |
struct PointLight
{
vec3 color;
vec3 position; //光源位置
float constant; //衰减常数
float linear; //衰减一次常数
float quadratic; //衰减二次常数
vec3 ambient; //环境光
vec3 diffuse; //漫反射
vec3 specular; //高光反射
};
//计算点光
vec3 CalcPointLight(PointLight light,vec3 normal, vec3 viewDir)
{
float distance = length(light.position - WorldPos);
//衰减值
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
vec4 diffuseTex = texture(material.diffuse, TexCoords);
vec4 specularTex = texture(material.specular, TexCoords);
vec3 lightDir = normalize(light.position - WorldPos);
vec3 reflectDir = reflect(-lightDir, normal);
//环境光
vec3 ambient = light.ambient * vec3(diffuseTex)* light.color;
//漫反射
float diff = max(dot(normal, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * vec3(diffuseTex)* light.color;
//高光反射
float specularShininess = 32;
float spec = pow(max(dot(viewDir,reflectDir),0.0),specularShininess);
vec3 specular = light.specular * spec * vec3(specularTex)* light.color;
//点光源衰减值
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
return ambient + diffuse + specular;
}
聚光
聚光(Spotlight)是位于环境中某个位置的光源,它只朝一个特定方向而不是所有方向照射光线。
LightDir
:从片段指向光源的向量。SpotDir
:聚光所指向的方向。Phi
ϕ:指定了聚光半径的切光角。落在这个角度之外的物体都不会被这个聚光所照亮。Theta
θ:LightDir向量和SpotDir向量之间的夹角。在聚光内部的话θ值应该比ϕ值小。
平滑/软化边缘
模拟聚光有一个内圆锥(Inner Cone)和一个外圆锥(Outer Cone)。
这里ϵ
(Epsilon)是内(ϕ
)和外圆锥(γ
)之间的余弦值差(ϵ=ϕ−γ
)。最终的I
值就是在当前片段聚光的强度。
struct SpotLight
{
vec3 color;
vec3 position; //光源位置
vec3 direction; //光照方向
float cutOff; //内切光角度
float outerCutOff; //外切光角度
vec3 ambient; //环境光
vec3 diffuse; //漫反射
vec3 specular; //高光反射
};
//计算聚光
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 viewDir)
{
vec4 diffuseTex = texture(material.diffuse, TexCoords);
vec4 specularTex = texture(material.specular, TexCoords);
vec3 lightDir = normalize(light.position - WorldPos);
vec3 reflectDir = reflect(-lightDir, normal);
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
//聚光强度
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
//环境光
vec3 ambient = light.ambient * vec3(diffuseTex)* light.color;
//漫反射
float diff = max(dot(normal, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * vec3(diffuseTex)* light.color;
//高光反射
float specularShininess = 32;
float spec = pow(max(dot(viewDir,reflectDir),0.0),specularShininess);
vec3 specular = light.specular * spec * vec3(specularTex) * light.color;
//聚光灯软化边缘
diffuse *= intensity;
specular *= intensity;
ambient *= intensity;
return ambient + diffuse + specular;
}
所有光源加起来
Comments NOTHING