• QT+OpenGL高级光照 Blinn-Phong和Gamma校正


    QT+OpenGL高级光照1

    本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主

    Blinn-Phong

    • 冯氏光照:视线与反射方向之间的夹角不小于90度,镜面光分量会变成0.0(不是很合理,会有清晰的分界线)

    • Blinn-Phone模型采用了半程向量,即光线与视线夹角一般方向上的一个单位向量。当半程向量与法线向量越接近,镜面光分量就越大。

    半程向量公式如下:
    H ⃗ = L ⃗ + V ⃗ ∣ ∣ L ⃗ + V ⃗ ∣ ∣ \vec{H} = \frac {\vec{L} + \vec{V}}{||\vec{L} + \vec{V}||} H =∣∣L +V ∣∣L +V

    vec3 lightDir = normalize(lightPos - FragPos);
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 halfWayDir = normalize(lightDir + viewDir);
    ......
    float spec = pow(max(dot(normal halfWayDir), 0.0), shininess)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    效果图:冯氏光照

    在这里插入图片描述

    Gammar校正

    • 阴极射线管显示器(CRT):输入电压翻倍与亮度提高的关系是与Gammar次方相关
      • 设备输入亮度 = 电压的Gamma次幂
    • 这正好与人眼的感知是类似(相反)的

    对于CRT,Gamma通常为2.2

    在这里插入图片描述

    人眼对黑夜的环境更加敏感(把更多的精度分配给了低灰度)

    在这里插入图片描述

    点线:线性的理想情况, Gamma为1

    实线:CRT的实际情况,Gamma为2.2

    (0.5,0.0,0.0)*2 = (1.0, 0.0,0.0)

    对于CRT来说,实际上亮度提高了: 1 / 0.218 约等于 4.587

    所以到目前位置,我们之前配置的颜色和光照变量从物理角度来看都是不正确的

    • 由于颜色是基于显示器的输出进行配置的,因此线性空间中的所有中间值计算在物理上都是不正确的。随着更先进的照明算法的加入,这一点变得更加明显

    有两种方法可以将gamma校正应用于场景:

    • 通过使用OpenGL内置的sRGB帧缓冲支持(glEnable(GL_FRAMEBUFFER_SRGB))

    • 通过自己在片段着色器中执行gamma校正

    float gamma = 2.2;
    FragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));
    
    • 1
    • 2

    sRGB(standard Red GreenBlue)

    微软联合HP、三菱、爱普生等厂商联合开发的通用色彩标准

    • Gamma值为2.2的颜色空间称为sRGB颜色空间(不是100%精确,但是接近)。每个监视器都有自己的Gamma曲线, Gamma值为2.2时候在大多数监视器上显示良好。游戏通常允许玩家更改游戏的Gamma值,因为每个显示器的Gamma设置略有不同。
    • 由于监视器显示应用了Gamma的颜色,因此创建或编辑的所有图片都不是在线性空间中,而是在sRGB空间中。

    纹理在sRGB空间创建和展示,在sRGB空间中使用,不必关心gamma校正纹理,显示也没问题。然而,如果把所有的东西都放在线性空间中展示,纹理颜色就会出现问题。

    在这里插入图片描述

    实际上进行了两次Gamma校正!

    • 基于显示器看到的情况创建一个图像,我们就已经对颜色进行了Gamma校正
    • 代码又进行了一次Gamma校正

    sRGB代码实现

    为了解决重复校正的问题,把这些sRGB纹理在在进行任何颜色值的计算之前变回线性空间:

    float gamma = 2.2;
    vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));
    
    • 1
    • 2

    或者通过OpenGL,自动把颜色校正到线性空间中

    glTexImage2D(GL_TEXTURE2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    
    • 1

    如果在纹理中引入了alpha元素,必须将纹理的内部格式指定为GL_SRGB_ALPHA

    不是所有的纹理都是在sRGB空间中,当把纹理转换成sRGB时要格外小心:

    • 比如diffuse纹理,这种为物体上色的纹理几乎都是在sRGB空间中的
    • 像specular贴图和法线贴图几乎都是在线性空间中
    QImage image(":/resources/wall.jpg");
    specular_texture_ = new QOpenGLTexture(QOpenGLTexture::Target2D);
    glBindTexture(GL_TEXTURE_2D, specular_texture_->textureId());
    glTexImage2D(GL_TEXTURE_2D, GL_SRGB, image.width(), image.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image.bits());
    glGenerateMipmap(GL_TEXTURE_2D);
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    衰减

    在真正的物理世界,光线的衰减与距离的平方成反比:

    float attenuation = 1.0 / (distance * distance);
    
    • 1

    但是当距离小的时候,上面的公式效果会很不对劲,用下面的公式会更好:

    float attenuation = 1.0 / distance;
    
    • 1

    所以以前一直使用下面的公式:
    F a t t = 1.0 K c + K l ∗ d + K q ∗ d 2 F_{att} = \frac{1.0}{K_c + K_l * d + K_q *d^2} Fatt=Kc+Kld+Kqd21.0

    #version 330 core
    
    struct Material {
        sampler2D diffuse;
        sampler2D specular;
        float shininess;
    };
    uniform Material material;
    
    struct Light {
        vec3 pos;
        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
    };
    uniform Light light;
    
    uniform bool gamma;
    
    out vec4 FragColor;
    
    in vec2 TexCoords;
    in vec3 Normal;
    in vec3 FragPos;
    
    uniform vec3 viewPos;
    vec3 BlinnPhong(vec3 normal, vec3 fragPos, vec3 lightPos, vec3 lightColor) {
    
        // diffuse
        vec3 lightDir = normalize(lightPos - fragPos);
        float diff = max(dot(lightDir, normal), 0.0);
        vec3 diffuse = diff * lightColor;
        // specular
        vec3 viewDir = normalize(viewPos - fragPos);
        vec3 reflectDir = reflect(-lightDir, normal);
        float spec = 0.0;
        vec3 halfwayDir = normalize(lightDir + viewDir);
        spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0);
        vec3 specular = spec * lightColor;
        // simple attenuation
        float max_distance = 1.5;
        float distance = length(lightPos - fragPos);
        float attenuation = 1.0 / (gamma ? distance * distance : distance);
        diffuse *= attenuation;
        specular *= attenuation;
        return diffuse + specular;
    }
    
    void main() {
        vec3 diffuseTexColor=vec3(texture(material.diffuse,TexCoords));
        vec3 ambient=light.ambient;
        //........
        //if(gamma) diffuseTexColor = pow(diffuseTexColor, vec3(1.0/2.2));
        vec3 norm = normalize(Normal);
    
        vec3 result ;
        for(int i = -2; i < 2; ++i){
            vec3 lightColor=(2-i)*vec3(0.25);
            result+= BlinnPhong(norm,FragPos,light.pos+i*vec3(2,0.0,0.0),lightColor);
        }
        if(gamma)
        ambient=pow(ambient, vec3(2.2));
        result+=ambient;
        result*=diffuseTexColor*result;
        if(gamma) result = pow(result, vec3(1.0/2.2));
        if(gl_FrontFacing==false)
        FragColor = vec4(result, 1.0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    在这里插入图片描述

  • 相关阅读:
    后厂村路灯在线签名网站,在线签名工具,IPA在线签名
    Web前端系列技术之Web APIs基础(从基础开始)①
    神经网络的三种训练方法,训练神经网络作用大吗
    android.support.v4.view.ViewPager爆红
    给出三个整数,判断大小
    对连续性变量进行LASSO回归分析 cox
    【漏洞复现】RARLAB WinRAR 代码执行漏洞RCE (CVE-2023-38831)
    抓包分析 TCP 协议
    【WLAN】Android 13 WIFI 选网机制--NetworkNominator 解读
    MFC Windows 程序设计[223]之统计图的生成(附源码)
  • 原文地址:https://blog.csdn.net/turbolove/article/details/131132714