美文网首页
LearnOpenGL 投光物

LearnOpenGL 投光物

作者: li_礼光 | 来源:发表于2020-10-07 01:48 被阅读0次

定向光

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

我们知道的定向光源的一个好例子是,太阳。太阳和我们不是无限远,但它也足够远了,在计算光照的时候,我们感觉它就像无限远。在下面的图片里,来自于太阳的所有的光线都被定义为平行光:

平行光

因为所有的光线都是平行的,对于场景中的每个物体光的方向都保持一致,物体和光源的位置保持怎样的关系都无所谓。由于光的方向向量保持一致,光照计算会和场景中的其他物体相似。

我们可以通过定义一个光的方向向量,来模拟这样一个定向光,而不是使用光的位置向量。着色器计算保持大致相同的要求,这次我们直接使用光的方向向量来代替用lightDir向量和position向量的计算:

struct Light
{
    // vec3 position; // 现在不在需要光源位置了,因为它是无限远的
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
...
void main()
{
    vec3 lightDir = normalize(-light.direction);
    ...
}


投光物这章中, 关于定向光的源码, 对着源代码调试, 好像是有些问题的. 源码对Cube做了一些旋转. 总共添加了10个.

最开始以为这里要做的是, 添加10个Cube, 就算旋转的方向各式各样, 但是接受的光源都是从同一个方向的.

运行的结果与实际的有点偏差,研究片元着色器.

void main()
    {
    
        vec3 lightDir = normalize(-light.direction);
    
        //环境光ambient
        //环境颜色 = 环境光照 × 贴图
        vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

        //漫反射diffuse
        //DiffuseFactor = max(0, dot(N, L))
        //漫反射颜色 = 漫反射因子(diffuseFactor) × 漫反射光照 × 贴图
        vec3 norm = normalize(Normal);
        float diffuseFactor = max(dot(norm, lightDir),0.0);
        vec3 diffuse = diffuseFactor * light.diffuse * vec3(texture(material.diffuse, TexCoords));

        //镜面反射specular
        //R=reflect(L, N)
        //SpecularFactor = pow(max(dot(R,V),0.0), shininess)
        //镜面反射颜色 = 镜面反射因子(SpecularFactor) × 镜面光照 × 贴图
        vec3 viewDir = normalize(viewPos - FragPos);
        vec3 reflectDir = reflect(-lightDir , norm);
        float specularFactor = pow(max(dot(viewDir, reflectDir),0.0),material.shininess);
        vec3 specular = specularFactor * light.specular * vec3(texture(material.specular, TexCoords));

        //最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色
        vec3 result = ambient + diffuse + specular;
        color = vec4(result , 1.0f);
    }
因为light.direction的值是固定的, 所以, Cube的亮的一面也是固定的. 旋转的话会把亮的一面旋转走了. 这里体现不出来那种一个光源照射下来, 多个Cube体现不同的阴暗面.

现在没有这个效果就先放着放下看.

多Cube定向光


点光源

定向光作为全局光可以照亮整个场景,这非常棒,但是另一方面除了定向光,我们通常也需要几个点光源(Point Light),在场景里发亮。点光是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。想象灯泡和火炬作为投光物,它们可以扮演点光的角色。

点光源

之前的教程我们已经使用了(最简单的)点光。我们指定了一个光源以及其所在的位置,它从这个位置向所有方向发散光线。然而,我们定义的光源所模拟光线的强度却不会因为距离变远而衰减,这使得看起来像是光源亮度极强。在大多数3D仿真场景中,我们更希望去模拟一个仅仅能照亮靠近光源点附近场景的光源,而不是照亮整个场景的光源。

如果你把10个箱子添加到之前教程的光照场景中,你会注意到黑暗中的每个箱子都会有同样的亮度,就像箱子在光照的前面;没有公式定义光的距离衰减。我们想让黑暗中与光源比较近的箱子被轻微地照亮


衰减 Fatt=1.0 / (Kc+Kl∗d+Kq∗d2)

在这里d 代表片段到光源的距离。为了计算衰减值,我们定义3个(可配置)项:常数项Kc,一次项Kl 和二次项Kq 。

根据根式来看, 当d, 距离越远, 衰减Fatt的值越小. 也可以理解为看到光衰减的效果越小
  • 常数项通常是1.0,它的作用是保证分母永远不会比1小,因为它可以利用一定的距离增加亮度,这个结果不会影响到我们所寻找的。
  • 一次项用于与距离值相乘,这会以线性的方式减少亮度。
  • 二次项用于与距离的平方相乘,为光源设置一个亮度的二次递减。二次项在距离比较近的时候相比一次项会比一次项更小,但是当距离更远的时候比一次项更大。
Fatt

你可以看到当距离很近的时候光有最强的亮度,但是随着距离增大,亮度明显减弱,大约接近100的时候,就会慢下来。这就是我们想要的。

距离 常数项 一次项 二次项
7 1.0 0.7 1.8
13 1.0 0.35 0.44
20 1.0 0.22 0.20
32 1.0 0.14 0.07
50 1.0 0.09 0.032
65 1.0 0.07 0.017
100 1.0 0.045 0.0075
160 1.0 0.027 0.0028
200 1.0 0.022 0.0019
325 1.0 0.014 0.0007
600 1.0 0.007 0.0002
3250 1.0 0.0014 0.000007
处理不同的衰减得到的不同的效果

片元着色器Shader

//片元着色器程序
static char *myLightCastersFragmentShaderSrc = SHADER(
    \#version 330 core\n
    //材质
    /**
    要记住的是sampler2D也叫做模糊类型,这意味着我们不能以某种类型对它实例化,只能用uniform定义它们。
    如果我们用结构体而不是uniform实例化(就像函数的参数那样),GLSL会抛出奇怪的错误;这同样也适用于其他模糊类型。
    */
    struct Material {
        sampler2D diffuse;
        sampler2D specular;
        float shininess;//镜面反射散射因子(半径).
    };

    //光照强度
    struct Light {
        vec3 position;//光源位置
        vec3 direction;//光源方向
        vec3 ambient;//环境光照
        vec3 diffuse;//漫反射光照
        vec3 specular;//镜面反射光照
        //衰减
        float constant; //常量
        float linear;//一次项
        float quadratic;//二次项
    };

    in vec3 Normal;
    in vec3 FragPos;
    in vec2 TexCoords;
                                                      
    uniform vec3 viewPos;//镜面反射
    uniform Material material;//材质
    uniform Light light;


    out vec4 color;
                                                               
    void main()
    {
    
//        vec3 lightDir = normalize(-light.direction);
        vec3 lightDir = normalize(light.position - FragPos);

        //环境光ambient
        //环境颜色 = 环境光照 × 贴图
        vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

        //漫反射diffuse
        //DiffuseFactor = max(0, dot(N, L))
        //漫反射颜色 = 漫反射因子(diffuseFactor) × 漫反射光照 × 贴图
        vec3 norm = normalize(Normal);
        float diffuseFactor = max(dot(norm, lightDir),0.0);
        vec3 diffuse = diffuseFactor * light.diffuse * vec3(texture(material.diffuse, TexCoords));

        //镜面反射specular
        //R=reflect(L, N)
        //SpecularFactor = pow(max(dot(R,V),0.0), shininess)
        //镜面反射颜色 = 镜面反射因子(SpecularFactor) × 镜面光照 × 贴图
        vec3 viewDir = normalize(viewPos - FragPos);
        vec3 reflectDir = reflect(-lightDir , norm);
        float specularFactor = pow(max(dot(viewDir, reflectDir),0.0),material.shininess);
        vec3 specular = specularFactor * light.specular * vec3(texture(material.specular, TexCoords));

    
        float distance = length(light.position - FragPos);
        float attenuation = 1.0f / (light.constant + light.linear*distance +light.quadratic*(distance*distance));
    
        //最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色
        vec3 result = ambient * attenuation + diffuse * attenuation + specular * attenuation;
        color = vec4(result , 1.0f);
    }
);

程序

        //衰减
        GLint lightConstantLoc = glGetUniformLocation(myProgram.program, "light.constant");
        GLint lightLinearLoc = glGetUniformLocation(myProgram.program, "light.linear");
        GLint lightQuadraticLoc = glGetUniformLocation(myProgram.program, "light.quadratic");
        glUniform1f(lightConstantLoc, 1.0f);
        
//        //7
//        glUniform1f(lightLinearLoc, 0.7f);
//        glUniform1f(lightQuadraticLoc, 1.8f);
//
//        //13
//        glUniform1f(lightLinearLoc, 0.35f);
//        glUniform1f(lightQuadraticLoc, 0.44f);
//
//        //20
//        glUniform1f(lightLinearLoc, 0.22f);
//        glUniform1f(lightQuadraticLoc, 0.20f);
//
//        //32
//        glUniform1f(lightLinearLoc, 0.14f);
//        glUniform1f(lightQuadraticLoc, 0.07f);
//
//        //50
//        glUniform1f(lightLinearLoc, 0.09f);
//        glUniform1f(lightQuadraticLoc, 0.032f);
//
//        //100
//        glUniform1f(lightLinearLoc, 0.045f);
//        glUniform1f(lightQuadraticLoc, 0.0075f);
//
//        //200
//        glUniform1f(lightLinearLoc, 0.022f);
//        glUniform1f(lightQuadraticLoc, 0.0019f);
//
//        //3250
//        glUniform1f(lightLinearLoc, 0.0014f);
//        glUniform1f(lightQuadraticLoc, 0.000007f);
13 50 200 3250

这里简单粗暴理解为 : 当距离越远, 你能看到的光衰减的效果越弱.





聚光

我们要讨论的最后一种类型光是聚光(Spotlight)。聚光是一种位于环境中某处的光源,它不是向所有方向照射,而是只朝某个方向照射。结果是只有一个聚光照射方向的确定半径内的物体才会被照亮,其他的都保持黑暗。聚光的好例子是路灯或手电筒。

OpenGL中的聚光用世界空间位置,一个方向和一个指定了聚光半径的切光角来表示。我们计算的每个片段,如果片段在聚光的切光方向之间(就是在圆锥体内),我们就会把片段照亮。下面的图可以让你明白聚光是如何工作的:

聚光
  • LightDir:从片段指向光源的向量。
  • SpotDir:聚光所指向的方向。
  • Phiϕ:定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。
  • Thetaθ:LightDir向量和SpotDir向量之间的角度。θ值应该比Φ值小,这样才会在聚光内。

所以我们大致要做的是,计算LightDir向量和SpotDir向量的点乘(返回两个单位向量的点乘,还记得吗?),然后在和切光角ϕ对比。现在你应该明白聚光是我们下面将创建的手电筒的范例。


手电筒

片元着色器

//片元着色器程序
static char *myLightCastersFragmentShaderSrc = SHADER(
    \#version 330 core\n
    //材质
    /**
    要记住的是sampler2D也叫做模糊类型,这意味着我们不能以某种类型对它实例化,只能用uniform定义它们。
    如果我们用结构体而不是uniform实例化(就像函数的参数那样),GLSL会抛出奇怪的错误;这同样也适用于其他模糊类型。
    */
    struct Material {
        sampler2D diffuse;
        sampler2D specular;
        float shininess;//镜面反射散射因子(半径).
    };

    //光照强度
    struct Light {
        vec3 position;//光源位置
        vec3 direction;//光源方向
        vec3 ambient;//环境光照
        vec3 diffuse;//漫反射光照
        vec3 specular;//镜面反射光照
    
        //衰减
        float constant; //常量
        float linear;//一次项
        float quadratic;//二次项
        
        //聚光
        float cutOff;

    };

    in vec3 Normal;
    in vec3 FragPos;
    in vec2 TexCoords;
                                                      
    uniform vec3 viewPos;//镜面反射
    uniform Material material;//材质
    uniform Light light;


    out vec4 color;
                                                               
    void main()
    {
    
//        vec3 lightDir = normalize(-light.direction);
        vec3 lightDir = normalize(light.position - FragPos);
    
        // Check if lighting is inside the spotlight cone
        float theta = dot(lightDir, normalize(-light.direction));

        if(theta > light.cutOff) // 执行光照计算.
        {

            //环境光ambient
            //环境颜色 = 环境光照 × 贴图
            vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

            //漫反射diffuse
            //DiffuseFactor = max(0, dot(N, L))
            //漫反射颜色 = 漫反射因子(diffuseFactor) × 漫反射光照 × 贴图
            vec3 norm = normalize(Normal);
            float diffuseFactor = max(dot(norm, lightDir),0.0);
            vec3 diffuse = diffuseFactor * light.diffuse * vec3(texture(material.diffuse, TexCoords));

            //镜面反射specular
            //R=reflect(L, N)
            //SpecularFactor = pow(max(dot(R,V),0.0), shininess)
            //镜面反射颜色 = 镜面反射因子(SpecularFactor) × 镜面光照 × 贴图
            vec3 viewDir = normalize(viewPos - FragPos);
            vec3 reflectDir = reflect(-lightDir , norm);
            float specularFactor = pow(max(dot(viewDir, reflectDir),0.0),material.shininess);
            vec3 specular = specularFactor * light.specular * vec3(texture(material.specular, TexCoords));

        
            float distance = length(light.position - FragPos);
            float attenuation = 1.0f / (light.constant + light.linear*distance +light.quadratic*(distance*distance));
        
            //最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色
            vec3 result = ambient * attenuation + diffuse * attenuation + specular * attenuation;
            color = vec4(result , 1.0f);
        } else {
            //使用环境光,使得场景不至于完全黑暗
            color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0f);
        }
    }
);

程序

       //光照
        GLint lightPosLoc = glGetUniformLocation(myProgram.program, "light.position");
        GLint directionPos = glGetUniformLocation(myProgram.program, "light.direction");
        GLint lightAmbientLoc = glGetUniformLocation(myProgram.program, "light.ambient");
        GLint lightDiffuseLoc = glGetUniformLocation(myProgram.program, "light.diffuse");
        GLint lightSpecularLoc = glGetUniformLocation(myProgram.program, "light.specular");
        glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
        glUniform3f(directionPos, -0.2f, -1.0f, -0.3f);
        glUniform3f(lightAmbientLoc, 0.1f, 0.1f, 0.1f);
        glUniform3f(lightDiffuseLoc, 0.9f, 0.9f, 0.9f);
        glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
        
        
        //衰减
        GLint lightConstantLoc = glGetUniformLocation(myProgram.program, "light.constant");
        GLint lightLinearLoc = glGetUniformLocation(myProgram.program, "light.linear");
        GLint lightQuadraticLoc = glGetUniformLocation(myProgram.program, "light.quadratic");
        glUniform1f(lightConstantLoc, 1.0f);
        glUniform1f(lightLinearLoc, 0.09f);
        glUniform1f(lightQuadraticLoc, 0.032f);
        
        //聚光
        GLint lightCutOffLoc = glGetUniformLocation(myProgram.program, "light.cutOff");
        glUniform1f(lightCutOffLoc, glm::cos(glm::radians(44.5f)));

你可以看到,我们为切光角设置一个角度,但是我们根据一个角度计算了余弦值,把这个余弦结果传给了片段着色器。这么做的原因是在片段着色器中,我们计算LightDir和SpotDir向量的点乘,而点乘返回一个余弦值,不是一个角度,所以我们不能直接把一个角度和余弦值对比。为了获得这个角度,我们必须计算点乘结果的反余弦,这个操作开销是很大的。所以为了节约一些性能,我们先计算给定切光角的余弦值,然后把结果传递给片段着色器。由于每个角度都被表示为余弦了,我们可以直接对比它们,而不用进行任何开销高昂的操作。

现在剩下要做的是计算θ值,用它和ϕ值对比,以决定我们是否在或不在聚光的内部:

聚光




平滑/软化边缘

为了创建一种看起来边缘平滑的聚光,我们需要模拟聚光有一个内圆锥(Inner Cone)和一个外圆锥(Outer Cone)。我们可以将内圆锥设置为上一部分中的那个圆锥,但我们也需要一个外圆锥,来让光从内圆锥逐渐减暗,直到外圆锥的边界。

为了创建一个外圆锥,我们只需要再定义一个余弦值来代表聚光方向向量和外圆锥向量(等于它的半径)的夹角。然后,如果一个片段处于内外圆锥之间,将会给它计算出一个0.0到1.0之间的强度值。如果片段在内圆锥之内,它的强度就是1.0,如果在外圆锥之外强度值就是0.0。

我们可以用下面这个公式来计算这个值:

这里ϵ(Epsilon)是内(ϕ)和外圆锥(γ)之间的余弦值差(ϵ=ϕ−γ)。最终的I值就是在当前片段聚光的强度。

很难去表现这个公式是怎么工作的,所以我们用一些实例值来看看:


你可以看到,我们基本是在内外余弦值之间根据θθ插值。如果你仍不明白发生了什么,不必担心,只需要记住这个公式就好了,在你更聪明的时候再回来看看。

我们现在有了一个在聚光外是负的,在内圆锥内大于1.0的,在边缘处于两者之间的强度值了。如果我们正确地约束(Clamp)这个值,在片段着色器中就不再需要if-else了,我们能够使用计算出来的强度值直接乘以光照分量:

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;
...

上面的效果像是有一般手电筒然后照在箱子上, 但是边缘没有羽化的效果. 接下来为了更好的体现羽化效果. 重新修改以下Shader和程序

Shader

//片元着色器程序
static char *myLightCastersFragmentShaderSrc = SHADER(
    \#version 330 core\n
    //材质
    struct Material {
        sampler2D diffuse;
        sampler2D specular;
        float shininess;//镜面反射散射因子(半径).
    };

    //光照强度
    struct Light {
        vec3 position;//光源位置
        vec3 direction;//光源方向
        vec3 ambient;//环境光照
        vec3 diffuse;//漫反射光照
        vec3 specular;//镜面反射光照
    
        //衰减
        float constant; //常量
        float linear;//一次项
        float quadratic;//二次项
        
        //聚光
        float cutOff;
        float outerCutOff;
    };

    in vec3 Normal;
    in vec3 FragPos;
    in vec2 TexCoords;
                                                      
    uniform vec3 viewPos;//镜面反射
    uniform Material material;//材质
    uniform Light light;

    out vec4 color;
                                                               
    void main()
    {
    
//        vec3 lightDir = normalize(-light.direction);
        vec3 lightDir = normalize(light.position - FragPos);
    
        //聚光角度
        float theta = dot(lightDir, normalize(-light.direction));
        //羽化(如果使用clamp 就不要用if-else了)
        float epsilon = light.cutOff - light.outerCutOff;
        float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);
       
    //        if(theta > light.cutOff) // 执行光照计算.
//        {
            //环境光ambient
            //环境颜色 = 环境光照 × 贴图
            vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

            //漫反射diffuse
            //DiffuseFactor = max(0, dot(N, L))
            //漫反射颜色 = 漫反射因子(diffuseFactor) × 漫反射光照 × 贴图
            vec3 norm = normalize(Normal);
            float diffuseFactor = max(dot(norm, lightDir),0.0);
            vec3 diffuse = diffuseFactor * light.diffuse * vec3(texture(material.diffuse, TexCoords)) ;

            //镜面反射specular
            //R=reflect(L, N)
            //SpecularFactor = pow(max(dot(R,V),0.0), shininess)
            //镜面反射颜色 = 镜面反射因子(SpecularFactor) × 镜面光照 × 贴图
            vec3 viewDir = normalize(viewPos - FragPos);
            vec3 reflectDir = reflect(-lightDir , norm);
            float specularFactor = pow(max(dot(viewDir, reflectDir),0.0),material.shininess);
            vec3 specular = specularFactor * light.specular * vec3(texture(material.specular, TexCoords)) ;
        
            float distance = length(light.position - FragPos);
            float attenuation = 1.0f / (light.constant + light.linear*distance +light.quadratic*(distance*distance));
        
            //最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色
            vec3 result = ambient * attenuation + diffuse * attenuation * intensity + specular * attenuation* intensity;
            color = vec4(result , 1.0f);
//        } else {
//            //使用环境光,使得场景不至于完全黑暗
//            color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0f);
//        }
    }
);

程序

   glm::vec3 cubePositions[] = {
         glm::vec3( 0.0f,  0.0f,  0.0f),
     };
    int cubePositionsCount = sizeof(cubePositions)/(sizeof(cubePositions[0]));

    //进行绘制
    while(!glfwWindowShouldClose(window)){
        glfwPollEvents();
        do_movement();

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        //================================================
        glm::mat4 model = glm::mat4(1.0f);
        glm::mat4 view = glm::mat4(1.0f);
        glm::mat4 projection = glm::mat4(1.0f);
        
        GLint myModelLoc = glGetUniformLocation(myProgram.program,"myModel");
        GLint myViewLoc = glGetUniformLocation(myProgram.program,"myView");
        GLint myProjectionLoc = glGetUniformLocation(myProgram.program,"myProjection");
        
        projection = glm::perspective(glm::radians(90.0f), 1.0f, 0.01f, 100.f);//投影矩阵
        glUniformMatrix4fv(myProjectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
        
        view = camera.GetViewMatrix();
        glUniform3f(myViewLoc,  camera.Position.x, camera.Position.y, camera.Position.z);
        glUniformMatrix4fv(myViewLoc, 1, GL_FALSE, glm::value_ptr(view));
        
        glBindVertexArray(VAO);
        //================================================

        
        //================================================
        //镜面反射半径
        GLint matShineLoc = glGetUniformLocation(myProgram.program, "material.shininess");
        glUniform1f(matShineLoc, 64.0f);

        GLint viewPosLoc  = glGetUniformLocation(myProgram.program, "viewPos");
        glUniform3f(viewPosLoc,  camera.Position.x, camera.Position.y, camera.Position.z);

        //光照方向
        //光照
        GLint lightPosLoc = glGetUniformLocation(myProgram.program, "light.position");
        GLint directionPosLoc = glGetUniformLocation(myProgram.program, "light.direction");
        GLint lightAmbientLoc = glGetUniformLocation(myProgram.program, "light.ambient");
        GLint lightDiffuseLoc = glGetUniformLocation(myProgram.program, "light.diffuse");
        GLint lightSpecularLoc = glGetUniformLocation(myProgram.program, "light.specular");
        //    glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
//        glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
        glUniform3f(lightPosLoc, 0.0f, 0.0f, 5.0f);
        glUniform3f(directionPosLoc, 0.0f,0.0f, -2.0f);
//        glUniform3f(directionPosLoc, camera.Front.x, camera.Front.y, camera.Front.z);
        glUniform3f(lightAmbientLoc, 0.2f, 0.2f, 0.2f);
        glUniform3f(lightDiffuseLoc, 0.9f, 0.9f, 0.9f);
        glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
        
        //衰减
        GLint lightConstantLoc = glGetUniformLocation(myProgram.program, "light.constant");
        GLint lightLinearLoc = glGetUniformLocation(myProgram.program, "light.linear");
        GLint lightQuadraticLoc = glGetUniformLocation(myProgram.program, "light.quadratic");
        glUniform1f(lightConstantLoc, 1.0f);
        glUniform1f(lightLinearLoc, 0.09f);
        glUniform1f(lightQuadraticLoc, 0.032f);
        
        //聚光
        GLint lightCutOffLoc = glGetUniformLocation(myProgram.program, "light.cutOff");
        glUniform1f(lightCutOffLoc, glm::cos(glm::radians(12.5f)));

        //羽化
        GLint lightOuterCutOffLoc = glGetUniformLocation(myProgram.program, "light.outerCutOff");
        glUniform1f(lightOuterCutOffLoc, glm::cos(glm::radians(17.5f)));
        //================================================

        for(GLuint i = 0; i < cubePositionsCount; I++)
        {
            model = glm::translate(model, cubePositions[I]);
            model = glm::scale(model, glm:: vec3(3.0f,3.0f,3.0f));
            GLfloat angle = 20.0f * I;
            model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
            glUniformMatrix4fv(myModelLoc, 1, GL_FALSE, glm::value_ptr(model));
            glDrawArrays(GL_TRIANGLES, 0, squareIndicesCount);
        }
        
        glBindVertexArray(0);
        glfwSwapBuffers(window);
    }

重要 :

程序中重新修改了light.position(光源位置)和light.direction(光线方向), 主要为了更好的体现羽化的效果.

如果我们正确地约束(Clamp)这个值,在片段着色器中就不再需要if-else了

羽化前效果

羽化前效果

羽化后效果

羽化后效果

多个Cube羽化后效果

修改一下程序

GLint lightPosLoc = glGetUniformLocation(myProgram.program, "light.position");
GLint directionPosLoc = glGetUniformLocation(myProgram.program, "light.direction");
glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
glUniform3f(directionPosLoc, camera.Front.x, camera.Front.y, camera.Front.z);
手电筒效果

参考 :
投光物
OpenGL 图形库的使用(十五)—— 光照之投光物

相关文章

  • LearnOpenGL 投光物

    定向光 当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察...

  • 投光物

    一个光源把光投射到物体上,叫做投光(Light casters)。 定向光 当一个光源很远的时候,来自光源的每条光...

  • 【四十八,投光物-01定向光】

    投光物 投光 : 光源把光照射到物体上; 定向光 当光源被设置为无限远时,它被称为定向光(Directional ...

  • 重新自学学习openGL 之光照投光物

    概念 将光投射(Cast)到物体的光源叫做投光物(Light Caster) 分类 定向光(Directional...

  • OpenGL ES for Android(投光物)

    简介 在之前的章节学习的光源都是一个点,虽然效果不错,和现实世界的光源还有一定的差距。将光投射(Cast)到物体的...

  • LearnOpenGL 入门篇

    官方教程 :LearnOpenGL-CN[https://learnopengl-cn.readthedocs.i...

  • OpenGL学习资料和记录

    学习资料 OpenGL: LearnOpenGL[https://learnopengl.com/] 计算机图形/...

  • HDR

    参考资料 LearnOpenGL - HDR[https://learnopengl.com/Advanced-L...

  • opengl相关资料

    推荐度较高的opengl教程:《learnopengl》中文版https://learnopengl-cn.git...

  • 教程

    LearnOpenGL CN

网友评论

      本文标题:LearnOpenGL 投光物

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