美文网首页
冯氏光照模型

冯氏光照模型

作者: _Felix_ | 来源:发表于2018-06-20 07:19 被阅读0次

    概述

    冯氏光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射 (Diffuse)和镜面(Specular)光照

    • 环境光照(Ambient Lighting):物体几乎永远不会是完全黑暗的。所以环境光照一般是个常量

    • 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响,物体的某一部分越是正对着光源,它就会越亮。

    • 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

    光照公式

    最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色

    • 环境颜色 = 光源的环境光颜色 × 物体的环境材质颜色

    • 漫反射颜色 = 光源的漫反射光颜色 × 物体的漫反射材质颜色 × 漫反射因子


    DiffuseFactor = max(0, dot(N, L))
    • 镜面反射颜色 = 光源的镜面光颜色 × 物体的镜面材质颜色 × 镜面反射因子

    R=reflect(L, N)
    SpecularFactor = power(max(0, dot(R,V)), shininess)

    投光物

    投光物主要有三种:定向光(太阳光)、点光(电灯泡)、聚光(手电筒)

    以下是三种投光物对应的顶点着色器和片段着色器的实现。顶点着色器没有什么差别,它们的不同点体现在片段着色器。

    顶点着色器

    #version 330 core
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 aNormal;
    layout (location = 2) in vec2 aTexCoords;
    
    out vec3 FragPos;
    out vec3 Normal;
    out vec2 TexCoords;
    
    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;
    
    void main()
    {
        FragPos = vec3(model * vec4(aPos, 1.0));
        Normal = mat3(transpose(inverse(model))) * aNormal;  
        TexCoords = aTexCoords;
        
        gl_Position = projection * view * vec4(FragPos, 1.0);
    }
    

    1. 平行光

    片段着色器

    #version 330 core
    out vec4 FragColor;
    
    struct Material {
        sampler2D diffuse;
        sampler2D specular;    
        float shininess;
    }; 
    
    struct Light {
        //vec3 position;
        vec3 direction;
    
        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
    };
    
    in vec3 FragPos;  
    in vec3 Normal;  
    in vec2 TexCoords;
      
    uniform vec3 viewPos;
    uniform Material material;
    uniform Light light;
    
    void main()
    {
        // ambient
        vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
        
        // diffuse 
        vec3 norm = normalize(Normal);
        // vec3 lightDir = normalize(light.position - FragPos);
        vec3 lightDir = normalize(-light.direction);  
        float diff = max(dot(norm, lightDir), 0.0);
        vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;  
        
        // specular
        vec3 viewDir = normalize(viewPos - FragPos);
        vec3 reflectDir = reflect(-lightDir, norm);  
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;  
            
        vec3 result = ambient + diffuse + specular;
        FragColor = vec4(result, 1.0);
    } 
    
    

    二 . 点光

    1. 光的衰减

    随着光线传播距离的增长逐渐削减光的强度通常叫做衰减(Attenuation)。在现实世界中,灯在近处通常会非常亮,但随着距离的增加光源的亮度一开始会下降非常快,但在远处时剩余的光强度就会下降的非常缓慢了

    第一列指定的是光所能覆盖的距离,第二、三、四列分别为 Kc、Kl 、 Kq

    1. 片段着色器
    #version 330 core
    out vec4 FragColor;
    
    struct Material {
        sampler2D diffuse;
        sampler2D specular;    
        float shininess;
    }; 
    
    struct Light {
        vec3 position;  
      
        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
        
        float constant;
        float linear;
        float quadratic;
    };
    
    in vec3 FragPos;  
    in vec3 Normal;  
    in vec2 TexCoords;
      
    uniform vec3 viewPos;
    uniform Material material;
    uniform Light light;
    
    void main()
    {
        // ambient
        vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
        
        // diffuse 
        vec3 norm = normalize(Normal);
        vec3 lightDir = normalize(light.position - FragPos);
        float diff = max(dot(norm, lightDir), 0.0);
        vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;  
        
        // specular
        vec3 viewDir = normalize(viewPos - FragPos);
        vec3 reflectDir = reflect(-lightDir, norm);  
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;  
        
        // attenuation
        float distance    = length(light.position - FragPos);
        float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    
    
        ambient  *= attenuation;  
        diffuse   *= attenuation;
        specular *= attenuation;   
            
        vec3 result = ambient + diffuse + specular;
        FragColor = vec4(result, 1.0);
    } 
    

    三. 聚光

    • LightDir:从片段指向光源的向量。
    • SpotDir:聚光所指向的方向。
    • ϕ:指定了聚光半径的切光角。落在这个角度之外的物体都不会被这个聚光所照亮。
    • θ:LightDir向量和SpotDir向量之间的夹角。在聚光内部的话θθ值应该比ϕϕ值小。
    1. 优化:平滑/软化边缘

    需要模拟聚光有一个内圆锥(Inner Cone)和一个外圆锥(Outer Cone)
    计算intensity ,在内圆锥与外圆锥之间平滑插值,内圆锥之内恒为1,外圆锥之外恒为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);    
    ...
    // 将不对环境光做出影响,让它总是能有一点光
    diffuse  *= intensity;
    specular *= intensity;
    ...
    
    
    1. 片段着色器
    #version 330 core
    out vec4 FragColor;
    
    struct Material {
        sampler2D diffuse;
        sampler2D specular;    
        float shininess;
    }; 
    
    struct Light {
        vec3 position;  
        vec3 direction;
        float cutOff;
        float outerCutOff;
      
        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
        
        float constant;
        float linear;
        float quadratic;
    };
    
    in vec3 FragPos;  
    in vec3 Normal;  
    in vec2 TexCoords;
      
    uniform vec3 viewPos;
    uniform Material material;
    uniform Light light;
    
    void main()
    {
        // ambient
        vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
        
        // diffuse 
        vec3 norm = normalize(Normal);
        vec3 lightDir = normalize(light.position - FragPos);
        float diff = max(dot(norm, lightDir), 0.0);
        vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;  
        
        // specular
        vec3 viewDir = normalize(viewPos - FragPos);
        vec3 reflectDir = reflect(-lightDir, norm);  
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;  
        
        // spotlight (soft edges)
        float theta = dot(lightDir, normalize(-light.direction)); 
        float epsilon = (light.cutOff - light.outerCutOff);
        float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
        diffuse  *= intensity;
        specular *= intensity;
        
        // attenuation
        float distance    = length(light.position - FragPos);
        float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    
        ambient  *= attenuation; 
        diffuse   *= attenuation;
        specular *= attenuation;   
            
        vec3 result = ambient + diffuse + specular;
        FragColor = vec4(result, 1.0);
    } 
    

    四. 多光源

    多光源指的是多个投光物同时存在于同一场景,这时对片段颜色的计算即是对多个投光物计算结果的累计叠加

    #version 330 core
    out vec4 FragColor;
    
    struct Material {
        sampler2D diffuse;
        sampler2D specular;
        float shininess;
    }; 
    
    struct DirLight {
        vec3 direction;
        
        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
    };
    
    struct PointLight {
        vec3 position;
        
        float constant;
        float linear;
        float quadratic;
        
        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
    };
    
    struct SpotLight {
        vec3 position;
        vec3 direction;
        float cutOff;
        float outerCutOff;
      
        float constant;
        float linear;
        float quadratic;
      
        vec3 ambient;
        vec3 diffuse;
        vec3 specular;       
    };
    
    #define NR_POINT_LIGHTS 4
    
    in vec3 FragPos;
    in vec3 Normal;
    in vec2 TexCoords;
    
    uniform vec3 viewPos;
    uniform DirLight dirLight;
    uniform PointLight pointLights[NR_POINT_LIGHTS];
    uniform SpotLight spotLight;
    uniform Material material;
    
    // function prototypes
    vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
    vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
    vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
    
    void main()
    {    
        // properties
        vec3 norm = normalize(Normal);
        vec3 viewDir = normalize(viewPos - FragPos);
        
        // == =====================================================
        // Our lighting is set up in 3 phases: directional, point lights and an optional flashlight
        // For each phase, a calculate function is defined that calculates the corresponding color
        // per lamp. In the main() function we take all the calculated colors and sum them up for
        // this fragment's final color.
        // == =====================================================
        // phase 1: directional lighting
        vec3 result = CalcDirLight(dirLight, norm, viewDir);
        // phase 2: point lights
        for(int i = 0; i < NR_POINT_LIGHTS; i++)
            result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);    
        // phase 3: spot light
        result += CalcSpotLight(spotLight, norm, FragPos, viewDir);    
        
        FragColor = vec4(result, 1.0);
    }
    
    // calculates the color when using a directional light.
    vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
    {
        vec3 lightDir = normalize(-light.direction);
        // diffuse shading
        float diff = max(dot(normal, lightDir), 0.0);
        // specular shading
        vec3 reflectDir = reflect(-lightDir, normal);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        // combine results
        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);
    }
    
    // calculates the color when using a point light.
    vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
    {
        vec3 lightDir = normalize(light.position - fragPos);
        // diffuse shading
        float diff = max(dot(normal, lightDir), 0.0);
        // specular shading
        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 results
        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);
    }
    
    // calculates the color when using a spot light.
    vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
    {
        vec3 lightDir = normalize(light.position - fragPos);
        // diffuse shading
        float diff = max(dot(normal, lightDir), 0.0);
        // specular shading
        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 results
        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);
    }
    

    参考资料:
    https://learnopengl-cn.github.io/02%20Lighting/05%20Light%20casters/
    https://learnopengl-cn.github.io/02%20Lighting/06%20Multiple%20lights/

    相关文章

      网友评论

          本文标题:冯氏光照模型

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