LearnOpenGL——Blinn-Phong模型光照、伽马矫正

Blinn-Phong模型光照

Phong模型可以对光照有很好的模拟,但是在镜面反射时可能会有问题。在镜面反射时我们会看到一条明显的分界线,这是因为我们一开始如果观察向量和反射向量夹角超过90度,就钳制为0,但这种情况适用于漫反射,镜面反射效果不好。
在这里插入图片描述

所以Blinn-Phong模型引入了一个半程向量——观察方向与反射方向的角平分线向量h,然后计算h与法线向量的点乘

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);
vec3 specular = lightColor * spec;

这样Blinn-Phong模型的高光看起来会比Phong模型大一点,并且没有明显的分界线。

伽马矫正

在这里插入图片描述

Gamma也叫灰度系数,每种显示设备都有自己的Gamma值,都不相同,有一个公式:设备输出亮度 = 电压的Gamma次幂,任何设备Gamma基本上都不会等于1,等于1是一种理想的线性状态,这种理想状态是:如果电压和亮度都是在0到1的区间,那么多少电压就等于多少亮度。
对于CRT显示器,Gamma通常为2.2,实际显示的通常会比预期的暗;Gamma 0.45时(2.2的倒数)就会比预期的亮。显示器所显示出来的图像和线性图像的最小亮度是相同的,它们最大的亮度也是相同的;只是中间亮度部分会被压暗。

2.2通常是是大多数显示设备的大概平均gamma值。基于gamma2.2的颜色空间叫做sRGB颜色空间

一、什么是伽马矫正

Gamma矫正就是在显示到显示器之前加一个Gamma2.2的倒数Gamma0.45进行矫正。我们在颜色显示到显示器的时候把每个颜色输出都加上这个翻转的Gamma曲线,这样应用了显示器Gamma以后最终的颜色将会变为线性的
在这里插入图片描述

举个例子,我们想要将暗红色 ( 0.5 , 0.0 , 0.0 ) (0.5, 0.0, 0.0) (0.5,0.0,0.0)显示在屏幕上,线性的颜色显示出来相当于降低了2.2次幂的亮度,即 ( 0.5 , 0.0 , 0.0 ) 2.2 = ( 0.218 , 0.0 , 0.0 ) (0.5, 0.0, 0.0)^{2.2} = (0.218, 0.0, 0.0) (0.5,0.0,0.0)2.2=(0.218,0.0,0.0) ,亮度会减弱。如果进行了Gamma矫正,即 ( 0.5 , 0.0 , 0.0 ) 0.45 = ( 0.73 , 0.0 , 0.0 ) (0.5, 0.0, 0.0)^{0.45} = (0.73, 0.0, 0.0) (0.5,0.0,0.0)0.45=(0.73,0.0,0.0) ,然后屏幕再降低2.2次幂亮度时会变回线性空间 ( 0.73 , 0.0 , 0.0 ) 2.2 = ( 0.5 , 0.0 , 0.0 ) (0.73, 0.0, 0.0)^{2.2} = (0.5, 0.0, 0.0) (0.73,0.0,0.0)2.2=(0.5,0.0,0.0)

gamma校正使你可以在线性空间中进行操作。因为线性空间更符合物理世界,大多数物理公式现在都可以获得较好效果,比如真实的光的衰减。你的光照越真实,使用gamma校正获得漂亮的效果就越容易。

二、伽马矫正的方式

1. OpenGL内置的sRGB framebuffer 支持

这个方法实现简单,但是我们可控性就很少,因为是基于硬件自动完成的。我们只需要通过调用 GL_FRAMEBUFFER_SRGB 告诉OpenGL后续的绘图命令将在颜色存储到颜色缓冲区之前进行Gamma矫正

glEnable(GL_FRAMEBUFFER_SRGB); 

gamma校正将把线性颜色空间转变为非线性空间,所以在最后一步进行gamma校正是极其重要的。

2. 在我们自己的片段着色器中进行

我们在片元着色器的最后应用gamma矫正

void main()
{
    // do super fancy lighting 
    [...]
    // apply gamma correction
    float gamma = 2.2;
    fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));
}

但是我们需要在每个片元着色器中都要添加这步操作。不过我们也可以在后处理阶段直接应用到纹理中,这样只要做一次就好

三、sRGB纹理

因为监视器总是在sRGB空间中显示应用了gamma的颜色,所以我们在制作图片的时候,我们创建或编辑的图片并不是在线性空间,而是在sRGB空间中(sRGB空间定义的gamma接近于2.2)

如果我们把纹理应用到渲染应用中,因为现在我们对渲染应用也进行了Gamma矫正,所以对于那些在制作时就已经进行Gamma矫正的纹理,就会变的更亮(被两次Gamma矫正)所以我们需要将纹理勾选为sRGB纹理,这样OpenGL将自动把颜色校正到线性空间中,这样我们所使用的所有颜色值都是在线性空间中的了。

glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);

但并不是所有纹理都需要进行sRGB设置,漫反射纹理是在sRGB空间中,但是用于检索光照的高光贴图和法线贴图几乎总是在线性空间中的,所以这两个就不用再设置为sRGB

四、衰减 Attenuation

在真实世界中,光照衰减近似于与距离平方成反比

float attenuation = 1.0 / (distance * distance);

在应用Gamma矫正之前,显示器中这个衰减效果有点太过强烈而不真实,所以我们使用了另外一个线性衰减函数

float attenuation = 1.0 / distance;

在应用Gamma矫正之后,上面真实世界的二次衰减函数就更加真实。这种差异产生的原因是,光的衰减方程改变了亮度值,而且屏幕上显示出来的也不是线性空间,在监视器上效果最好的衰减方程,并不是符合物理的。

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值