精华内容
下载资源
问答
  • 版权 ... 先看phong光照模型中一段顶点着色器的代码 #version 330 core out vec4 FragColor; in vec3 Normal; //法相向量 in vec3 FragPos; // uniform vec3 lightPos; uniform vec3 viewPos; u...

    版权

    cesuolidec4

    https://blog.csdn.net/xiewenzhao123/article/details/54600191

    引言

            现实世界的光照是极其复杂的,而且会受到诸多因素的影响,这是以目前我们所拥有的处理能力无法模拟的。因此OpenGL的光照仅仅使用了简化的模型并基于对现实的估计来进行模拟,这样处理起来会更容易一些,而且看起来也差不多一样。这些光照模型都是基于我们对光的物理特性的理解。其中一个模型被称为冯氏光照模型(Phong Lighting Model)。冯氏光照模型的主要结构由3个元素组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。这些光照元素看起来像下面这样:

                 先看phong光照模型中一段顶点着色器的代码

    #version 330 core
    out vec4 FragColor;
    
    in vec3 Normal;  //法相向量
    in vec3 FragPos; //
    
    uniform vec3 lightPos;
    uniform vec3 viewPos;
    uniform vec3 lightColor;
    uniform vec3 objectColor;
    
    void main()
    {
    	//ambient环境光
    	float ambientStrength = 0.1f;   //环境光照强度
    	vec3 ambient = ambientStrength * lightColor;
    
    	//diffuse漫反射光
    	vec3 norm =	normalize(Normal);
    	vec3 lightDir = normalize(lightPos - FragPos); //光的方向向量是光的位置向量与片段的位置向量之间的向量差
    	float diff = max(dot(norm, lightDir), 0.0f);
    	vec3 diffuse = diff * lightColor;
    
    	//specular镜面反射光
    	float specularStrength = 0.9;
    	vec3 viewDir = normalize(viewPos - FragPos);
    	vec3 reflectDir = reflect(-lightDir, norm);
    	float spec = pow(max(dot(viewDir, reflectDir), 0.0f), 32);
    	vec3 specular = specularStrength * spec * lightColor;
    
    	vec3 result = (ambient + diffuse + specular) * objectColor;
    	FragColor = vec4(result, 1.0f);
    
    }

    OpenGL光照模型总结

    一、 冯氏光照
    所谓的冯氏光照分为三大部分:环境光照、漫反射光照以及镜面反射光照。对于同一个物体来讲,当有多个光源发出光线照亮该物体时,都可以将其划分成上述三种情况分别进行计算,最后将三部分组合即为其中一个光源照射物体后得到的最终结果,再将每个光源的照射结果进行叠加即为复杂照射环境下的最终结果。
    1、 环境光照:
    这是冯氏光照中最简单的光照,只需要用环境光向量乘上物体本身的颜色即可,要注意的是环境光往往比较弱,所以环境光向量要设置的比较小。

    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

    解释:
    light.ambient为环境光线,material.diffuse为漫反射光照贴图(因为大部分情况下环境光贴图与漫反射贴图相同),TexCoords为纹理坐标,texture函数会在纹理坐标内生成一些插值坐标,用来采样纹理图片其他地方的颜色。
    2、 漫反射光照:
    这里写图片描述
    如图可知,计算漫反射光照最重要的就是计算光线向量与法向量的夹角角度。法向量可以通过叉乘获得。而光线向量要通过光源位置向量与片段位置向量相减获得。

    vec3 lightDir = normalize(light.position - fragPos);
    float diff = max(dot(normal, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

    解释:
    LightDir为光线向量,normalize是向量标准化函数,将向量变为长度为1的单位向量。Diff为法向量与光线向量夹角,注意该夹角不能小于0,normal是法向量我们一般可以通过数据输入得到。
    重要注意点:
    当模型发生不均匀缩放是会破坏法向量导致光线扭曲。
    解决方法:使用正规矩阵(模型矩阵左上角的逆矩阵的转置矩阵)
    得到如下顶点着色器代码:


    3、 镜面反射光照
    这里写图片描述
    计算该光照需要以下几个数据:光源位置,片段位置,法向量,观察者位置。
    光源位置和片段位置能够求出光线向量,通过光线向量与法向量能够计算出反射光线向量,观察者位置和片段位置能够计算出观察者向量,观察者向量与反射光线向量能够求出角度,该角度即为参数spec,用来计算镜面反射光线向量。

    vec3 lightDir = normalize(light.position - fragPos);
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    

    viewDir为观察者向量。
    reflectDir是反射光线向量,reflect是求反射向量的函数其中第一个参数是入射光线,它必须是从光源出发,所以lightDir要取反。
    Material.shininess是发光值一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。往往设置为32。
    4、 光照结果:

    vec3 result = ambient + diffuse + specular;
    color = vec4(result, 1.0f);
    

    二、 投光物
    1、 定向光
    当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光(Directional Light),因为所有的光线都有着同一个方向;它会独立于光源的位置。
    这里写图片描述

    struct DirLight 
    {
        vec3 direction;//定向光的光线向量(定向光的光线向量不发生改变)
        vec3 ambient;//环境光向量
        vec3 diffuse;//漫反射光线向量
        vec3 specular;//镜面反射光线向量
    };
    uniform DirLight dirLight;
    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);
    }
    

    2、 点光源
    点光是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。想象灯泡和火炬作为投光物,它们可以扮演点光的角色。
    这里写图片描述
    重要特性——衰减:
    随着光线穿越距离的变远使得亮度也相应地减少的现象,通常称之为衰减(Attenuation)。
    衰减方程:
    这里写图片描述
    在这里d代表片段到光源的距离。为了计算衰减值,我们定义3个(可配置)项:常数项Kc,一次项Kl和二次项Kq。

    struct PointLight 
    {
        vec3 position;//点光源位置
        float constant;//衰减公式常数项
        float linear;//衰减公式一次项系数
        float quadratic;//衰减公式二次项系数
        vec3 ambient;//环境光向量
        vec3 diffuse;//漫反射向量
        vec3 specular;//镜面反射向量
    };
    vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
    {
        vec3 lightDir = normalize(light.position - fragPos);
        // 计算漫反射系数
        float diff = max(dot(normal, lightDir), 0.0);
        // 计算镜面反射系数
        vec3 reflectDir = reflect(-lightDir, normal);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        // 计算衰减系数
        float distance = length(light.position - fragPos);
        float attenuation = 1.0f / (light.constant + light.linear * distance +light.quadratic * (distance * distance));    
        // 计算各个光照分量向量
        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、 聚光光源
    聚光是一种位于环境中某处的光源,它不是向所有方向照射,而是只朝某个方向照射。结果是只有一个聚光照射方向的确定半径内的物体才会被照亮,其他的都保持黑暗。聚光的好例子是路灯或手电筒。
    这里写图片描述
    • LightDir:从片段指向光源的向量。
    • SpotDir:聚光所指向的方向。
    • Phiϕ:定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。
    • Thetaθ:LightDir向量和SpotDir向量之间的角度。θ值应该比Φ值小,这样才会在聚光内。
    重要特性——平滑/软化边缘
    为创建聚光的平滑边,我们希望去模拟的聚光有一个内圆锥和外圆锥。我们可以把内圆锥设置为前面部分定义的圆锥,我们希望外圆锥从内边到外边逐步的变暗。
    为创建外圆锥,我们简单定义另一个余弦值,它代表聚光的方向向量和外圆锥的向量(等于它的半径)的角度。然后,如果片段在内圆锥和外圆锥之间,就会给它计算出一个0.0到1.0之间的亮度。如果片段在内圆锥以内这个亮度就等于1.0,如果在外面就是0.0。
    这里写图片描述
    这里ϵ是内部圆锥(ϕ)和外部圆锥(γ)(epsilon = phi - gamma)的差。结果I的值是聚光在当前片段的亮度。

    struct SpotLight
    {
        vec3 position;//光源位置
        vec3 direction;//光的方向,即上图的SpotDir
        float cutOff;//内圆锥切角
        float outerCutOff;//外圆锥切角
    
        float constant;
        float linear;
        float quadratic;
    
        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
    };
    vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
    {
        vec3 lightDir = normalize(light.position - fragPos);
        // 计算漫反射系数
        float diff = max(dot(normal, lightDir), 0.0);
        // 镜面反射系数
        vec3 reflectDir = reflect(-lightDir, normal);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        // 光线衰减
        float distance = length(light.position - fragPos);
        float attenuation = 1.0f / (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);
        // 计算各个光照分量答案
        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);
    }
    

    三、 完整的顶点着色器和片段着色器
    1、 Vertexshader:

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

    2、 Fragmentshader

    #version 330 core
    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;
    
    out vec4 color;
    
    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);    
    
        color = 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.0f / (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);
    }
    
    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.0f / (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);
    }

    举个栗子,程序

    运行效果

    工程源码 

    参考

    cesuolidec4

    openGL-phong光照模型很好的总结

    learnopengl CN

    展开全文
  • 光照模型(lighting model)就是试图描述光源发出的光线和场景中的物体是如何相互作用。现实世界的光照过程是无法精确模拟,需要计算机近似处理。OpenGL把现实世界的光照系统近似分为三个部分:光源、材质和光照环境...

    光照模型

      如果我们希望渲染出的物体像是处于有若干光源照明的场景中。根据物体表面性质和光源的位置,每个物体不同的部分反射光的多少也不同。光照模型(lighting model)就是试图描述光源发出的光线和场景中的物体是如何相互作用。现实世界的光照过程是无法精确模拟,需要计算机近似处理。OpenGL把现实世界的光照系统近似分为三个部分:光源、材质和光照环境。

    光源

      OpenGL提供了许多函数用来设置光源和物体表面材质属性。理解并掌握其中所有的变化和细节会比较乏味,所以我们在这里只介绍基本的用法。OpenGL允许定义八个光源,依次用GL_LIGHT0、GL_LIGHT1等命名。每个光源可以设置不同的属性,也可以被开启或关闭。每个属性都有默认的赋值。

    光源位置和类型

      用于指定光源位置的OpenGL符号属性常数是 GL_POSITION。实际上,这个符号常数用来同时设置光源的两个特性:光源位置和光源类型。OpenGL中用于照明场景的光源有两个基本类:方向光源和位置光源。光源位置和光源类型用有一个四元浮点数向量来指定,如果该向量的第四个元素为0.0, 则该光源是方向光源(如light2PosType)。该光源是非常远的光源,发出的光仅允许向一个方向发射,该方向应用到场景中的所有表面,与指定的光源位置无关。该方向按从光源位置到坐标原点的方向计算。如果该向量的第四个元素为1.0, 则该光源是位置光源(如light1PosType)。该光源是局部光源或近光源,发出的光向所有方向辐射,该光源位置被光照子程序用来确定场景中每个对象的光照方向。

    GLfloat light1PosType[] = {2.0, 0.0, 3.0, 1.0};
    GLfloat light2PosType[] = {0.0, 1.0, 0.0, 0.0};
    glLightfv(GL_LIGHT1, GL_POSITION, light1PosType);
    glEnable(GL_LIGHT1);
    
    glLightfv(GL_LIGHT2, GL_POSITION, light2PosType);
    glEnable(GL_LIGHT2);

    光源颜色

      OpenGL允许为一个光源发出的三种不同光——环境光、漫反射和镜面反射,设置不同的颜色。我们使用符号颜色特性常量GL_AMBIENT、GL_DIFFUSE和GL_SPECULAR来设定这些颜色。每个颜色的分量按次序(R,G,B,A)指定,分量A仅用于颜色混合函数激活以后。例如:

    GLfloat blackColor[] = {0.0, 0.0, 0.0, 1.0};
    GLfloat whiteColor[] = {1.0, 1.0, 1.0, 1.0};
    glLightfv(GL_LIGHT3, GL_AMBIENT, blackColor);
    glLightfv(GL_LIGHT3, GL_DIFFUSE, whiteColor);
    glLightfv(GL_LIGHT3, GL_SPECULAR, whiteColor);

      光源GL_LIGHT0的默认颜色是环境光为黑色而漫反射和镜面反射为白色。所有其他光源的环境光、漫反射及镜面反射颜色特性均为黑色。
      GL_AMBIENT设置环境光, 环境光是无方向性的,在场景中均匀照射所有物体。环境光成分是从某个光源发出,并由环境经多次散射得到的,难以确定其最初的方向——它看起来好像来自于四面八方。
      GL_DIFFUSE设置漫反射,散射光来自一个固定方向,所以当光线垂直照射到物体表面上,要比斜照时亮一些。然而,一旦光线照到表面上,就会均匀地在各个方向都发生散射,因此,不论观察点处于哪个位置,其亮度都是一样的。来自于特定位置与方向的任何光线,都可能带有散射光成分。
     GL_SPECULAR设置镜面反,镜面反射也来自于特定方向,但她趋于反射到特定的方向上。一束校准好的激光射到高质量的镜面上,几乎可以产生100%的镜面反射。镜面反射度可以看成代表着物体的光洁度。

    光源的辐射强度衰减系数

      我们可以为OpenGL局部光源设置辐射强度衰减系数。光线辐射强度衰减的三个OpenGL特性常量是GL_CONSTANT_ATTENUATION、GL_LINEAR_ATTENUATION和GL_QUADRATIC_ATTENUATION,每一个辐射衰减系数用正整数或正浮点数来设定。

     glLightf(GL_LIGHT6, GL_CONSTANT_ATTENUATION, 1.5);
     glLightf(GL_LIGHT6, GL_LINEAR_ATTENUATION, 0.75);
     glLightf(GL_LIGHT6, GL_QUADRATIC_ATTENUATION, 0.4);

    在衰减系数的值设定后,光线衰减函数应用于该光源的所有三种颜色(环境光、漫反射和镜面反射)。

    材质属性

       在相同的光源条件下,不同物体的表面呈现明显不同的视觉表现,最大的区别就是不同材料的表面有不同的视觉表现,这反映了物体表面的物理属性,一般由材质属性来设置表现的。我们需要在描述对象几何数据之前使用系列glMaterial函数来指定该对象的所有光照特性。

    glMaterial*(surfFace, surfProperty, propertyvalue)

    参数surfFace参数选择,GL_FRONT(为正面设置属性),GL_BACK(为背面设置反射系数),GL_FRONT_AND_BACK(为两面同时设置属性)。

    示例演示

    给GL_LIGHT1光源设置三种颜色、位置及类型,通过键盘L控制开启关闭灯光。

        GLfloat lightAmbient[] = {1.0f, 0.5f, 0.5f, 1.0f};  //环境光参数
        GLfloat lightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};  //漫散光参数
        GLfloat lightSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f}; //镜面反射参数
        GLfloat lightPosition[] = {0.0f, 0.0f, 2.0f, 1.0f}; //光源位置,局部光源
    
        glLightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient);     //设置环境光
        glLightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse);     //设置漫射光
        glLightfv(GL_LIGHT1, GL_SPECULAR, lightSpecular);   //镜面反射后
        glLightfv(GL_LIGHT1, GL_POSITION,lightPosition);   //设置光源位置
        glEnable(GL_LIGHT1);                                //启动一号光源

    运行结果:

    这里写图片描述

    代码下载
    OpenGL学习系列导航

    展开全文
  • Opengl 4种光照模型

    千次阅读 2017-05-19 10:41:58
    1.Lambert模型(漫反射) 环境光: Iambdiff = Kd*Ia ...其中Ia 表示环境光强度,Kd(0 ...其中Il是点光源强度,θ是入射光方向与顶点法线的夹角,称入射角(0 ...最后综合环境光和方向光源,Lambert光照模型可以
    1.Lambert模型(漫反射)
     
    

    环境光:

    Iambdiff = Kd*Ia

    其中Ia 表示环境光强度,Kd(0<K<1)为材质对环境光的反射系数,Iambdiff是漫反射体与环境光交互反射的光强。

    方向光:

    Ildiff = Kd * Il * Cos(θ)

    其中Il是点光源强度,θ是入射光方向与顶点法线的夹角,称入射角(0<=A<=90°),Ildiff是漫反射体与方向光交互反射的光强,若 N为顶点单位法向量,L表示从顶点指向光源的单位向量(注意顶点指向光源),则Cos(θ)等价于dot(N,L),故又有:

    Ildiff = Kd * Il * dot(N,L)

    最后综合环境光和方向光源,Lambert光照模型可以写成:

    Idiff = Iambdiff + Ildiff = Kd * Ia + Kd * Il * dot(N,L)


    2.Phong模型(镜面反射)

    Phong模型认为镜面反射的光强与反射光线和视线的夹角相关:

    Ispec = Ks * Il * ( dot(V,R) )^Ns

    其中Ks 为镜面反射系数,Ns是高光指数,V表示从顶点到视点的观察方向,R代表反射光方向。由于反射光的方向R可以通过入射光方向L(从顶点指向光源)和物体的法向量求出,
    R + L = 2 * dot(N, L) * N  即 R = 2 * dot(N,L) * N - L

    所以最终的计算式为:

    Ispec = Ks * Il * ( dot(V, (2 * dot(N,L) * N – L ) )^Ns



    3.Blinn-Phong光照模型(修正镜面光)

    Blinn-Phong是一个基于Phong模型修正的模型,其公式为:

    Ispec = Ks * Il * ( dot(N,H) )^Ns

    其中N是入射点的单位法向量,H是光入射方向L和视点方向V的中间向量,通常也称之为半角向量(半角向量被广泛用于各类光照模型,原因不但在于半角向量蕴含的信息价值,也在于半角向量是很简单的计算:H = (L + V) / |L + V|  )。



    4.Rendering Equation(全局光照模型)

    Rendering Equation 是Kajia在1986年提出的,

    Lo(X, Wo) = Le(X, Wo) + ∫fr(X, Wi, Wo) Li(X, Wi) dot(N, Wi) dWi

    其中X表示入射点,Lo(X, Wo)即从物体表面X点,沿方向Wo反射的光强,Le(X, Wo)表示从物体表面X以方向Wo 发射出去的光强,该值仅对自发光体有效,fr(X, Wi, Wo)为,入射光线方向为Wi, 照射到点X上,然后从Wo方向发射出去的BRDF值,Li(X, Wi)为入射方向为Wi照射到点X上的入射光强,N表示点X处的法向量,然后对入射方向进行积分(因为光线入射的方向是四面八方的,积分的意义是对每个方向进行一遍计算后相加),计算的结果就是全局光照的辐射率。

    对于单个点光源照射到不会自发光的物体上,公式可以简化成:

    Lo(X, Wo) = fr(X, Wi, Wo) Li(X, Wi) dot(N, Wi)

    这个公式非常有用,通常会将该公式分解为漫反射表达式和镜面表达式之和。对于漫反射表面,BRDF可以忽略不计,因为它总是返回某个恒定值,所以可以写成如下形式:

    Lo(X, Wo) = Idiff + frs(X, Wi, Wo) Li(X, Wi) dot(N, Wi)

    其中Idiff表示漫反射分量,使用公式的计算方法,frs(X, Wi, Wo)表示镜面反射的BRDF函数,前面的Phong高光模型,其实是rendering equation在单一光源下针对理想镜面反射的特定推导,对于Phong高光而言:

    frs(X, Wi, Wo) = Ks (dot(N, H)^Ns  / dot(N, Wi)


    //-------------------------------------------------------------------------------------------------------------------------

    几种光照模型的比较

    Lambert 模型能够较好地表现粗糙表面上的光照现象,如石灰墙,纸张等等,但是在渲染金属材质制成的物体时,则会显得呆板,表现不出光泽,主要原因是其没有考虑到镜面反射效果,所以Phong模型对其进行了很好的补充。由于Blinn-phng光照模型混合了Lambert的漫射部分和标准的高光,渲染效果有时会比 Phong高光更柔和,有些人认为phong光照模型比blinn-phong更加真实,实际上也是如此,Blinn-phong渲染效果要更加柔和一些,但是由于Blinn-phong的光照模型省去了计算反射光线方向向量的两个乘法运算,速度更快,因此成为许多CG软件中默认的光找渲染方法,此外它也继承在了大多数图形芯片中,用以产生实时的快速渲染。在OpenGL和Direct3D渲染管线中,Blinn-Phong就是默认的渲染模型。 Rendering Equation是基于物理光学的模型,其对于观察方向上的辐射率进行了本质上的量化,Phong模型只是其特定BRDF的推导。

    以下给出Blinn-Phong的CG片段,用于参考实现:

    struct VertexScreen
    {
        float4 oPosition : POSITION;
        float4 objectPos : TEXCOORD0;
        float4 objectNormal : TEXCOORD1;
    };

    void main_f(VertexScreen posIn,
        out float4 color : COLOR,
        uniform float4x4 worldMatrix,
        uniform float4x4 worldMatrix_IT,
        uniform float3 globalAmbient,
        uniform float3 eyePosition,
        uniform float3 lightPosition,
        uniform float3 lightColor,
        uniform float3 Kd,
        uniform float3 Ks,
        uniform float shininess)
    {
        float3 worldPos = mul(worldMatrix, posIn.objectPos).xyz;
        float3 N = mul(worldMatrix_IT, posIn.objectNormal).xyz;
        N = normalize(N);

        //计算入射光方向\视线方向\半角向量
        float3 L = normalize(lightPosition - worldPos);
        float3 V = normalize(eyePosition - worldPos);
        float3 H = normalize(L + V);

        // 计算漫反射分量
        float3 diffuseColor = Kd * globalAmbient+Kd*lightColor*max(dot(N, L), 0);
     
        //计算镜面反射分量
        float3 specularColor = Ks * lightColor*pow(max(dot(N, H), 0), shininess);
        color.xyz = diffuseColor + specularColor;
        color.w = 1;
    }


    展开全文
  • OpenGL光源光照基础

    2017-11-10 11:22:25
    OpenGL光照模型中,除非一个物体自己会发光,否则它将受到3种不同类型的光的照射:环境光(ambient)、散射光(diffuse)和镜面光(specular)。 全局环境光 在我们没有用到光照之前,我们也能够看到场景中绘制的物体,...
     

    光照模型

    在OpenGL光照模型中,除非一个物体自己会发光,否则它将受到3种不同类型的光的照射:环境光(ambient)、散射光(diffuse)和镜面光(specular)。

    • 全局环境光 在我们没有用到光照之前,我们也能够看到场景中绘制的物体,原因是每个场景都有这个全局环境光,它不来自任何特定的光源。
    • 环境光(ambient) 环境光并不来自任何特定的方向。它来自某个光源,但光线却是在房间或场景中四处反射,没有方向可言。由环境光所照射的物体在所有方向的表面都是均匀照亮的。
    • 散射光(diffuse) 散射光具有方向性,来自于一个特定的方向,它根据入射光线的角度在表面上均匀地反射开来。
    • 镜面光(specular) 镜面光具有很强的方向性,但它的反射角度很锐利,只沿一个特定的方向反射。

    光源与材料

    光源

    OpenGL可以同时为我们提供8个有效的光源。也就是说,我们最多可以同时启用8个光源。它们分别是GL_LIGHT0,GL_LIGHT1,GL_LIGHT2 …… 其中,GL_LIGHT0是最特殊的一个光源,我们可以为GL_LIGHT0指定环境光成分。在默认情况下,GL_LIGHT0光源的颜色为白光,其他7个光源在默认情况下是没有颜色的,也

    即为黑色。

    光源种类

    • 环境光 环境光是一种无处不在的光。环境光源放出的光线被认为来自任何方向。因此,当你仅为场景指定环境光时,所有的物体无论法向量如何,都将表现为同样的明暗程度。
    • 点光源 由这种光源放出的光线来自同一点,且方向辐射自四面八方。
    • 平行光 平行光又称镜面光,这种光线是互相平行的。从手电筒、太阳等物体射出的光线都属于平行光。
    • 聚光灯 这种光源的光线从一个锥体中射出,在被照射的物体上产生聚光的效果。使用这种光源需要指定光的射出方向以及锥体的顶角α。

    设置光源

    1.设置环境光成分

    对于GL_LIGHT0,我们可以为其指定环境光成分。调用

    glLightfv(GL_LIGHT0,GL_AMBIENT,@AmbientLight);


    在上述函数调用中,第一个参数表示我们要对GL_LIGHT0进行设置;
    第二个参数表示我们要设置的是环境光成分;
    第三个参数则是一个数组,它有4个值,分别表示光源中含有红、绿、蓝三种光线的成分,一般情况下都为1;
    第四个参数为透明度值,一般也为1。
    完整的代码是这样的:

    void SetLight
    {
        int AmbientLight[4]={1,1,1,1};
        glLightfv(GL_LIGHT0,GL_AMBIENT,@AmbientLight);
        glEnable(GL_LIGHT0);       //开启GL_LIGHT0光源
        glEnable(GL_LIGHTING);     //开启光照系统
    }


    2.设置漫射光成分

    通过对漫射光成分的设置,我们可以产生一个点光源。方法和设置环境光成分相似,只需调用

     glLightfv(GL_LIGHT0,GL_DIFFUSE,@DiffuseLight);


    其中DiffuseLight是漫射光的颜色成分。可以根据不同需要指定不同的颜色,一般情况下也为(1,1,1,1)。

    3.设置镜面光成分

    通过对镜面光成分的设置,我们可以产生一个平行光源。方法和设置漫射光成分相似,只需调用

    glLightfv(GL_LIGHT0,GL_SPECULAR,@SpecularLight);


    其中SpecularLight是镜面反射光的颜色成分。可以根据不同需要指定不同的颜色。

    4.设置光源的位置

    对于点光源和平行光源,我们常常需要指定光源的位置来产生需要的效果。方法仍然是调用glLightfv函数,仅仅是换换参数而已:

    glLightfv(GL_LIGHT0,GL_POSITION,@LightPosition);


    其中,LightPosition也是一个四维数组,四维数组的前3项依次为光源位置的X,Y,Z分量。
    第四个值很特殊,一般为1或0。当LightPosition[4]=0的时候,表示光源位于距离场景无限远的地方,相当于平行光;而当LightPosition[4]=1时,光源的位置就是前三项所

    指定的位置,也就是成为了点光源。

    材料

    材料是针对场景中的物体而言的,但并不是指现实生活中构成物体的那种有质感材料,这里的材料只与物体的颜色有关(你可能会想到怎样表现类似金属、玻璃等物质质感,

    除了要使用光照系统并为它们指定合适的材质外,还要使用纹理贴图来表现)。OpenGL用材料对光的红、绿、蓝三原色的反射率来近似定义材料的颜色。当一束光照到物体上

    时,光本身有颜色(可以自己指定。通常情况下的白光是RGB三色光的混合),光照到物体上时,物体的材料会根据自身对光中的各种成分的反射参数决定反射哪种光,也就是物

    体所呈现出的颜色啦。所以并不是什么颜色的光照过来,物体就会呈现出什么颜色的光。

    设置材料

    材质的设置与光源的设置类似。其函数为:

    void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param);


    face可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它表明当前材质应该应用到物体的哪一个面上;
    pname说明一个特定的材质;
    param是材质的具体数值,若函数为向量形式,则param是一组值的指针,反之为参数值本身。非向量形式仅用于设置GL_SHINESS。pname参数值具体内容见下表。另外,

    参数GL_AMBIENT_AND_DIFFUSE表示可以用相同的RGB值设置环境光颜色和漫反射光颜色。


    完整代码如下:

    void SetMaterialAndLight
    {
       GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
       GLfloat mat_shininess[] = { 50.0 };
    
       GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };  //最后一个参数为0表示该光源是directional的
     //GLfloat light_position[] = { 1.0, 1.0, 1.0, 1.0 };  //最后一个参数非0表示该光源是positional的
    
       GLfloat light_ambient[] = { 0.0, 1.0, 0.0, 1.0 };
       GLfloat light_diffuse[] = { 0.0, 1.0, 0.0, 1.0 };
       GLfloat light_specular[] = { 0.0, 1.0, 0.0, 1.0 };
    
       glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
       glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    
       glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
       glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
       glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
       glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    
       glEnable(GL_LIGHT0);       //开启GL_LIGHT0光源
       glEnable(GL_LIGHTING);     //开启光照系统
    }


    颜色追踪

    在没有学习光照之前,我们是通过glColor()函数来指定物体颜色的,前面已经说过,这时候我们能看到我们所设置的物体颜色是因为有全局环境光的原因。当开启光照功能后,

    如果用上述的设置材料属性的方法进行设置,则原来设置的颜色与被光照到后物体呈现的颜色毫无关系,即原来的 glColor*()命令失去原有的作用。
    但还有一种方法设置材料属性的方法,称为颜色追踪,即用颜色指定代替材料属性指定。使用颜色追踪,可以告诉OpenGL仅仅通过调用glColor来设置材料属性。为了启用

    颜色追踪,需要以GL_COLOR_MATERIAL为参数调用glEnable。接着,glColorMaterial函数根据glColor所设置的值来指定材料参数。

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);


    如果调用了 glEnable(GL_COLOR_MATERIAL),那么就会使光照模型中的几种光根据glColor*()中的指定确定颜色;

    void glColorMaterial(GLenum face, GLenum mode);


    – face的取值GL_FRONT, GL_BACK与GL_FRONT_AND_BACK(默认值)
    – mode的取值为GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR与 GL_AMBIENT_AND_DIFFUSE(默认值)
    例如:

    glEnable(GL_COLOR_MATERIAL); 
    glColorMaterial(GL_FRONT, GL_DIFFUSE); /* now glColor* changes diffuse reflection */ 
    glColor3f(0.2, 0.5, 0.8);
    
    /* draw some objects here */ 
    
    glColorMaterial(GL_FRONT, GL_SPECULAR); /* now glColor* changes specular reflection */
    glColor3f(0.9, 0.0, 0.2);
    
    /* draw other objects here */ 
    glDisable(GL_COLOR_MATERIAL);


    另外,不需要使用glColorMaterial()时,确保禁用。

    材质RGB值和光源RGB值的关系

    材质的颜色与光源的颜色有些不同。对于光源,R、G、B值等于R、G、B对其最大强度的百分比。若光源颜色的R、G、B值都是1.0,则是最强的白光;若值变为0.5,颜色仍为

    白色,但强度为原来的一半,于是表现为灰色;若R=G=1.0,B=0.0,则光源为黄色。对于材质,R、G、B值为材质对光的R、G、B成分的反射率。比如,

    一种材质的R=1.0,G=0.5,B=0.0,则材质反射全部的红色成分,一半的绿色成分,不反射蓝色成分。也就是说,若OpenGL的光源颜色为(LR,LG,LB),

    材质颜色为(MR,MG,MB),那么,在忽略所有其他反射效果的情况下,最终到达眼睛的光的颜色为(LRMR,LGMG,LB*MB)。同样,如果有两束光,相应的值分别为(R1,G1,B1)和

    (R2,G2,B2),则OpenGL将各个颜色成分相加,得到(R1+R2,G1+G2,B1+B2),若任一成分的和值大于1(超出了设备所能显示的亮度)则约简到1.0。

    OpenGL 光照模效果的原理

    OpenGL的光照模型是用来模拟现实生活中的光照的。它根据顶点的法线向量光源的位置决定顶点的明暗程度,根据顶点的材质光源中三原色的成分来决定物体将

    表现出怎样的颜色

    使用OpenGL的光照模型的步骤:

    • 设置光源的种类、位置和方向(对于平行光源)
    • 为每个图元的每个顶点指定它的法线向量
    • 为各个图元指定它的材料

    步骤1和步骤3上面已经说过了,下面解释一下步骤2。
    众所周知,光线是根据物体表面法向来确定其反射方向的,所以想要对开启光照效果就要对物体表面的每个图元指定法向。如果场景中的物体是多边形网格模型,则需要自己

    计算每个网格的法向量然后再调用glNormal*()为每个网格指定法线向量。当然,如果你用的是类似glutSolidTorus()或者glutSolidSphere()这种GLUT工具包中的函数则不用

    自己指定图元的法向量。



    作者:Beatrice7
    链接:http://www.jianshu.com/p/1da6ae890ef3
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    展开全文
  • OpenGL光源、材质和光照模型

    千次阅读 2015-10-03 23:30:22
    好久没写博客了,从现在开始,养成一...OpenGL在处理光照的过程中主要包含三项内容,光源、材质和光照模型。也就是我们在光照设置的代码中常看到的glLightfv、glMaterialfv和glLightModelfv。其中要注意的是,GL_AMBIE
  • OpenGL.光照模型

    千次阅读 2016-06-06 20:01:43
    点光源:从一点向四面八方发出光线 聚光灯:从一点朝一个方向发出光线,且只在一定的角度内有光,离中心越近越亮 无穷远:平行光 环境光:错综复杂的给每处物体都均匀的打光 材质有三种,也就是对光有三种反射 ...
  • Android OpenGL ES 光照模型

    千次阅读 2014-04-19 18:28:25
    前面绘制球体时Android OpenGL ES 开发教程(22):绘制一个球体 ,为了能看出3D效果,给场景中添加光源。如果没有光照,绘出的球看上去和一个二维平面上圆没... 光照模型光源和光照效果可以细分为红,绿,蓝三个部分
  • OpenGL总结15-光照模型

    2020-08-17 10:30:05
    光照模型 绘制地形图时发现图像看不出起伏,觉得应该是关照模型和法线的问题,所以查找了一些资料,这里做...点光源:通过RGB颜色值指定的单色光点,通过位置和颜色定义。一般用于距离较远的场景(包括距离以及相对物体
  • 1. 光照模型  环境光——经过多次反射而来的光称为环境光,无法确定其最初的方向,但当特定的光源关闭后,它们将消失. 全局环境光——每个光源都能对场景提供环境光。此外,还有一个环境光,它不来自任何特定...
  • OpenGL光照模型

    2012-06-07 09:22:18
    OpenGL光照模型 发表于 2011 年 07 月 26 日 由guidebee 了能看出3D效果,给场景中添加光源。如果没有光照,绘出的球看上去和一个二维平面上圆没什么差别,如下图,左边为有光照效果的球体,右边为同一个球体...
  • 冯氏光照模型 基础光照模型 现实世界的光照是极其复杂的,而且会受到诸多因素的影响,这是我们有限的计算能力所无法模拟的。因此OpenGL的光照使用的是简化的模型,对现实的情况进行近似,这样处理起来会更容易一些,...
  • opengl 光照模型和光照纹理贴图

    千次阅读 2019-03-26 15:56:46
    上篇文章我们讲到了 opengl的 mvp矩阵,以及在三维世界里漫游的方法,与实现,这篇讲解下,opengl光照模型。 光基础介绍 环境光ambient: 就是环境周边物体反射找到物体上的光 漫反射光diffuse: 物体表面漫...
  • 一、Phong Lighting Model 光照模型的计算方式 二、计算步骤 1、给定环境色ambient 2、计算漫反射diffuse 2.1、根据灯光位置和物体某的位置计算光源的指向 vec3 lightDir = normalize(lightPos - FragPos); 2.2、...
  • OpenGL(七) 光照模型及设置

    万次阅读 多人点赞 2016-11-10 23:42:53
    OpenGL把现实世界中的光照系统近似归为三部分,分别是光源、材质和光照环境。 光源就是光的来源,是“光”这种物质的提供者; 材质是指被光源照射的物体的表面的反射、漫反射(OpenGL不考虑折射)特性; 材质...
  • OpenGL-光照模型

    千次阅读 2012-09-02 14:38:50
    1. 光的成分   (1)环境光:经过了充分散射,无法分辨其方向 ...(2)散射光:来自于某个方向,撞击表面时,均匀的向所有方向发散 ... Opengl光照模型根据所反射的红蓝绿光的比例来模拟颜色。    对于
  • 一、 OpenGL光照模型 ... 光照计算时,OpenGL采用的是简单光照模型,只考虑环境光和光源直接入射光,不考虑物体间的反射光和透射光。且不考虑物体间的相互遮挡,因此没有阴影。    1. 环境光
  • opengl光照效果之点光源

    千次阅读 2017-08-10 13:36:55
    点光源:点光源的入射方向是被照射模型上的点的坐标减去点光源所在位置坐标的向量,且会随着距离的增加发生衰减。 效果图: 点光源片元shader uniform vec4 U_LightPos;//点光源坐标 uniform vec3 U_EyePos;//...
  • Android OpenGL ES 开发教程 25 OpenGL光照模型
  • 一、模型准备、 二、光照设置、 1、启用光照设置、 2、启用光源、 3、设置光照参数、 4、设置环境光、 5、设置反射材质、 三、光照法线设置、 1、设置光源位置、 2、设置法线、 3、代码示例及运行效果、 四、相关...
  • 1)、OpenGL光照模型 OpenGL的光, 可以分解为红,绿,蓝3种分;对于物体表面的材质则可以使用其在各个方向反射的红,绿,蓝光的比例来描述. OpenGL光照模型将光照分为4个独立的部分: 环境光(ambient light) -- 被...
  • opengl光照模型

    千次阅读 2013-06-14 01:14:07
    1. 几个概念 ...全局环境光——它们并非来自特定的光源,这些光经过了多次散射,已经无法确定其光源位于何处.散射光——来自同一方向,照射到物体表面后,将沿各个方向均匀反射,因此,无论从
  • OpenGL 4.0 GLSL 采用平行光照模型

    千次阅读 2014-02-12 10:02:53
    点光源光照模型中,一个重要的向量就是从物体表面到光源的向量(s)。但如果光源处于无穷远处,那么物体表面所有点到光源的向量可以近似看作是平行的,这样,我们可以用一个方向向量代替原来的光源向量s(比如太阳...
  • OpenGL光照模型--四种光照效果

    千次阅读 2016-04-16 20:37:01
    OpenGL光照模型  为了能看出3D效果,给场景中添加光源。如果没有光照,绘出的球看上去和一个二维平面上圆没什么差别,如下图,左边为有光照效果的球体,右边为同一个球体但没有设置光源,看上去就没有立体效果,...
  • 从本节开始,我们可以开始学习OpenGL中包括光照模型加载等主题。光照是一个复杂的主题,本节学习简单的Phong reflection model.本节示例程序https://github.com/wangdingqiao/noteForOpenGL/tree/master/lighting...
  • 漫反射光照模型是:在所有方向上反射的光照强度相等,...漫反射的数学模型包含两个向量:从物体表面的光源的的向量S 和 物体表面的某一点的法线向量N。如图: 到达物体表面的光照轻度很大一部分依赖于物体表面

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,074
精华内容 1,629
关键字:

opengl点光源光照模型