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)