LearnOpenGL(八)之光照

一、冯氏光照模型

冯氏光照模型(Phong Lighting Model)主要由环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照这三部分组成,各效果如下:

1、环境光照

即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。我们可以使用一个环境光照(Ambient Lighting)常量来模拟这个。

如何把环境光照添加到场景呢?我们可以用光的颜色乘以一个很小的常量环境因子,再乘以物体的颜色,然后将最终结果作为片段的颜色:

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

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

2、漫反射光照

模拟光源对物体的方向性影响(Directional Impact)。物体的某一部分越是正对着光源,它就会越亮。

从上图我们可以看出,光线越垂直于物体表面,这束光对物体的影响会越大,即物体越亮。为了测量光线和片段的角度,我们可以使用一个叫做法向量(Normal Vector)的东西,它是垂直于片段表面的一个向量(图中以黄色箭头表示)。

计算漫反射光照需要法向量定向的光线!!

2.1 法向量

法向量是一个垂直于顶点表面的(单位)向量。我们可以使用叉乘对立方体所有的顶点计算法向量,但是由于3D立方体不是一个复杂的形状,所以我们可以简单地把法线数据手工添加到顶点数据中,然后更新顶点着色器:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 Normal;
...

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    Normal = aNormal;
}

在片段着色器添加:

in vec3 Normal;

2.2 定向的光线

定向的光线,作为光源的位置与片段的位置之间向量差的方向向量。我们可以通过光的位置向量片段的位置向量计算该光线。

由于光源的位置是一个静态变量,我们在片段着色器中把它声明为uniform:

uniform vec3 lightPos;

然后在渲染循环中将lightPos向量传进去:

lightingShader.setVec3("lightPos", lightPos);

对于片段的位置,我们可以通过把顶点位置属性乘以模型矩阵(不是观察和投影矩阵)来获取。这个在顶点着色器中很容易完成:

out vec3 FragPos;  
out vec3 Normal;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = aNormal;
}

然后,在片段着色器中添加相应的输入变量:

in vec3 FragPos;

至此,我们便可以计算定向的光线了——光源位置向量与片段位置向量之间的向量差。

vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);

接着,可以计算漫反射分量了。对norm和lightDir向量进行点乘,计算光源对当前片段实际的漫反射影响。结果值再乘以光的颜色,得到漫反射分量。两个向量之间的角度越大,漫反射分量就会越小:

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

将环境光分量和漫反射分量相加,然后把结果乘以物体的颜色,可以获得片段最后的输出颜色:

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

3、镜面光照

模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

和漫反射光照一样,镜面光照也决定于光的方向向量物体的法向量,但是它也决定于人(摄像机)的观察方向。可以把物体表面设想为一面镜子,那么镜面光照最强的地方就是我们看到表面上反射光的地方,如下图:

我们通过根据法向量翻折入射光的方向来计算反射向量。然后我们计算反射向量与观察方向的角度差,它们之间夹角越小,镜面光的作用就越大,我们看到的效果就越亮。

观察向量是我们计算镜面光照时需要的一个额外变量,我们可以使用观察者(摄像机)的世界空间位置和片段的位置来计算它。之后我们计算出镜面光照强度,用它乘以光源的颜色,并将它与环境光照和漫反射光照部分加和。

观察者(摄像机)的世界空间位置:

lightingShader.setVec3("viewPos", camera.Position);

高光强度:

float specularStrength = 0.5;//镜面强度

视线方向向量,以及对应的沿着法线轴的反射向量:

vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);

镜面分量:

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

先计算视线方向与反射方向的点乘(并确保它不是负值),然后取它的32次幂。这个32是高光的反光度(Shininess)。一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小。不同反光度的视觉效果影响如下:

最后,把它加到环境光分量和漫反射分量里,再用结果乘以物体的颜色:

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

demo下载:点击跳转

效果如下:

觉得有帮助的话,打赏一下呗。。

           

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值