1
0
mirror of https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git synced 2025-08-22 20:25:28 +08:00

Compare commits

...

2 Commits

Author SHA1 Message Date
SuperAo
3bead6b3fd 校对 docs/05 Advanced Lighting/02 Gamma Correction.md
1. 补全翻译
2. 调整译注
3. "monitor"译为"显示器"
2025-03-08 12:13:04 +08:00
LiuZengqiang
3956459af8 Fix error in 07-PBR-IBL-Diffuse irradiance.md (#332)
* Update 06 HDR.md

typo

* Update 06 HDR.md

Delete wrong figure insertion.

* Fix error in 07-PBR-IBL-Diffuse irradiance.md

离散积分 kd*c/pi*... -> kd*c*pi...;
方向向量normalize;
2025-03-08 12:01:22 +08:00
2 changed files with 38 additions and 32 deletions

View File

@@ -4,13 +4,13 @@
---|--- ---|---
作者 | JoeyDeVries 作者 | JoeyDeVries
翻译 | [Django](http://bullteacher.com/) 翻译 | [Django](http://bullteacher.com/)
校对 | 暂无 校对 | [AoZhang](https://github.com/SuperAoao)
!!! note !!! note
本节暂未进行完全的重写,错误可能会很多。如果可能的话,请对照原文进行阅读。如果有报告本节的错误,将会延迟至重写之后进行处理。 本节暂未进行完全的重写,错误可能会很多。如果可能的话,请对照原文进行阅读。如果有报告本节的错误,将会延迟至重写之后进行处理。
当我们计算出场景中所有像素的最终颜色以后,我们就必须把它们显示在监视器上。过去,大多数监视器是阴极射线管显示器CRT。这些监视器有一个物理特性就是两倍的输入电压产生的不是两倍的亮度。输入电压产生约为输入电压的2.2次幂的亮度,这叫做监视器Gamma。 当我们计算出场景中所有像素的最终颜色以后,我们就必须把它们显示在显示器上。过去,大多数显示器是阴极射线管显示器CRT。这些显示器有一个物理特性就是两倍的输入电压并不能产生两倍的亮度。输入电压加倍产生的亮度约为输入电压的2.2次幂,这叫做显示器Gamma。
!!! note "译注" !!! note "译注"
@@ -22,35 +22,40 @@
第一行是人眼所感知到的正常的灰阶亮度要增加一倍比如从0.1到0.2你才会感觉比原来变亮了一倍译注我们在看颜色值从0到1从黑到白的过程中亮度要增加一倍我们才会感受到明显的颜色变化变亮一倍。打个比方颜色值从0.1到0.2我们会感受到一倍的颜色变化而从0.4到0.8我们才能感受到相同程度(变亮一倍)的颜色变化。如果还是不理解,可以参考知乎的[答案](https://www.zhihu.com/question/27467127/answer/37602200)。然而当我们谈论光的物理亮度比如光源发射光子的数量的时候底部第二行的灰阶显示出的才是物理世界真实的亮度。如底部的灰阶显示亮度加倍时返回的也是真实的物理亮度译注这里亮度是指光子数量和正相关的亮度即物理亮度前面讨论的是人的感知亮度物理亮度和感知亮度的区别在于物理亮度基于光子数量感知亮度基于人的感觉比如第二个灰阶里亮度0.1的光子数量是0.2的二分之一),但是由于这与我们的眼睛感知亮度不完全一致(对比较暗的颜色变化更敏感),所以它看起来有差异。 第一行是人眼所感知到的正常的灰阶亮度要增加一倍比如从0.1到0.2你才会感觉比原来变亮了一倍译注我们在看颜色值从0到1从黑到白的过程中亮度要增加一倍我们才会感受到明显的颜色变化变亮一倍。打个比方颜色值从0.1到0.2我们会感受到一倍的颜色变化而从0.4到0.8我们才能感受到相同程度(变亮一倍)的颜色变化。如果还是不理解,可以参考知乎的[答案](https://www.zhihu.com/question/27467127/answer/37602200)。然而当我们谈论光的物理亮度比如光源发射光子的数量的时候底部第二行的灰阶显示出的才是物理世界真实的亮度。如底部的灰阶显示亮度加倍时返回的也是真实的物理亮度译注这里亮度是指光子数量和正相关的亮度即物理亮度前面讨论的是人的感知亮度物理亮度和感知亮度的区别在于物理亮度基于光子数量感知亮度基于人的感觉比如第二个灰阶里亮度0.1的光子数量是0.2的二分之一),但是由于这与我们的眼睛感知亮度不完全一致(对比较暗的颜色变化更敏感),所以它看起来有差异。
因为人眼看到颜色的亮度更倾向于顶部的灰阶,监视器使用的也是一种指数关系电压的2.2次幂),所以物理亮度通过监视器能够被映射到顶部的非线性亮度;因此看起来效果不错译注CRT亮度是是电压的2.2次幂而人眼相当于2次幂因此CRT这个缺陷正好能满足人的需要 因为人眼看到颜色的亮度更倾向于顶部的灰阶,显示器使用的也是一种指数关系电压的2.2次幂),所以物理亮度通过显示器能够被映射到顶部的非线性亮度;因此看起来效果不错。
监视器的这个非线性映射的确可以让亮度在我们眼中看起来更好,但当渲染图像时,会产生一个问题:我们在应用中配置的亮度和颜色是基于监视器所看到的,这样所有的配置实际上是非线性的亮度/颜色配置。请看下图: 显示器的这个非线性映射的确可以让亮度在我们眼中看起来更好,但当渲染图像时,会产生一个问题:我们在应用中配置的亮度和颜色是基于我们从显示器所看到的,这样所有的配置实际上是非线性的亮度/颜色配置。请看下图:
![](../img/05/02/gamma_correction_gamma_curves.png) ![](../img/05/02/gamma_correction_gamma_curves.png)
点线代表线性颜色/亮度值译注这表示的是理想状态Gamma为1实线代表监视器显示的颜色。如果我们把一个点线线性的颜色翻一倍,结果就是这个值的两倍。比如,光的颜色向量\(\bar{L} = (0.5, 0.0, 0.0)\)代表的是暗红色。如果我们在线性空间中把它翻倍,就会变成\((1.0, 0.0, 0.0)\),就像你在图中看到的那样。然而,由于我们定义的颜色仍然需要输出的监视器上,监视器上显示的实际颜色就会是\((0.218, 0.0, 0.0)\)。在这儿问题就出现了:当我们将理想中直线上的那个暗红色翻一倍时,在监视器上实际上亮度翻了4.5倍以上! 点线代表线性颜色/亮度值译注这表示的是理想状态Gamma为1实线代表显示器器显示的颜色。如果我们把一个点线线性的颜色翻一倍,结果就是这个值的两倍。比如,光的颜色向量\(\bar{L} = (0.5, 0.0, 0.0)\)代表的是暗红色。如果我们在线性空间中把它翻倍,就会变成\((1.0, 0.0, 0.0)\),就像你在图中看到的那样。然而,由于我们定义的颜色仍然需要输出的显示器上,显示器上显示的实际颜色就会是\((0.218, 0.0, 0.0)\)。在这儿问题就出现了:当我们将理想中直线上的那个暗红色翻一倍时,在显示器上实际上亮度翻了4.5倍以上!
直到现在我们还一直假设我们所有的工作都是在线性空间中进行的译注Gamma为1最终还是要把所有的颜色输出到监视器上,所以我们配置的所有颜色和照变量物理角度来看都是不正确的,在我们的监视器上很少能够正确地显示。出于这个原因,我们(以及艺术家)通常将光照值设置得比本来更亮一些(由于监视器会将其亮度显示的更暗一些),如果不是这样,在线性空间里计算出来的光照就会不正确。同时,还要记住,监视器所显示出来的图像和线性图像的最小亮度是相同的,它们最大的亮度也是相同的;只是中间亮度部分会被压暗。 直到现在我们还一直假设我们所有的工作都是在线性空间中进行的译注Gamma为1实际上我们一直在显示器的输出空间中工作,所以我们配置的所有颜色和照变量物理上都不正确,但在显示器上看起来(有点)正确。出于这个原因,我们(以及艺术家)通常将光照值设置得比本来更亮一些(由于显示器会将其亮度显示的更暗一些),这导致大多数在线性空间里计算不正确。同时,还要记住,显示器所显示出来的图像和线性图像的最小亮度是相同的,它们最大的亮度也是相同的;只是中间亮度部分会被压暗。
因为所有中间亮度都是线性空间计算出来的译注计算的时候假设Gamma为1监视器显以后实际上都不正确。当使用更高级的光照算法时,这个问题会变得越来越明显,你可以看看下图 因为颜色是根据显示器的输出配置的,所以线性空间中的所有中间(照明)计算在物理上都不正确的。随着更多先进的照明算法的引入,这一点变得更加明显,如下图所示
![](../img/05/02/gamma_correction_example.png) ![](../img/05/02/gamma_correction_example.png)
你可以看到,通过伽玛校正,(更新的)颜色值可以很好地结合在一起,较暗的区域显示更多的细节。总的来说,一个更好的图像质量与一些小的修改。
如果没有适当地纠正这个显示器伽马,照明看起来是错误的,艺术家将很难获得逼真和好看的结果。解决方案正是应用<def>伽马校正</def>
## Gamma校正 ## Gamma校正
Gamma校正(Gamma Correction)的思路是在最终的颜色输出上应用监视器Gamma的倒数。回头看前面的Gamma曲线图你会有一个短划线它是监视器Gamma曲线的翻转曲线。我们在颜色显示到监视器的时候把每个颜色输出都加上这个翻转的Gamma曲线这样应用了监视器Gamma以后最终的颜色将会变为线性的。我们所得到的中间色调就会更亮所以虽然监视器使它们变暗但是我们又将其平衡回来了 Gamma校正(Gamma Correction)的思路是在最终的颜色输出到显示器之前先将Gamma的倒数作用到颜色上。回顾本章前面的伽马曲线图我们看到另一条虚线它与显示器的伽马曲线相反。我们将每个线性输出颜色乘以这个逆伽马曲线(使它们更亮),一旦颜色显示在显示器上,显示器的伽马曲线就被应用,结果颜色就变成线性的。我们有效地使中间颜色变亮,这样一旦显示器变暗,它们就会平衡
我们来看另一个例子。还是那个暗红色\((0.5, 0.0, 0.0)\)。在将颜色显示到监视器之前我们先对颜色应用Gamma校正曲线。线性的颜色显示在监视器上相当于降低了\(2.2\)次幂的亮度,所以倒数就是\(1/2.2\)次幂。Gamma校正后的暗红色就会成为\((0.5, 0.0, 0.0)^{1/2.2} = (0.5, 0.0, 0.0)^{0.45} = (0.73, 0.0, 0.0)\)。校正后的颜色接着被发送给监视器,最终显示出来的颜色是\((0.73, 0.0, 0.0)^{2.2} = (0.5, 0.0, 0.0)\)。你会发现使用了Gamma校正监视器最终会显示出我们在应用中设置的那种线性的颜色。 我们来看另一个例子。还是那个暗红色\((0.5, 0.0, 0.0)\)。在将颜色显示到显示器之前我们先对颜色应用Gamma校正曲线。线性的颜色显示在显示器上相当于降低了\(2.2\)次幂的亮度,所以倒数就是\(1/2.2\)次幂。Gamma校正后的暗红色就会成为\((0.5, 0.0, 0.0)^{1/2.2} = (0.5, 0.0, 0.0)^{0.45} = (0.73, 0.0, 0.0)\)。校正后的颜色接着被发送给显示器,最终显示出来的颜色是\((0.73, 0.0, 0.0)^{2.2} = (0.5, 0.0, 0.0)\)。你会发现使用了Gamma校正显示器最终会显示出我们在应用中设置的那种线性的颜色。
!!! Important !!! Important
2.2通常是是大多数显示设备的大概平均gamma值。基于gamma2.2的颜色空间叫做sRGB颜色空间。每个监视器的gamma曲线都有所不同但是gamma2.2在大多数监视器上表现都不错。出于这个原因游戏经常都会为玩家提供改变游戏gamma设置的选项以适应每个监视译注现在Gamma2.2相当于一个标准后文中你会看到。但现在你可能会问前面不是说Gamma2.2看起来不是正好适合人眼么为何还需要校正。这是因为你在程序中设置的颜色比如光照都是基于线性Gamma即Gamma1所以你理想中的亮度和实际表达出的不一样如果要表达出你理想中的亮度就要对这个光照进行校正)。 2.2通常是是大多数显示设备的大概平均gamma值。基于gamma2.2的颜色空间叫做sRGB颜色空间。每个显示器的gamma曲线都有所不同但是gamma2.2在大多数显示器上表现都不错。出于这个原因游戏经常都会为玩家提供改变游戏gamma设置的选项以适应每个显示译注现在Gamma2.2相当于一个标准后文中你会看到。但现在你可能会问前面不是说Gamma2.2的曲线不是和人眼识别亮度的特点相同吗为何还需要校正。其实我们不需要考虑人眼识别亮度的特点我们重点需要关注保持亮度的线性变化特点。你在程序中设置的颜色比如光照都是基于线性Gamma即Gamma1比如1.0的亮度应该是0.5的二倍如果不进行gamma矫正线性0.5的亮度会输出为0.22线性1.0的亮度会被输出为1.0,亮度相差了很多倍,和你的预期会很不一样)。
有两种在你的场景中应用gamma校正的方式 有两种在你的场景中应用gamma校正的方式
使用OpenGL内建的sRGB帧缓冲。 * 使用OpenGL内建的sRGB帧缓冲。
自己在像素着色器中进行gamma校正。 * 自己在像素着色器中进行gamma校正。
第一个选项也许是最简单的方式但是我们也会丧失一些控制权。开启GL_FRAMEBUFFER_SRGB可以告诉OpenGL每个后续的绘制命令里在颜色储存到颜色缓冲之前先校正sRGB颜色。sRGB这个颜色空间大致对应于gamma2.2它也是家用设备的一个标准。开启GL_FRAMEBUFFER_SRGB以后每次像素着色器运行后续帧缓冲OpenGL将自动执行gamma校正包括默认帧缓冲。
第一个选项也许是最简单的方式但是我们也会丧失一些控制权。开启GL_FRAMEBUFFER_SRGB可以告诉OpenGL每个后续的绘制命令里在颜色储存到颜色缓冲之前先校正sRGB颜色。sRGB这个颜色空间大致对应于gamma2.2它也是大多数设备的一个标准。开启GL_FRAMEBUFFER_SRGB以后每次像素着色器运行后续帧缓冲OpenGL将自动执行gamma校正包括默认帧缓冲。
开启GL_FRAMEBUFFER_SRGB简单的调用glEnable就行 开启GL_FRAMEBUFFER_SRGB简单的调用glEnable就行
@@ -58,16 +63,16 @@ Gamma校正(Gamma Correction)的思路是在最终的颜色输出上应用监视
glEnable(GL_FRAMEBUFFER_SRGB); glEnable(GL_FRAMEBUFFER_SRGB);
``` ```
自此你渲染的图像就被进行gamma校正处理你不需要做任何事情硬件就帮你处理了。有时候你应该记得这个建议gamma校正将把线性颜色空间转为非线性空间,所以在最后一步进行gamma校正是极其重要的。如果你在最后输出之前就进行gamma校正所有的后续操作都是在操作不正确的颜色值。例如如果你使用多个帧缓冲你可能打算让两个帧缓冲之间传递的中间结果仍然保持线性空间颜色只是给发送给监视器的最后的那个帧缓冲应用gamma校正。 自此你渲染的图像就被进行gamma校正处理你不需要做任何事情硬件就帮你处理了。使用这种方法(和其他方法)你应该记住的重点是,伽马校正(也)将颜色从线性空间转为非线性空间,所以在最后一步进行伽马校正。如果你在最后输出之前就进行gamma校正所有的后续操作都是在操作不正确的颜色值。例如如果你使用多个帧缓冲你可能打算让两个帧缓冲之间传递的中间结果仍然保持线性空间颜色只是给发送给显示器的最后的那个帧缓冲应用gamma校正。
第二方法稍微复杂点但同时也是我们对gamma操作有完全控制。我们在每个相关像素着色器运行的最后应用gamma校正所以在发送到帧缓冲前颜色就被校正 第二方法需要更多的工作,但也让我们完全控制伽马操作。我们在每个相关像素着色器运行结束时应用伽玛校正,因此最终的颜色在发送到显示器之前结束伽玛校正。
```c++ ```c++
void main() void main()
{ {
// do super fancy lighting // 在线性空间做炫酷的光照效果
[...] [...]
// apply gamma correction // 应用伽马矫正
float gamma = 2.2; float gamma = 2.2;
fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma)); fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));
} }
@@ -75,23 +80,23 @@ void main()
最后一行代码将fragColor的每个颜色元素应用有一个1.0/gamma的幂运算校正像素着色器的颜色输出。 最后一行代码将fragColor的每个颜色元素应用有一个1.0/gamma的幂运算校正像素着色器的颜色输出。
方法个问题是为了保持一致,你必须在像素着色器里加上这个gamma校正所以如果你有很多像素着色器它们可能分别用于不同物体那么你就必须每个着色器里都加上gamma校正了。一个更简单的方案是在你的渲染循环中引入后处理阶段,在后处理四边形上应用gamma校正,这样你只要做一次就好了 方法的一个问题是为了保持一致,你必须对每个有助于最终输出的片段着色器应用伽玛校正。如果你有多个对象的十几个片段着色器,你必须每个着色器添加伽马校正代码。一个更简单的解决方案是在你的渲染循环中引入一个后处理阶段,在后处理四边形上应用伽马校正作为最后一步,这样你只要做一次伽马矫正即可
单行代码代表了gamma校正的实现。不令人印象深刻但当你进行gamma校正的时候有一些额外的事情别忘了考虑。 单行代码代表了gamma校正的实现。不那么令人印象深刻但当你进行gamma校正的时候有一些额外的事情别忘了考虑。
### sRGB纹理 ### sRGB纹理
因为监视器总是在sRGB空间中显示应用了gamma的颜色无论什么时候当你在计算机上绘制、编辑或者画出一个图片的时候,你所选的颜色都是根据你在监视器上看到的那种。这实际意味着所有你创建或编辑的图片并不是在线性空间而是在sRGB空间中译注sRGB空间定义的gamma接近于2.2),假如在你的屏幕上对暗红色翻一倍,便是根据你所感知到的亮度进行的,并不等于将红色元素加倍。 因为显示器总是在sRGB空间中显示应用了gamma的颜色无论什么时候当你在计算机上绘制、编辑或者绘制一个图片的时候,你所选的颜色都是根据你在显示器上看到的那种。这实际意味着所有你创建或编辑的图片并不是在线性空间而是在sRGB空间中译注sRGB空间定义的gamma接近于2.2),假如在你的屏幕上对暗红色翻一倍,便是根据你所感知到的亮度进行的,并不等于将红色元素加倍。
结果就是纹理编辑者所创建的所有纹理都是在sRGB空间中的纹理所以如果我们在渲染应用中使用这些纹理我们必须考虑到这点。在我们应用gamma校正之前这不是个问题因为纹理在sRGB空间创建和展示同样我们还是在sRGB空间中使用从而不必gamma校正纹理显示也没问题。然而现在我们是把所有东西都放在线性空间中展示的纹理颜色就会变坏如下图展示的那样 结果就是纹理编辑者所创建的所有纹理都是在sRGB空间中的纹理所以如果我们在渲染应用中使用这些纹理我们必须考虑到这点。在我们不知道gamma校正之前这不是个问题因为纹理在sRGB空间创建和展示同样我们还是在sRGB空间中使用从而不必gamma校正纹理显示也没问题。然而现在我们是把所有东西都放在线性空间中展示的纹理颜色就会变坏如下图展示的那样
![](../img/05/02/gamma_correction_srgbtextures.png) ![](../img/05/02/gamma_correction_srgbtextures.png)
纹理图像实在太亮了,发生这种情况是因为它们实际上进行了两次gamma校正想一想当我们基于监视器上看到的情况创建一个图像,我们就已经对颜色值进行了gamma校正所以再次显示在监视器上就没错。由于我们在渲染中又进行了一次gamma校正图片就实在太亮了。 纹理图像太亮了,这是因为它实际上被伽玛校正了两次!想想看,当我们根据在显示器上看到的创建图像,我们有效地对图像的颜色值进行伽玛校正,使其在显示器上看起来正确。因为我们在渲染器中再次进行伽玛校正,图像最终变得太亮了。
为了修复这个问题,我们确保纹理制作者是在线性空间中进行创作的。但是由于大多数纹理制作者并不知道什么是gamma校正并且在sRGB空间中进行创作更简单这也许不是一个好办法 为了解决这个问题,我们必须确保纹理美工在线性空间中工作。然而因为在sRGB空间中工作更容易而且大多数工具甚至不支持线性纹理所以这可能不是首选的解决方案
另一个解决方案是重校或把这些sRGB纹理在进行任何颜色值的计算前变回线性空间。我们可以这样做 另一个解决方案是在对其颜色值进行任何计算之前重新校正或将这些sRGB纹理转换为线性空间。我们可以这样做
```c++ ```c++
float gamma = 2.2; float gamma = 2.2;
@@ -106,7 +111,7 @@ vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
``` ```
如果你还打算在你的纹理中引入alpha元素必究必须将纹理的内部格式指定为GL_SRGB_ALPHA。 如果你还打算在你的纹理中引入alpha元素必须将纹理的内部格式指定为GL_SRGB_ALPHA。
因为不是所有纹理都是在sRGB空间中的所以当你把纹理指定为sRGB纹理时要格外小心。比如diffuse纹理这种为物体上色的纹理几乎都是在sRGB空间中的。而为了获取光照参数的纹理像specular贴图和法线贴图几乎都在线性空间中所以如果你把它们也配置为sRGB纹理的话光照就坏掉了。指定sRGB纹理时要当心。 因为不是所有纹理都是在sRGB空间中的所以当你把纹理指定为sRGB纹理时要格外小心。比如diffuse纹理这种为物体上色的纹理几乎都是在sRGB空间中的。而为了获取光照参数的纹理像specular贴图和法线贴图几乎都在线性空间中所以如果你把它们也配置为sRGB纹理的话光照就坏掉了。指定sRGB纹理时要当心。
@@ -115,7 +120,7 @@ glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BY
## 衰减 ## 衰减
在使用了gamma校正之后另一个不同之处光照衰减(Attenuation)。真实的物理世界中,光照的衰减光源距离的平方成反比。 伽马校正的另一个不同之处在于光照衰减。真实的物理世界中,光衰减光源距离的平方成反比。在正常的英语中,它只是意味着光强度随着距离光源的平方而减少,如下图所示
```c++ ```c++
float attenuation = 1.0 / (distance * distance); float attenuation = 1.0 / (distance * distance);
@@ -131,18 +136,19 @@ float attenuation = 1.0 / distance;
![](../img/05/02/gamma_correction_attenuation.png) ![](../img/05/02/gamma_correction_attenuation.png)
这种差异产生的原因是,光的衰减方程改变了亮度值,而且屏幕上显示出来的也不是线性空间,在监视器上效果最好的衰减方程并不是符合物理的。想想平方衰减方程如果我们使用这个方程而且不进行gamma校正显示在监视器上的衰减方程实际上将变成\((1.0 / distance^2)^{2.2}\)。若不进行gamma校正将产生更强烈的衰减。这也解释了为什么双曲线不用gamma校正时看起来更真实因为它实际变成了\((1.0 / distance)^{2.2} = 1.0 / distance^{2.2}\)。这和物理公式是很相似的。 这种差异产生的原因是,光的衰减方程改变了亮度值,而且屏幕上显示出来的也不是线性空间,在显示器上效果最好的衰减方程并不是符合物理的。想想平方衰减方程如果我们使用这个方程而且不进行gamma校正显示在监视器上的衰减方程实际上将变成\((1.0 / distance^2)^{2.2}\)。若不进行gamma校正将产生更强烈的衰减。这也解释了为什么双曲线不用gamma校正时看起来更真实因为它实际变成了\((1.0 / distance)^{2.2} = 1.0 / distance^{2.2}\)。这和物理公式是很相似的。
!!! Important !!! Important
我们在基础光照教程中讨论的更高级的那个衰减方程在有gamma校正的场景中也仍然有用因为它可以让我们对衰减拥有更多准确的控制权不过在进行gamma校正的场景中当然需要不同的参数 我们在基础光照教程中讨论的更高级的那个衰减方程在有gamma校正的场景中也仍然有用因为它可以让我们对衰减拥有更多准确的控制权不过在进行gamma校正的场景中当然需要不同的参数
我创建的这个简单的demo场景你可以在这里找到源码以及顶点和像素着色器。按下空格就能在有gamma校正和无gamma校正的场景进行切换两个场景使用的是相同的纹理和衰减。这不是效果最好的demo不过它能展示出如何应用所有这些技术。 我创建的这个简单的demo场景你可以在[这里](https://learnopengl.com/code_viewer_gh.php?code=src/5.advanced_lighting/2.gamma_correction/gamma_correction.cpp)找到源码。按下空格就能在有gamma校正和无gamma校正的场景进行切换两个场景使用的是相同的纹理和衰减。这不是效果最好的demo不过它能展示出如何应用所有这些技术。
总而言之gamma校正使你可以在线性空间中进行操作。因为线性空间更符合物理世界大多数物理公式现在都可以获得较好效果比如真实的光的衰减。你的光照越真实使用gamma校正获得漂亮的效果就越容易。这也正是为什么当引进gamma校正时建议只去调整光照参数的原因。 总而言之gamma校正使你可以在线性空间中进行操作。因为线性空间更符合物理世界大多数物理公式现在都可以获得较好效果比如真实的光的衰减。你的光照越真实使用gamma校正获得漂亮的效果就越容易。这也正是为什么当引进gamma校正时建议只去调整光照参数的原因。
## 附加资源 ## 附加资源
- [What every coder should know about gamma](https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/)John Novak写的一篇关于伽马校正的深度文章。
- [cambridgeincolour.com](http://www.cambridgeincolour.com/tutorials/gamma-correction.htm):更多关于gamma和gamma校正的内容。 - [cambridgeincolour.com](http://www.cambridgeincolour.com/tutorials/gamma-correction.htm):更多关于gamma和gamma校正的内容。
- [wolfire.com](http://blog.wolfire.com/2010/02/Gamma-correct-lighting): David Rosen关于在渲染领域使用gamma校正的好处 - [blog.wolfire.com](http://blog.wolfire.com/2010/02/Gamma-correct-lighting): 这是David Rosen的一篇关于伽马校正在图形渲染中的好处的博客
- [renderwonk.com](http://renderwonk.com/blog/index.php/archive/adventures-with-gamma-correct-rendering/): 一些额外的实践上的思考。 - [renderwonk.com](http://renderwonk.com/blog/index.php/archive/adventures-with-gamma-correct-rendering/): 一些额外的实践上的思考。

View File

@@ -375,7 +375,7 @@ $$
$$ $$
L_o(p,\phi_o, \theta_o) = L_o(p,\phi_o, \theta_o) =
k_d\frac{c}{\pi} \frac{1}{n1 n2} \sum_{\phi = 0}^{n1} \sum_{\theta = 0}^{n2} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta k_d \frac{c\pi}{n1 n2} \sum_{\phi = 0}^{n1} \sum_{\theta = 0}^{n2} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta
$$ $$
当我们离散地对两个球坐标轴进行采样时,每个采样近似代表了半球上的一小块区域,如上图所示。注意,由于球的一般性质,当采样区域朝向中心顶部会聚时,天顶角 \(\theta\) 变高,半球的离散采样区域变小。为了平衡较小的区域贡献度,我们使用 \(sin\theta\) 来权衡区域贡献度,这就是多出来的 \(sin\) 的作用。 当我们离散地对两个球坐标轴进行采样时,每个采样近似代表了半球上的一小块区域,如上图所示。注意,由于球的一般性质,当采样区域朝向中心顶部会聚时,天顶角 \(\theta\) 变高,半球的离散采样区域变小。为了平衡较小的区域贡献度,我们使用 \(sin\theta\) 来权衡区域贡献度,这就是多出来的 \(sin\) 的作用。
@@ -386,8 +386,8 @@ $$
vec3 irradiance = vec3(0.0); vec3 irradiance = vec3(0.0);
vec3 up = vec3(0.0, 1.0, 0.0); vec3 up = vec3(0.0, 1.0, 0.0);
vec3 right = cross(up, normal); vec3 right = normalize(cross(up, normal));
up = cross(normal, right); up = normalize(cross(normal, right));
float sampleDelta = 0.025; float sampleDelta = 0.025;
float nrSamples = 0.0; float nrSamples = 0.0;