LearnOpenGL学习笔记—PBR:光照
本节对应官网学习内容:光照
结合英文原站,中文站,以及个人实践进行描述。
并且结合了个人在games101中的相关笔记
GAMES101课程学习笔记—Lec 14(2)~16:Ray Tracing(2) BRDF、渲染方程、全局光照、路径追踪
GAMES101课程学习笔记—Lec 17:Materials and Appearances 材质和外观
本项目地址点击 这里这里这里
1 官网
1.1引入
在上一个教程中,我们讨论了一些PBR渲染的基础知识。
在本章节中,我们将重点放在把以前讨论过的理论转化为实际的渲染器,这个渲染器将使用直接的(或解析的)光源:比如点光源,定向灯或聚光灯。
我们先来看看上一个章提到的反射方程的最终版:
L o ( p , ω o ) = ∫ Ω ( k d c π + k s D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) ) L i ( p , ω i ) n ⋅ ω i d ω i L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i Lo(p,ωo)=Ω∫(kdπc+ks4(ωo⋅n)(ωi⋅n)DFG)Li(p,ωi)n⋅ωidωi
我们大致上清楚这个反射方程在干什么,但我们仍然留有一些迷雾尚未揭开。
比如说我们究竟将怎样表示场景上的入射辐照度(Irradiance),出射辐射度(Radiance)?
我们知道Radiance是某个单位面向某个单位立体角辐射出去的能量
在我们的情况下,不妨假设立体角 ω ω ω无限小,这样辐射度就表示光源在一条光线或单个方向向量上的辐射通量。
基于以上的知识,我们如何将其转化为以前的教程中积累的一些光照知识呢?
那么想象一下,我们有一个点光源(一个光源在所有方向具有相同的亮度)。
它的辐射通量(Radiant Flux)为用RGB表示为(23.47,21.31,20.79)
该光源的辐射强度(Radiant Intensity) 等于其在每个出射光线的辐射通量。
然而,当我们为一个表面上的特定的点p着色时,在其半球领域Ω的所有可能的入射方向上,只有一个入射方向向量ωi直接来自于该点光源。
假设我们在场景中只有一个光源,位于空间中的某一个点,因而对于p点的,其他可能的入射光线方向上过来的Radiance为0:
如果从一开始,我们就假设点光源不受光线衰减(光照强度会随着距离变暗)的影响,那么无论我们把光源放在哪,入射光线的Radiance总是一样的(除去入射角cosθ对Radiance的影响之外)。
正是因为无论我们从哪个角度观察它,点光源总具有相同的Intensity,我们可以有效地将其Intensity建模为其Flux: 一个常量向量(23.47,21.31,20.79)。
然而,算Radiance也需要将位置p作为输入,正如所有现实的点光源都会受光线衰减影响一样,点光源的Intensity应该根据点p所在的位置和光源的位置以及他们之间的距离而做一些缩放。
因此,根据原始的方程,我们会根据表面法向量n和入射角度wi来缩放光源的Intensity。
在实现上来说:对于直接点光源的情况,Radiance函数L先获取光源的颜色值, 然后光源和某点p的距离衰减,接着按照n⋅wi缩放,但是仅仅有一条入射角为wi的光线打在点p上, 这个wi同时也等于在p点光源的方向向量。
写成代码的话会是这样:
vec3 lightColor = vec3(23.47, 21.31, 20.79);
vec3 wi = normalize(lightPos - fragPos);
float cosTheta = max(dot(N, Wi), 0.0);
float attenuation = calculateAttenuation(fragPos, lightPos);
float radiance = lightColor * attenuation * cosT