平行光

平行光(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。

距离常数项一次项二次项
71.00.71.8
131.00.350.44
201.00.220.20
321.00.140.07
501.00.090.032
651.00.070.017
1001.00.0450.0075
1601.00.0270.0028
2001.00.0220.0019
3251.00.0140.0007
6001.00.0070.0002
32501.00.00140.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; 
}

所有光源加起来

最后更新于 2022-08-01