• 投光物


    平行光:当使用一个假设光源处于无限远处的模型时,被称为定向光。因为它的光线都有着相同的方向,与光源的位置无关。

    理解:太阳光便为平行光

    定义一个光线方向向量而不是位置向量来模拟一个定向光

     1 struct Light {
     2     // vec3 position; // 使用定向光就不再需要了
     3     vec3 direction;
     4 
     5     vec3 ambient;
     6     vec3 diffuse;
     7     vec3 specular;
     8 };
     9 ...
    10 void main()
    11 {
    12   vec3 lightDir = normalize(-light.direction);
    13   ...
    14 }

    光照计算中采用的都是一个从片段到光源的光线方向。light.direction是人们的习惯,由光源指向物体的方向。所以需要取反。

    二、点光源

    是处于世界中某一个位置的光源,它会朝着所有方向发光,但光线会随着距离逐渐衰减,例如:灯泡

    衰减

    随着光线传播距离的增长逐渐消减光的强度通常叫做衰减

    随距离减少光强度的一种方式是使用一个线性方程。这样的方程能够随着距离的增长线性地减少光的强度,从而让远处的物体更暗。然而,这样的线性方程通常会看起来比较假。在现实世界中,灯在近处通常会非常亮,但随着距离的增加光源的亮度一开始会下降非常快,但在远处时剩余的光强度就会下降的非常缓慢了。所以,我们需要一个不同的公式来减少光的强度。

    d:代表片段距光源的距离。

    Kc:常数项;Kl:一次项;Kq:二次项。

    常数项通常保持为1.0,作用为:保证值永远小于1,否则的话在某些距离上反而会增加强度

    一次项会与距离值相乘,以线性的方式较少强度

    二次项会与距离的平方相乘,让光源以二次递减的方式减少强度。二次项在距离比较小的时候影响会比一次项小很多,但当距离值比较大的时候就会比一次项更大了。

    由于二次项的存在,光线会在大部分时候以线性的方式衰退,直到距离变得足够大,让二次项超过一次项,光的强度会以更快的速度下降。这样的结果就是,光在近距离时亮度很高,但随着距离变远亮度迅速降低,最后会以更慢的速度减少亮度。下面这张图显示了在100的距离内衰减的效果:

    实现衰减:

    为了实现衰减,在片段着色器中我们还需要三个额外的值:也就是公式中的常数项、一次项和二次项。它们最好储存在之前定义的Light结构体中。

    然后我们将在OpenGL中设置这些项:我们希望光源能够覆盖50的距离,所以我们会使用表格中对应的常数项、一次项和二次项:

    1 cubeShader.setFloat("light.constant",1.0f);
    2 cubeShader.setFloat("light.linear",0.09f);
    3 cubeShader.setFloat("light.quadratic",0.032f);

    计算衰减:

    length函数为GLSL内建的函数

    1 float distance = length(light.position - fragPos);
    2 float attenuation = 1.0/(light.constant + light.linear * distance + light。quadratic *(distance,distance));

    接下来,我们将包含这个衰减值到光照计算中,将它分别乘以环境光、漫反射和镜面光颜色。

    1 ambient  *= attenuation; 
    2 diffuse  *= attenuation;
    3 specular *= attenuation;

     聚光

    聚光是位于环境中某个位置的光源,它只朝一个特定方向而不是所有方向照射光线。这样的结果就是只有在聚光方向的特定半径内的物体才会被照亮,其它的物体都会保持黑暗。聚光很好的例子就是路灯或手电筒

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

    所以我们要做的就是计算LightDir向量和SpotDir向量之间的点积(还记得它会返回两个单位向量夹角的余弦值吗?),并将它与切光角ϕ值对比。

    在片段着色器中我们需要的值有聚光的位置向量(来计算光的方向向量)、聚光的方向向量和一个切光角。我们可以将它们储存在Light结构体中:

    struct Light {
        vec3  position;
        vec3  direction;
        float cutOff;
        ...
    };

    手电筒(Flashlight)是一个位于观察者位置的聚光,通常它都会瞄准玩家视角的正前方。

    lightingShader.setVec3("light.position",  camera.Position);
    lightingShader.setVec3("light.direction", camera.Front);
    lightingShader.setFloat("light.cutOff",   glm::cos(glm::radians(12.5f)));

    你可以看到,我们并没有给切光角设置一个角度值,反而是用角度值计算了一个余弦值,将余弦结果传递到片段着色器中。这样做的原因是在片段着色器中,我们会计算LightDirSpotDir向量的点积,这个点积返回的将是一个余弦值而不是角度值,所以我们不能直接使用角度值和余弦值进行比较。

    接下来就是计算θ值,并将它和切光角ϕ对比,来决定是否在聚光的内部:

    float theta = dot(lightDir, normalize(-light.direction));
    
    if(theta > light.cutOff) 
    {       
      // 执行光照计算
    }
    else  // 否则,使用环境光,让场景在聚光之外时不至于完全黑暗
      color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0);

    你可能奇怪为什么在if条件中使用的是 > 符号而不是 < 符号。theta不应该比光的切光角更小才是在聚光内部吗?这并没有错,但不要忘记角度值现在都由余弦值来表示的。

    角度越大,余弦值越小

  • 相关阅读:
    黑马程序员_java基础笔记(13)...类加载器和代理
    nyoj-411-Friends number
    nyoj-38-布线问题
    nyoj-233-Sort it
    nyoj-115-城市平乱
    nyoj-608-畅通工程
    nyoj-36-最长公共子序列
    nyoj-150-Train Problem I
    nyoj-494-Dancing With the Googlers
    nyoj-214-单调递增子序列(二)
  • 原文地址:https://www.cnblogs.com/keguniang/p/10020187.html
Copyright © 2020-2023  润新知