Advanced Lighting
在照明章节中,我们简要介绍了Phong照明模型,以带来基本数量的现实主义到我们的场景。Phong模型看起来不错,但有一些细微的差别,我们将在本章关注。
Blinn-Phong
Phong照明是一个伟大的和非常有效的近似照明,但它的镜面反射分解在某些条件下,特别是当亮度低导致一个大的(粗糙的)镜面区域。下面的图片展示了当我们在平面纹理上使用1。0的镜面亮度指数时会发生什么:
你可以在边缘看到镜面区域立即被切断。这是因为视图和反射向量之间的角度没有超过90度。如果角度大于90度,所得到的点积就变成负的,结果是镜面指数为0.0。你可能会想这不会有问题因为我们不会得到任何角度大于90度的光线,对吧?
错误,这只适用于漫反射部分,当法线和光源之间的角度大于90度时,意味着光源在被照亮的表面之下,因此光线的漫反射贡献应该等于0.0。然而,在高光下,我们不是在测量光源和法线之间的角度,而是在视图和反射向量之间。看看下面两张图片:
在这里,问题应该变得显而易见。左边的图像显示的Phong反射为熟悉,与 小于90度。在右边的图像中我们可以看到这个角度 在视图和反射向量之间是大于90度的,这将导致镜面贡献无效。这通常不是一个问题,因为视图方向离反射方向很远,但如果我们使用一个低的反射指数,反射半径就足够大,在这些条件下有贡献。因为我们在大于90度的角度上抵消了这个贡献,我们得到了第一张图中看到的伪影。
1977年,Blinn-Phong阴影模型被James F. Blinn引入,作为我们迄今为止使用的Phong阴影的扩展。Blinn-Phong模型与镜面模型有很大的相似之处,但与镜面模型略有不同,因此克服了我们的问题。不依赖于反射向量,我们使用了所谓的中间向量,这是一个单位向量,正好在视图方向和光线方向的中间。这个中间向量与表面的法向量越接近,镜面的贡献就越高。
当视图方向与反射向量(现在是假想的)完全对齐时,一半向量与法向量完全对齐。视图方向越接近原始反射方向,高光就越强。
在这里你可以看到,无论观察者从哪个方向看,中间矢量和表面法线之间的角度永远不会超过90度(当然,除非光线远低于表面)。结果与Phong反射略有不同,但通常在视觉上更合理,特别是低镜面指数。Blinn-Phong材质模型也是OpenGL早期固定函数管道中使用的材质模型。
得到中间矢量很容易,我们把光线的方向矢量和视图矢量加在一起,然后对结果进行规格化:
This translates to GLSL code as follows:
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和Phong镜面反射的唯一区别是我们现在测量的是法线和中间矢量之间的角度,而不是视图和反射矢量之间的角度。
随着半矢量的引入,我们将不再有Phong阴影的高光截止问题。下图显示了两种方法的高光区域,高光指数为0.5:
另一个细微的差别在Phong和Blinn-Phong阴影之间是中间向量和表面法线之间的角度通常比视角和反射向量之间的角度短。因此,为了得到类似于Phong阴影的视觉效果,镜面亮度指数必须设置得更高一些。一般的经验规则是设置它之间的2到4倍的冯氏光泽指数。
以下是Phong指数设置为8.0的镜面反射模型和Blinn-Phong组件设置为32.0的镜面反射模型的比较:
你可以看到,Blinn-Phong的高光指数比Phong更尖锐。它通常需要一点调整,以获得类似的结果,就像你以前与Phong阴影。这是值得的,虽然Blinn-Phong阴影通常是更现实的比默认Phong阴影。
这里,我们使用了一个简单的片段着色器,在规则Phong反射和Blinn-Phong反射之间切换:
void main()
{
[...]
float spec = 0.0;
if(blinn)
{
vec3 halfwayDir = normalize(lightDir + viewDir);
spec = pow(max(dot(normal, halfwayDir), 0.0), 16.0);
}
else
{
vec3 reflectDir = reflect(-lightDir, normal);
spec = pow(max(dot(viewDir, reflectDir), 0.0), 8.0);
}
您可以在这里here.找到这个简单演示的源代码。按b键,演示切换从Phong到Blinn-Phong照明和vica反之。