1、 Phong、Blinn-Phong光照模型是一种简单光照模型,它仅考虑了光源直接照射的效果,没有考虑非直接光照的效果。如下直接光照与间接光照。
2、上述模型在考虑间接光照(环境光)时,采用的是一个常数来表示,因此存在一定的局限性。全局光照模拟(Global illumination,GI)= 直接光照 + 间接光照。为了实时计算光照,引入了很多间接光照的计算方法,光线跟踪算法,考虑了光滑表面对环境光的反射和折射。
本文同时发布在我的个人博客上: https://dragon_boy.gitee.io
这一章我们使用之前讲解过的PBR理论进行使用直接光源(点光源、平行光、聚光灯等)的光照渲染。
先给出反射方程:
通过上面的点光源的理论,我们还可以拓展到平行光和聚光灯,比如平行光的入射光方向 是一个常量,不考虑光的衰减;聚光灯的辐射强度不是一个常量,会通过聚光等的方向向量进行缩放。
我们首先根据前面的理论在片元着色器中实现PBR模型,首先,设置相关输入变量和uniform变量:
接着计算法线和观察方向:
这里我们将使用4个点光源来照亮场景,我们首先遍历每个光源,计算单独的辐射率,接着通过BRDF和光的入射角缩放,最后合并起来。在循环中,我们先计算好每个光源公用的变量:
由于我们在线性空间计算光照,我们通过平方反比定律来衰减光照(物理正确)。
接着对每个光源我们使用 Cook-Torrance BRDF:
不过首先我们要计算反射和折射的比例,这通过菲涅尔等式计算:
菲涅尔等式将基本反射率 作为参数。
在main函数中,我们首先将基本反射率定位0.04,这适用于大多数的非金属材质,接着我们根据设置的金属度与表面颜色进行混合,以达到金属和非金属之间的切换,接着用上述菲涅尔等式计算反射的比例:
计算完反射的部分后,还剩下D和G,即发现分布函数和几何函数。
在PBR理论那一章介绍过D和G的实现:
接着在循环中计算NDF和G的部分:
接着计算BRDF:
注意我们将分母控制在0.001内,以免分母为0。
在反射方程中,我们将菲涅尔等式的结果F作为反射比率 ,我们可以根据这个计算折射比率 :
由于我们不考虑金属表面的折射,所以我们*=1-metallic来消除金属部分的折射。
最后,就可以计算每个光源的反射率,并逐一相加得到反照率:
最后输出颜色的时候记得加上环境光:
在上面的计算中我们都是在线性空间中进行的,所以我们需要进行伽马矫正。在之前讲解AO时,我们知道它本身是一个超出LDR范围的值,即HDR范围,如果单纯的截取LDR的范围的话,AO值会很高,所以在进行伽马矫正前需要先将其通过色调映射合理的转化到LDR的范围:
最后,完整的片元着色器如下:
下面是使用这个片元着色器渲染的一些例子(4个光源,拥有不同金属度和粗糙度的一些球体):
若要在PBR流程中使用纹理,我们需要将albedo、normal、metallic、roughness、ao这些参数使用纹理采样:
注意到albedo颜色纹理大多数时候是在sRGB空间制作的,所以我们首先将其转化到线性空间(注意!PBR必须在线性空间计算光照,否则结果不准确),AO图也可能在sRGB空间进行制作的,所以按情况判断。
下面是使用一些纹理的PBR光照例子:
着色器的输出没有了镜面光照效果,对所有的顶点都进行了高亮照明。于是进行了着色器调试,最终发现居然是变量数据类型的问题。着色器中材质结构体的定义如下:
struct MaterialInfo
{
vec3 Ka
vec3 Kd
vec3 Ks
float Shininess
}
uniform MaterialInfo Material
在C++程序中,Material.Shininess值的设置如下:
program.setUniform("Material.Shininess", 200)
传到OpenGL端的Shininess的值200是int型变量!而在着色器中定义的是float型变量!将200修改为200.0f后,镜面光照就变回来了。
原因:在应用程序端将数据传递到着色器时,是进行内存复制,但定点数和浮点数的内存表示不一样,所以传递到着色器的数据不是预期值。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)