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

简介: 冯氏光照:视线与反射方向之间的夹角不小于90度,镜面光分量会变成0.0(不是很合理,会有清晰的分界线)Blinn-Phone模型采用了半程向量,即光线与视线夹角一般方向上的一个单位向量。当半程向量与法线向量越接近,镜面光分量就越大。

QT+OpenGL高级光照1

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


Blinn-Phong

  • 冯氏光照:视线与反射方向之间的夹角不小于90度,镜面光分量会变成0.0(不是很合理,会有清晰的分界线)
  • Blinn-Phone模型采用了半程向量,即光线与视线夹角一般方向上的一个单位向量。当半程向量与法线向量越接近,镜面光分量就越大。


半程向量公式如下:

屏幕截图 2023-08-04 145512.png

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)

效果图:冯氏光照

image.gif

Gammar校正

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


对于CRT,Gamma通常为2.2

46a431f055574cf4b55cd9b0a06511af.png

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

784f8d5cef2c404c98248c08c88a1001.png

点线:线性的理想情况, 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));

sRGB(standard Red GreenBlue)


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


   ●  Gamma值为2.2的颜色空间称为sRGB颜色空间(不是100%精确,但是接近)。每个监视器都有自己的Gamma曲线, Gamma值为2.2时候在大多数监视器上显示良好。游戏通常允许玩家更改游戏的Gamma值,因为每个显示器的Gamma设置略有不同。

   ●  由于监视器显示应用了Gamma的颜色,因此创建或编辑的所有图片都不是在线性空间中,而是在sRGB空间中。


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

87522cc38bcf42498f309e66e07610c8.png

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


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


sRGB代码实现

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


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

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

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

如果在纹理中引入了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);

caf135ea428646d99baa573165eaa5fe.png

衰减

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

float attenuation = 1.0 / (distance * distance);

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

float attenuation = 1.0 / distance;

所以以前一直使用下面的公式:

屏幕截图 2023-08-04 150226.png

#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);
}

b4d57109e9624bcd988a5665e6ea0a00.gif

目录
相关文章
|
6月前
QT4.7版本的OPENGL的3D旋转模型例子
QT4.7版本的OPENGL的3D旋转模型例子
127 0
|
3月前
|
Linux
关于linux的qt发布(linuxdeployqt)中opengl版本过高的解决
关于linux的qt发布(linuxdeployqt)中opengl版本过高的解决
|
6月前
|
机器学习/深度学习 API vr&ar
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
949 4
|
6月前
|
Linux API iOS开发
【Qt 渲染引擎】一文带你了解qt的三种 渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统
【Qt 渲染引擎】一文带你了解qt的三种 渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统
200 0
|
6月前
|
Linux Windows
imx6ull开发板之qt应用编程读取AP3216c(光照,距离)数据。
imx6ull开发板之qt应用编程读取AP3216c(光照,距离)数据。
102 0
|
6月前
Qt+OpenGL 打砖块游戏
Qt+OpenGL 打砖块游戏
72 0
|
6月前
|
数据可视化 API vr&ar
qt“五彩斑斓“ opengl
qt“五彩斑斓“ opengl
QT圆形进度条(QT桌面项目光照强度检测)
QT圆形进度条(QT桌面项目光照强度检测)
107 0
|
编解码 异构计算
QT+OpenGL实例化和抗锯齿
如果将数据一次性发送给GPU,然后使用一个绘制函数让OpenGL利用这些数据绘制多个物体,就会方便了。这就是实例化(Instancing)。
278 0
|
6月前
|
XML 小程序 Java
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
124 0