LearnOpenGL笔记-多光源

多光源

之前的章节中我们学习了如何设置定向光、点光源以及聚光效果。而为了在场景中使用多个光源,我们就需要在GLSL中对不同种类似光照计算进行封装。若不进行封装,当场景中有多个光源时,每一种光源都需要一种不同的光照计算,就会使代码变得极为复杂和难读。

GLSL函数语法与C很像,需要注意的几点是若函数不是在main函数之前声明的,则必须要在代码顶部声明一个原型。

简单来说我们只是需要一个输出的片段颜色,因此我们将场景内所有光源对某一片段作用相加,就可以得到该片段最终的输出值。所以我们会用不同的函数来对不同的光照类型进行计算,并在main函数中进行结合输出。

out vec4 FragColor;

void main()
{
    //定向光计算
    vec3 result = CalcDirLight(...);
    //点光源计算
    result += CalcPointLight(...);
    //聚光计算
    result += CalcSpotLight(...);

    FragColor = vec4(result,1.0);
} 

定向光

我们在片段着色器中定义一个函数CalcDirLight用来计算定向光照,与前几章中的定向光计算无异。

因为不同类型光照需要不同的参数,所以先定义一个定向光结构体,并设置uniform。

struct DirLight {
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 reflection;
};  
uniform DirLight dirLight;

接下来定义一下CalcDirLight原型函数

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);

在main函数后面实现CalcDirLight函数(若在main函数之前,则可不用声明原型,与C类似)。

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    // 漫反射着色
    float diff = max(dot(normal, lightDir), 0.0);
    // 镜面光着色
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // 合并结果
    vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    return (ambient + diffuse + specular);
}

实现步骤与之前无异,最终返回的是定向光对片段颜色值的贡献。

点光源

同样,定义一个点光源结构体,并设置uniform。

struct PointLight {
    vec3 position;

    float constant;
    float linear;
    float quadratic;

    vec3 ambient;
    vec3 diffuse;
    vec3 reflection;
};  
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];

这里我们想要使用多个点光源,在GLSL中使用了预处理指令来定义了我们场景中点光源的数量。接着我们使用了这个NR_POINT_LIGHTS常量来创建了一个PointLight结构体的数组。GLSL中的数组和C数组一样,可以使用一对方括号来创建。现在我们有四个待填充数据的PointLight结构体。

定义CalcPointLight原型函数

vec3 CalcPointLight(PointLight light,vec3 normal,vec3 viewDir,vec3 FragPos);

实现CalcPointLight函数

vec3 CalcPointLight(PointLight light,vec3 normal,vec3 viewDir,vec3 FragPos)
{
    vec3 lightDir = normalize(light.position - FragPos);
    //漫反射
    float diff = max(dot(lightDir,normal),0.0);
    //镜面反射
    vec3 reflectDir = reflect(-lightDir,normal);
    float ref = pow(max(dot(viewDir,reflectDir),0.0),material.shininess);

    vec3 ambient = light.ambient * vec3(texture(material.diffuse,TexCoords));
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse,TexCoords));
    vec3 reflection = light.reflection * ref * vec3(texture(material.reflection,TexCoords));
    //衰减
    float distance = length(light.position - FragPos);
    float attenuation = 1.0/(light.constant + light.linear * distance + light.quadratic * (distance * distance));

    ambient *= attenuation;
    diffuse *= attenuation;
    reflection *= attenuation;

    return (ambient + diffuse + reflection);
}

聚光

定义一个聚光结构体,并设置uniform。

struct SpotLight
{
    vec3 position;
    vec3 direction;
    float cutOff;
    float outerCutOff;
  
    float constant;
    float linear;
    float quadratic;
  
    vec3 ambient;
    vec3 diffuse;
    vec3 reflection;  
};
uniform SpotLight spotLight;

原型声明

vec3 CalcSpotLight(SpotLight light,vec3 normal,vec3 viewDir,vec3 FragPos);

实现

vec3 CalcSpotLight(SpotLight light,vec3 normal,vec3 viewDir,vec3 FragPos)
{
    vec3 lightDir = normalize(light.position - FragPos);
    //漫反射
    float diff = max(dot(lightDir,normal),0.0);
    //镜面反射
    vec3 reflectDir = reflect(-lightDir,normal);
    float ref = pow(max(dot(viewDir,reflectDir),0.0),material.shininess);

    vec3 ambient = light.ambient * vec3(texture(material.diffuse,TexCoords));
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse,TexCoords));
    vec3 reflection = light.reflection * ref * vec3(texture(material.reflection,TexCoords));
    //衰减
    float distance = length(light.position - FragPos);
    float attenuation = 1.0/(light.constant + light.linear * distance + light.quadratic * (distance * distance));
    //平滑
    float theta = dot(lightDir,normalize(-light.direction));
    float epsilon = light.cutOff - light.outerCutOff;
    float intensity = clamp((theta - light.outerCutOff)/epsilon ,0.0,1.0);
    
    ambient *= attenuation * intensity;
    diffuse *= attenuation * intensity;
    reflection *= attenuation * intensity;

    return (ambient + diffuse + reflection);
}

合并结果

由于我们有多个点光源,所以首先定义各个点光源的位置数组。

glm::vec3 pointLightPositions[] = {
    glm::vec3( 0.7f,  0.2f,  2.0f),
    glm::vec3( 2.3f, -3.3f, -4.0f),
    glm::vec3(-4.0f,  2.0f, -12.0f),
    glm::vec3( 0.0f,  0.0f, -3.0f)
};

接下来我们可以通过下标来对各个点光源的uniform进行设置

    lightingShader.setVec3("pointLights[0].position", pointLightPositions[0]);
    lightingShader.setVec3("pointLights[0].ambient", 0.05f, 0.05f, 0.05f);
    lightingShader.setVec3("pointLights[0].diffuse", 0.8f, 0.8f, 0.8f);
    lightingShader.setVec3("pointLights[0].reflection", 1.0f, 1.0f, 1.0f);
    lightingShader.setFloat("pointLights[0].constant", 1.0f);
    lightingShader.setFloat("pointLights[0].linear", 0.09f);
    lightingShader.setFloat("pointLights[0].quadratic", 0.032f);

其他几个点光源也是同样的操作。

在着色器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,FragPos);
    }
    //聚光计算
    result += CalcSpotLight(spotLight,norm,viewDir,FragPos);

    FragColor = vec4(result,1.0);
} 

最后,我们使用了多个光源立方体,因此,我们需要对不同的光源立方体进行模型变换,变换到不同的位置上去。

lightCubeShader.use();
        lightCubeShader.setMat4("projection", projection);
        lightCubeShader.setMat4("view", view);

        glBindVertexArray(lightCubeVAO);
        for (unsigned int i = 0;i < 4;i++)
        {
            glm::mat4 model = glm::mat4(1.0f);
            model = glm::translate(model, pointLightPositions[i]);
            model = glm::scale(model, glm::vec3(0.2f));
            lightCubeShader.setMat4("model", model);

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

效果如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值