美文网首页my-QT专栏
QT+OPenGL十四之多光源混合的问题

QT+OPenGL十四之多光源混合的问题

作者: c之气三段 | 来源:发表于2021-04-12 12:00 被阅读0次

    我们前面已经研究了各中不同光源,但是我们都是单独实现的。一个场景中往往会有多种光源。所以我们不得不考虑这种情况。为了能看出效果,我们需要先来一个地形,下载地形模型直接按前面讲的,绘制出来就好了。
    效果:


    image.png

    这只是有定向光的情况,但是我们已经能看到各个部分,背向光线有了阴影。

    混合光的思路:

    我们可以对每种光源处理后最后返回的颜色封装成函数。然后对结果进行结合。
    封装的好处:封装好了更便于我们以后重用,而且能使shader中main函数更简介更可读。

    注意:这里为了简洁我们默认每一种光的环境光因子,漫反射因子和镜面反射因子都是相同的,我们就不在每种光结构体中定义这三种因子了。首先shader和c语言及其相似,我们可以用c语言的方法来写函数。

    修复法向量:

    经过前面的学习我们法线光照往往是需要法线的,而法向量会随着模型的旋转和非等比缩放,就会发生改变,如果我们不修复就会出现光照的严重视觉错误。当旋转后,我们可以直接使用:

    Normal=vec3(model*vec4(normal,0.0));
    

    把最后的法向量发送给片段着色器去处理光照,我们知道模型矩阵中包括了位移缩放和旋转,而向量没有位移属性因此我们必须排除掉,直接把w分量置0就能很好的解决,这样法向量就和顶点坐标一样被放置到世界坐标系中。当然这种情况不适用于非等比缩放。非等缩放需要用到逆转置矩阵,这个在以后来讨论。

    struct Factor
    {
        float ambient;
        float diffuse;
        float specular;
    };
    uniform Factor factor;
    

    封装定向光:

    struct DirLight
    {
        vec3 direct;
        vec3 color;
    };
    uniform DirLight dirDight;
    

    函数,注意先声明再使用,也可写再main函数上面

    vec3 getDirLightColor(vec2 texcoord,vec3 Normal,Factor factor,DirLight dirDight,Material material){
    //获取漫反射贴图,镜面反射贴图纹理采样的颜色
    vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
    vec3 specularColor=vec3(texture(material.specular,texcoord));
    vec3 lightDir=normalize(dirDight.direct);
    vec3 normal=normalize(Normal);
    //计算漫反射强度
    float diffuseStringth = max(dot(normal,lightDir), 0.0);
    //计算镜面反射强度
    vec3 viewDir = normalize(cameraPos - worldPos);
    vec3 reflectDir = reflect(-lightDir, normal);
    float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    //三种反射的颜色
    vec3 ambient=factor.ambient*dirDight.color*diffuseColor;
    vec3 diffuse =factor.diffuse* diffuseStringth *dirDight.color*diffuseColor;
    vec3 specular = factor.specular * specularStringth*dirDight.color*specularColor;
    //返回最终结合出的颜色
    return vec3(ambient + diffuse+specular);
    }
    

    完整shader:这是个例子后面就省略了这些代码

    #version 430 
    struct Material
    {
        sampler2D diffuse;
        sampler2D specular;
        float shininess;
    };
    struct DirLight
    {
        vec3 direct;
        vec3 color;
    };
    struct Factor
    {
        float ambient;
        float diffuse;
        float specular;
    };
    uniform Material material;
    uniform DirLight dirDight;
    uniform Factor factor;
    out vec4 color;
    uniform vec3 cameraPos;
    in vec2 texcoord;
    in vec3 Normal;
    in vec3 worldPos;
    vec3 getDirLightPointColor(vec2 texcoord,vec3 Normal,Factor factor,DirLight dirDight,Material material);
    void main(void) 
    {
    color=vec4(getDirLightColor(texcoord,Normal,factor,dirDight,material),1.0);
    }
    vec3 getDirLightColor(vec2 texcoord,vec3 Normal,Factor factor,DirLight dirDight,Material material){
    //获取漫反射贴图,镜面反射贴图纹理采样的颜色
    vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
    vec3 specularColor=vec3(texture(material.specular,texcoord));
    vec3 lightDir=normalize(dirDight.direct);
    vec3 normal=normalize(Normal);
    //计算漫反射强度
    float diffuseStringth = max(dot(normal,lightDir), 0.0);
    //计算镜面反射强度
    vec3 viewDir = normalize(cameraPos - worldPos);
    vec3 reflectDir = reflect(-lightDir, normal);
    float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    //三种反射的颜色
    vec3 ambient=factor.ambient*dirDight.color*diffuseColor;
    vec3 diffuse =factor.diffuse* diffuseStringth *dirDight.color*diffuseColor;
    vec3 specular = factor.specular * specularStringth*dirDight.color*specularColor;
    //返回最终结合出的颜色
    return vec3(ambient + diffuse+specular);
    }
    

    封装点光源:

    struct PointLight
    {
       vec3 position;
        float constant;//1.0
        float linear;//0.045
        float quadratic; //0.0075,这个衰减距离是100时全部衰减
        vec3 color;
    };
    uniform PointLight pointLight;
    
    vec3 getPointLightColor(vec2 texcoord,vec3 Normal,Factor factor,PointLight pointLight,Material material){
        //获取漫反射贴图,镜面反射贴图纹理采样的颜色
        vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
        vec3 specularColor=vec3(texture(material.specular,texcoord));
        vec3 lightDir = normalize(pointLight.position-worldPos);
        vec3 normal=normalize(Normal);
        // 计算漫反射强度
        float diffuseStringth = max(dot(normal, lightDir), 0.0);
        // 计算镜面反射
        vec3 reflectDir = reflect(-lightDir, normal);
        vec3 viewDir = normalize(cameraPos - worldPos);
        float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        // 计算衰减
        float distance = length(pointLight.position - worldPos);
        float attenuation = 1.0f/(pointLight.constant + pointLight.linear * distance +
        pointLight.quadratic * (distance * distance));
        // 将各个分量合并
        vec3 ambient  = factor.ambient*attenuation*pointLight.color*diffuseColor;
        vec3 diffuse  = factor.diffuse*diffuseStringth*attenuation*pointLight.color*diffuseColor;
        vec3 specular = factor.specular*specularStringth*attenuation*pointLight.color*specularColor;
    
        return vec3(ambient + diffuse + specular);
    }
    

    单独开启点光源效果(光源放在龙模型上方五个像素处):


    image.png

    可以看见只有点光源周围被点亮了。

    封装聚光灯:我们这里写一个手电筒和一个类似与路灯的光源:

    struct Spotlight
    {
        vec3 position;
        vec3 direction;
        float cutOff;//Φ的余玄值
        float outerCutOff;//r的余玄
        vec3 color;
    };
    uniform Spotlight spotlight;
    
    vec3 getSpotlightColor(vec2 texcoord,vec3 Normal,Factor factor,Spotlight spotlight,Material material){
      //获取漫反射贴图,镜面反射贴图纹理采样的颜色
        vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
        vec3 specularColor=vec3(texture(material.specular,texcoord));
        vec3 lightDir = normalize(spotlight.position-worldPos);
        vec3 normal=normalize(Normal);
        //计算光照范围强度
        float theta = dot(lightDir, normalize(spotlight.direction));
       float epsilon = spotlight.cutOff - spotlight.outerCutOff;
       float intensity = clamp((theta - spotlight.outerCutOff) / epsilon,0.0, 1.0);
       //计算漫反射强度
       float diffuseStringth = max(dot(normal,lightDir), 0.0);
       //计算镜面反射强度
       vec3 viewDir = normalize(cameraPos - worldPos);
       vec3 reflectDir = reflect(-lightDir, normal);
       float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
       //三种反射最终颜色
          vec3 ambient  = factor.ambient*spotlight.color*diffuseColor;
          vec3 diffuse  = factor.diffuse*diffuseStringth*spotlight.color*intensity*diffuseColor;
          vec3 specular = factor.specular*specularStringth*spotlight.color*intensity*specularColor;
         return vec3(ambient + diffuse + specular);
        
    }
    

    效果:


    image.png

    可以看到斜坡别聚光灯照亮了,我们这里认为聚光灯也不衰减。
    当然一般的模仿路灯的效果这样就足够了,如果做手电筒还是应该做衰减的。我这里就不做衰减了,就是把点光源的衰减方法加在这里就好了。

    手电筒的实现很简单就是把光源的位置换成相机的位置,光源的方向换成视线的方向:
    struct Torch
    {
        float cutOff;//Φ的余玄值
        float outerCutOff;//r的余玄
        float constant;
        float linear;
        float quadratic; 
        vec3 color;
    };
    uniform Torch torch;
    
    vec3 getElectricTorchColor(vec2 texcoord,vec3 Normal,Factor factor,Torch torch,Material material){
      //获取漫反射贴图,镜面反射贴图纹理采样的颜色
        vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
        vec3 specularColor=vec3(texture(material.specular,texcoord));
        vec3 lightDir = normalize(cameraPos-worldPos);
         vec3 cameraViewDir = normalize(cameraPos - cameraView);
        vec3 normal=normalize(Normal);
        //计算光照范围强度
        float theta = dot(lightDir, normalize(cameraViewDir));
       float epsilon = torch.cutOff - torch.outerCutOff;
       float intensity = clamp((theta - torch.outerCutOff) / epsilon,0.0, 1.0);
        // 计算衰减
        float distance = length(cameraPos - worldPos);
        float attenuation = 1.0f/(torch.constant + torch.linear * distance +
        torch.quadratic * (distance * distance));
       //计算漫反射强度
       float diffuseStringth = max(dot(normal,lightDir), 0.0);
       //计算镜面反射强度
       vec3 viewDir = normalize(cameraPos - worldPos);
       vec3 reflectDir = reflect(-lightDir, normal);
       float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
       //三种反射最终颜色
          vec3 ambient  = factor.ambient*torch.color*diffuseColor*attenuation;
          vec3 diffuse  = factor.diffuse*diffuseStringth*torch.color*intensity*diffuseColor*attenuation;
          vec3 specular = factor.specular*specularStringth*torch.color*intensity*specularColor*attenuation;
         return vec3(ambient + diffuse + specular);
    }
    

    效果图:


    image.png

    我们能看见近处被照亮远处亮度就不够了,这就是我们要的效果。如果要开启多个光源,直接把这些返回值相加就好了。
    统一变量发送

     shaderProgram->setUniformValue("factor.ambient", ambientFactor);
        shaderProgram->setUniformValue("factor.diffuse", diffuseFactor);
        shaderProgram->setUniformValue("factor.specular", specularFactor);
    
        shaderProgram->setUniformValue("material.shininess", shininess);
        shaderProgram->setUniformValue("dirDight.direct", direct);
        shaderProgram->setUniformValue("dirDight.color", dirLightColor);
    
        shaderProgram->setUniformValue("pointLight.position", QVector3D(0.0,5.0,-30.0));
        shaderProgram->setUniformValue("pointLight.constant", 1.0f);
        shaderProgram->setUniformValue("pointLight.linear", linear);
        shaderProgram->setUniformValue("pointLight.quadratic", quadratic);
        shaderProgram->setUniformValue("pointLight.color", pointLightColor);
    
        shaderProgram->setUniformValue("spotlight.position", QVector3D(0.0,20.0,-30.0));
        shaderProgram->setUniformValue("spotlight.direction", QVector3D(0.0,1.0,0.0));
        shaderProgram->setUniformValue("spotlight.cutOff", 0.95f);
        shaderProgram->setUniformValue("spotlight.outerCutOff", 0.93f);
        shaderProgram->setUniformValue("spotlight.color", QVector3D(1.0,1.0,1.0));
    
        shaderProgram->setUniformValue("torch.constant", 1.0f);
        shaderProgram->setUniformValue("torch.linear", 0.014f);
        shaderProgram->setUniformValue("torch.quadratic", 0.0007f);
        shaderProgram->setUniformValue("torch.cutOff", 0.91f);
        shaderProgram->setUniformValue("torch.outerCutOff", 0.82f);
        shaderProgram->setUniformValue("torch.color", QVector3D(1.0, 1.0, 1.0));
    

    开启点光源和聚光灯:

    vec3 spotlightColor=getSpotlightColor(texcoord,Normal,factor,spotlight,material);
    vec3 pointLightColor=getPointLightColor(texcoord,Normal,factor,pointLight,material);
    vec3 mixtureColor=spotlightColor+pointLightColor;
    color=vec4(mixtureColor,1.0); 
    
    image.png

    可以看到龙被点光源照亮,光圈被聚光灯照亮。

    目录

    VSC++2019+QT+OpenGL
    QT+OpenGL一之绘制立方体(三角形图元)
    QT+OpenGL二之纹理贴图
    QT+OpenGL三之矩阵简解
    QT+OpenGL四之相机的移动和旋转
    QT+OpenGL五之绘制不同的模型(vao,vbo机制)
    QT+OpenGL六之天空盒
    QT+OpenGL七之使用EBO
    QT+OPenGL八之模型准备
    QT+OPenGL九之模型解码
    QT+OPenGL十之光照模型
    QT+OPenGL十一之漫反射和镜面反射贴图
    QT+OPenGL十二之定向光
    QT+OPenGL十三之真正的点光源和聚光灯
    QT+OPenGL十四之多光源混合的问题
    QT+OPenGL十五之深度缓冲区
    QT+OPenGL十六之模板缓冲区
    QT+OPenGL十七帧缓冲区(离屏渲染)
    QT+OPenGL十八抗锯齿
    QT+OPenGL十九镜面反射效率调整
    QT+OPenGL二十Gamma校正

    相关文章

      网友评论

        本文标题:QT+OPenGL十四之多光源混合的问题

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