6 - 多光源
本文将会对各种各样的光源进行一个综合使用,将会创建一个包含六个光源的场景:一个类似于太阳的定向光、四个分散在场景中的点光源,以及一个手电筒聚光。
多光源
为了在场景中使用多个光源,可以将每类光源的计算分别分装到GLSL函数中,最后把它们加起来。
定向光
定向光结构体如下:
struct DriectionalLight
{
// 方向:光源 -> 着色点
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform DriectionalLight dirLight;
然后定义一个计算定向光的函数:
vec3 calcDirLight(DriectionalLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(-light.direction);
vec3 half = normalize(viewDir + lightDir);
// 环境光
vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
// 漫反射
vec3 diffuse = light.diffuse * texture(material.diffuse, TexCoords).rgb * max(0.0, dot(normal, lightDir));
// 高光项
vec3 specular = light.specular * texture(material.specular, TexCoords).rgb * pow(max(0.0, dot(normal, half)), material.shininess);
return (ambient + diffuse + specular);
}
点光源
点光源结构体如下:
struct PointLight
{
vec3 position;
float constant;
float linear;
float quadratic;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];
然后定义一个计算点光源的函数:
vec3 calcPointLight(PointLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - FragPos);
vec3 half = normalize(viewDir + lightDir);
// 环境光
vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
// 漫反射
vec3 diffuse = light.diffuse * texture(material.diffuse, TexCoords).rgb * max(0.0, dot(normal, lightDir));
// 镜面光
vec3 specular = light.specular * texture(material.specular, TexCoords).rgb * pow(max(0.0, dot(normal, half)), material.shininess);
// 衰减计算
float distance = length(light.position - FragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
return (ambient + diffuse + specular) * attenuation;
}
聚光
聚光结构体如下:
struct SpotLight
{
vec3 position;
vec3 direction;
float cutOff;
float outerCutOff;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
uniform SpotLight spotLight;
然后定义一个计算聚光的函数:
vec3 calcSpotLight(SpotLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - FragPos);
vec3 half = normalize(viewDir + lightDir);
vec3 ambient, diffuse, specular;
ambient = texture(texture_diffuse1, TexCoords).rgb * light.ambient;
diffuse = texture(texture_diffuse1, TexCoords).rgb * light.diffuse * max(0.0, dot(normal, lightDir));
specular = texture(texture_specular1, TexCoords).rgb * light.specular * pow(max(0.0, dot(normal, half)), 32.0);
// 聚光相关计算
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
// 距离衰减
float dist = length(light.position - FragPos);
float attenuation = 1.0 / (1.0 + light.linear * dist + light.quadratic * (dist * dist));
return (ambient + diffuse + specular) * intensity * attenuation;
}
综合
接下来将它们综合到一起,首先修改片段着色器的main函数:
void main()
{
vec3 norm = normalize(Normal);
vec3 viewDir = normalize(viewPos - FragPos);
// 定向光
vec3 result = calcDirLight(dirLight, norm, viewDir);
// 点光源
for (int i = 0; i < NR_POINT_LIGHTS; ++i)
result += calcPointLight(pointLights[i], norm, viewDir);
// 聚光
result += calcSpotLight(spotLight, norm, viewDir);
FragColor = vec4(result, 1.0);
}
然后添加数据,这里选择Factory数据,结果如下:
参考资料
- 俺仿写的代码。
- 多光源 - LearnOpenGL CN (learnopengl-cn.github.io)