美文网首页
OpenGL学习13——多光源

OpenGL学习13——多光源

作者: 蓬篙人 | 来源:发表于2021-06-27 15:43 被阅读0次

多光源(Multiple Lights)

前面我们学习一些关于光照的知识,这一章节我们结合所获得的知识来创建一个包含6个光源的场景,分别是一个模拟太阳光的定向光源,4个分散在场景中的点光源和一个聚光灯光源。

  • 当我们在片元着色器中进行多光源场景下片元颜色的计算,我们最好将不同类型的光源对颜色的影响封装到不同的GLSL函数中,避免代码太过混乱。GLSL函数与C函数相似,我们需要一个函数名和一个返回类型,且如果函数在主函数后面定义我们需要声明一个函数原型。引入GLSL函数后,片元着色器的大致结构如下所示:
out vec4 FragColor;

void main()
{
    // 定义一个输出颜色
    vec3 output = vec3(0.0);
    // 添加定向光源的颜色贡献
    output += someFunctionToCaculateDirectionalLight();
    // 添加点光源的颜色贡献
    for(int i = 0; i < nr_of_point_lights; i++)
    {
        output += someFunctionToCaculatePointLight();
    }
    // 添加聚光灯光源的颜色贡献(如聚光灯)
    output += someFunctionToCaculateSpotlight();

    FragColor = vec4(output, 1.0);
}

1. 定向光源

  • 1.1 我们首先将计算定向光源的参数定义到一个结构体内,并声明一个uniform变量来接收参数值。
struct DirLight
{
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform DirLight dirLight;
  • 1.2 声明计算定向光源函数的原型。
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
  • 1.3 实现计算定向光源的函数。
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    // diffuse
    float diff = max(dot(normal, lightDir), 0.0);
    // specular
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // combine
    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);
}

2. 点光源

  • 2.1 定义点光源参数结构体。
struct PointLight
{
    vec3 position;

    float constant;
    float linear;
    float quadratic;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
  • 2.2 因为我们的场景中包含四个点光源,因此我们先使用预处理指令定义一个常量,然后声明一个上述结构类型的uniform变量数组。
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];
  • 2.3 声明点光源函数原型。
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
  • 2.4 实现点光源计算函数。
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    // diffuse
    float diff = max(dot(normal, lightDir), 0.0);
    // specular
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // attenuation
    float distance = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    // combine
    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));
    ambient *= attenuation;
    diffuse *= attenuation;
    specular *= attenuation;

    return (ambient + diffuse + specular);
}

3. 聚光灯光源

  • 3.1 定义聚光灯光源结构体,并声明uniform变量。
struct SpotLight
{
    vec3 position;
    vec3 direction;
    float cutOff;
    float outerCutOff;

    float constant;
    float linear;
    float quadratic;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform SpotLight spotLight;
  • 3.2 声明聚光灯光源函数原型。
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
  • 3.3 实现聚光灯光源计算函数。
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    // diffuse
    float diff = max(dot(normal, lightDir), 0.0);
    // specular
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // attenuation
    float distance = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    // spotlight intensity
    float theta = dot(lightDir, normalize(-light.direction));
    float epsilon = light.cutOff - light.outerCutOff;
    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); 
    // combine
    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));
    ambient *= attenuation * intensity;
    diffuse *= attenuation * intensity;
    specular *= attenuation * intensity;

    return (ambient + diffuse + specular);
}

4. 组合到一起

  • 将上述3种类型光源的计算组合到主函数中,计算出片元最终的颜色。
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, FragPos, viewDir);
    }

    result += CalcSpotLight(spotLight, norm, FragPos, viewDir);
    
    FragColor = vec4(result, 1.0);
}
  • 注意:上述光源的计算函数存在很多重复计算(因为基本是参照前面章节的计算过程进行封装),如反射矢量,扩散光和镜面光分量和纹理采样等的计算,因此后续我们可以对这些进行优化。
  • 前面我们定义了一个点光源结构数组,数组元素值的设置与普通变量相似。
objectShader.setFloat("pointLights[0].constant", 1.0f);
  • 对于四个电源,我们需要定义四个位置,并使用模型矩阵将其放置到空间中的不同位置。下面是四个点光源的位置数组。
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)
};
  • 场景的渲染效果。


    多光源场景1
  • 调整光源参数后的一些渲染效果。


    多光源场景2
多光源场景3 多光源场景4 多光源场景5

相关文章

  • OpenGL学习13——多光源

    多光源(Multiple Lights) 前面我们学习一些关于光照的知识,这一章节我们结合所获得的知识来创建一个包...

  • OpenGL ES for Android多光源

    在学习过光照,材质,投光物之后,现在我们把这些效果结合起来。我们显示这样的效果:在阳光照射下,有几个点光源,同时有...

  • 从0开始的OpenGL学习(十五)-多光源

    本文主要解决一个问题: 如何在场景中实现多个光源? 引言 在之前的文章中,我们学了很多OpenGL中的光照知识,包...

  • 重新自学学习openGL 之光照多光源

    其实前面我们已经将光照的基本知识学习完毕了.本章主要学习的是如何在shader中使用多个光源对物体进行渲染. 本章...

  • OpenGL光源光照基础

    光照模型 在OpenGL光照模型中,除非一个物体自己会发光,否则它将受到3种不同类型的光的照射:环境光(ambie...

  • 从0开始的OpenGL学习(十四)-3中光源模型

    本文主要解决一个问题: 如何在OpenGL中模拟三种光源类型? 引言 之前的文章中,我们把光源定义成空间中的一点。...

  • LearnOpenGL 多光源

    我们在前面的教程中已经学习了许多关于OpenGL 光照的知识,其中包括冯氏照明模型(Phong shading)、...

  • OpenGL 固定管线下的着色器

    OpenGL 固定管线下为开发者提供了几种着色器:单元着色器、平面着色器、上色着色器、默认光源着色器、点光源着色器...

  • 阴影渲染

    shadowmap原理,基于平行光,openGL中 1 计算相机在光源位置的帧缓存(深度缓存) 2.1 计算每个顶...

  • OpenGL一些基本理解和概念的学习

    OpenGL学习大致的理解 OpenGL为什么会涉及这么多操作顺序。这是因为,和我们现在使用的C++、JAVA这种...

网友评论

      本文标题:OpenGL学习13——多光源

      本文链接:https://www.haomeiwen.com/subject/pdsxultx.html