现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)

前言:该系列教程主要参考自网站www.opengl-tutorial.org和learnopengl.com/,基于开源GUI框架imgui v1.61实现,imgui自带的例子里面直接集成了glfw+gl3w环境,本系列教程将gl3w换成了glew,glew具体环境配置可参考:OpenGL环境配置教程:VS2012 + GLEW + GLFW + GLM。

教程目录(持续更新中):
现代OpenGL教程(一):绘制三角形(ImGui+OpenGL3.3)
现代OpenGL教程(二):矩阵变换(ImGui+OpenGL3.3)
现代OpenGL教程(三):绘制彩色立方体(ImGui+OpenGL3.3)
现代OpenGL教程(四):立方体纹理贴图(ImGui+OpenGL3.3)
现代OpenGL教程(五):obj模型加载(ImGui+OpenGL3.3)
现代OpenGL教程(六):鼠标和键盘(ImGui+OpenGL3.3)
现代OpenGL教程(七):基础光照——颜色(ImGui+OpenGL3.3)
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)
现代OpenGL教程(九):基础光照——材质(ImGui+OpenGL3.3)

本节教程在上一节(现代OpenGL教程(七):基础光照——颜色(imgui+OpenGL3.3))的基础上,实现了一个简单的Phong光照模型。本例完整代码下载地址:https://github.com/maijiaquan/blog-code/tree/master/opengl-phong

运行效果:
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)_第1张图片


Phong光照模型

Phong光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。下面这张图展示了这些光照分量看起来的样子:

现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)_第2张图片
  • 环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
  • 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
  • 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

第一步:环境光照

在上一节(现代OpenGL教程(七):基础光照——颜色(imgui+OpenGL3.3))中,我们模拟了光源照射物体然后反射出颜色的效果。本节的环境光照的实现非常简单,用光的颜色乘以一个很小的常量环境因子,再乘以物体的颜色,然后将最终结果作为片段的颜色:

void main()
{
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;

    vec3 result = ambient * objectColor;
    FragColor = vec4(result, 1.0);
}

运行效果:

现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)_第3张图片

第二步:漫反射光照

在现实生活中,如果我们在黑暗中用光照射不会发光的物体,则有如下现象:当入射光垂直于物体表面,即直射物体时,我们会觉得亮度很高。入射光越倾斜,亮度越低。

为了方便理解,请看下图:
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)_第4张图片

入射光在 L 1 L_1 L1的位置照射物体,光线方向跟物体表面法线 n n n的夹角为 θ \theta θ θ \theta θ越大,即入射光越倾斜,则亮度应该越低。现将入射光移动到 L 2 L_2 L2,此时 θ = 0 \theta=0 θ=0,即入射光垂直于物体表面,亮度会达到最大值。

这就是漫反射光照要模拟的效果:让亮度随着夹角 θ \theta θ的减小而增大,当 θ = 0 \theta=0 θ=0时,亮度达到最大值。

在OpenGL中实现漫反射光照,实质上是通过漫反射分量来改变物体片段的颜色。

计算漫反射分量所需的参数:

  • norm:物体当前片段的法向量
  • lightDir:光线方向向量

1.计算光线的方向向量
求光线的方向向量lightDir,其实就是求光源位置lightPos和片段位置FragPos的向量差。此外,为了简化计算,还要将其标准化为单位向量:

vec3 lightDir = normalize(lightPos - FragPos);

2.计算漫反射分量
物体当前片段的法向量 n n n和光线的方向向量lightDir进行点乘运算dot(),得到的结果,再乘以光的颜色,得到漫反射分量。该运算符合之前的描述:两个向量之间的夹角 θ \theta θ 越大,漫反射分量就会越小。此外,如果两个向量之间的角度大于90度,点乘的结果就会变成负数,这样会导致漫反射分量变为负数。为此,我们使用max()函数返回两个参数之间较大的参数,从而保证漫反射分量不会变成负数:

float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

3.颜色输出
最后,我们得到了环境光分量和漫反射分量,把它们相加,结果乘以物体的颜色,来获得片段最后的输出颜色:

vec3 result = (ambient + diffuse) * objectColor;
FragColor = vec4(result, 1.0);

目前我们已经有了环境光照和漫反射光照的效果,运行效果如下:
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)_第5张图片


第三步:镜面光照

除了依赖光线方向向量和物体法向量,镜面光照还依赖于观察方向,例如玩家是从什么方向看着这个片段的。日常生活中,当视线方向在反射向量附近,我们会看到一个高光。为了模拟这一现象,需要计算反射向量和视线方向的角度差,如果夹角越小,那么镜面光的影响就会越大,因此可以跟漫反射一样用点乘来实现该效果。镜面光照示意图:

现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)_第6张图片

计算镜面光分量所需的参数:

  • norm:物体当前片段的法向量
  • lightDir:光线方向向量
  • viewDir:视线方向向量
  • reflectDir:反射向量
  • specularStrength:镜面强度,用于控制镜面光强度
  • shininess:反光度,一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小。

1.计算视线方向向量
通过计算观察者位置viewPos和物体当前片段位置FragPos的向量差,求得视线方向向量viewDir
片段着色器中添加一个uniform作为viewPos:

uniform vec3 viewPos;
...
vec3 viewDir = normalize(viewPos - FragPos);

OpenGL中把观察者坐标传进去:

setVec3(cubeProgramID, "viewPos", position.x, position.y, position.z);

2.计算反射向量
注意要对lightDir向量进行取反,因为reflect()要求第一个向量是从光源指向片段位置的向量:

vec3 reflectDir = reflect(-lightDir, norm);

3.计算镜面分量
先计算视线方向与反射方向的点乘(并确保它不是负值),然后取它的反光度shininess次幂。一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小。

float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);
vec3 specular = specularStrength * spec * lightColor;  

4.颜色输出
将镜面分量加到环境光分量和漫反射分量里,结果再乘以物体的颜色:

vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);

运行效果:
1.不同镜面光强度specularStrength、不同反光度shininess下的光照效果:
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)_第7张图片

2.可见镜面高光根据相机位置而变化:
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)_第8张图片


本例完整代码下载地址:https://github.com/maijiaquan/blog-code/tree/master/opengl-phong

参考资料:基础光照 - LearnOpenGL CN

你可能感兴趣的:(计算机图形学)