本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主
冯氏光照:视线与反射方向之间的夹角不小于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)
效果图:冯氏光照
对于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));
sRGB(standard Red GreenBlue)
微软联合HP、三菱、爱普生等厂商联合开发的通用色彩标准
纹理在sRGB空间创建和展示,在sRGB空间中使用,不必关心gamma校正纹理,显示也没问题。然而,如果把所有的东西都放在线性空间中展示,纹理颜色就会出现问题。
实际上进行了两次Gamma校正!
为了解决重复校正的问题,把这些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时要格外小心:
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);
在真正的物理世界,光线的衰减与距离的平方成反比:
float attenuation = 1.0 / (distance * distance);
但是当距离小的时候,上面的公式效果会很不对劲,用下面的公式会更好:
float attenuation = 1.0 / distance;
所以以前一直使用下面的公式:
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+Kl∗d+Kq∗d21.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);
}