Vulkan_Shader_Day04—光照(光照贴图_Lighting maps)

光照贴图

在上一节中,我们讨论了让每个物体都拥有自己独特的材质从而对光照做出不同的反应的方法。这样子能够很容易在一个光照的场景中给每个物体一个独特的外观,但是这仍不能对一个物体的视觉输出提供足够多的灵活性。

在上一节中,我们将整个物体的材质定义为一个整体,但现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成。想想一辆汽车:它的外壳非常有光泽,车窗会部分反射周围的环境,轮胎不会那么有光泽,所以它没有镜面高光,轮毂非常闪亮(如果你洗车了的话)。汽车同样会有漫反射和环境光颜色,它们在整个物体上也不会是一样的,汽车有着许多种不同的环境光/漫反射颜色。总之,这样的物体在不同的部件上都有不同的材质属性。

所以,上一节中的那个材质系统是肯定不够的,它只是一个最简单的模型,所以我们需要拓展之前的系统,引入漫反射和镜面光贴图(Map)。这允许我们对物体的漫反射分量(以及间接地对环境光分量,它们几乎总是一样的)和镜面光分量有着更精确的控制。

我们希望通过某种方式对物体的每个片段单独设置漫反射颜色。有能够让我们根据片段在物体上的位置来获取颜色值得系统吗?

这可能听起来很熟悉,而且事实上这个系统我们已经使用很长时间了。这听起来很像在之前教程中详细讨论过的纹理,而这基本就是这样:一个纹理。我们仅仅是对同样的原理使用了不同的名字:其实都是使用一张覆盖物体的图像,让我们能够逐片段索引其独立的颜色值。在光照场景中,它通常叫做一个漫反射贴图(Diffuse Map)(3D艺术家通常都这么叫它),它是一个表现了物体所有的漫反射颜色的纹理图像。

为了演示漫反射贴图,我们将会使用下面的图片,它是一个有钢边框的木箱:
在这里插入图片描述
在着色器中使用漫反射贴图的方法和纹理教程中是完全一样的。
首先,我们先设置下光源位置及相机视角,方便我们能够更好地看到效果:

glm::vec3 lightPos= glm::vec3(0.0f, 0.0f, 1.0f);
Camera camera(glm::vec3(0.0f, 1.5f, 2.0f), glm::radians(-35.0f), glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f));

在片元着色器中,我们使用使用纹理贴图

void main() {
    outColor = texture(texSampler, fragTexCoord)* vec4(fragBaseLight, 1.0);
}

运行后可以看到:
在这里插入图片描述

一、环境光贴图

我们要移除环境光材质颜色向量,因为环境光颜色在几乎所有情况下都等于漫反射颜色。

    //map Ambient Lighting
    vec3 ambient = ambientStrength * fragBaseLight * lightAmbient;

    vec3 result = ambient;
    outColor = texture(texSampler, fragTexCoord)* vec4(result, 1.0);
}

编译后运行,
在这里插入图片描述

二、漫反射贴图

接下来我们在片元着色器中设置漫反射:

void main() {
    //map Ambient Lighting
    vec3 ambient = ambientStrength * fragBaseLight ;

	//map Diffuse Lighting
	vec3 norm = normalize(fragNormal);
    vec3 lightDir = normalize(lightPos - fragPos);
	float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * fragBaseLight;
    
   vec3 result = ambient + diffuse ;
    outColor = texture(texSampler, fragTexCoord)* vec4(result, 1.0);
}

运行如下图:
在这里插入图片描述

三、高光反射贴图

接下来我们在片元着色器中再次叠加镜面光照:

void main() {
    //map Ambient Lighting
    vec3 ambient = ambientStrength * fragBaseLight ;

	//map Diffuse Lighting
	vec3 norm = normalize(fragNormal);
    vec3 lightDir = normalize(lightPos - fragPos);
	float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * fragBaseLight;
	
	//map Specular Lighting
	vec3 viewDir = normalize(viewPos - fragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
	float spec = pow(max(dot(viewDir, reflectDir), 0.0), m_shininess);
    vec3 specular = spec * fragBaseLight * lightSpecular;

    vec3 result = ambient + diffuse + specular;
    outColor = texture(texSampler, fragTexCoord)* vec4(result, 1.0);
}

细节再一次得到惊人的提升,这次箱子有了光照开始闪闪发光(字面意思也是)了。你的箱子看起来可能像这样:
在这里插入图片描述

当然,您也可以加上环境光参数运行看看效果

四、金属边框镜面光贴图

你可能会注意到,镜面高光看起来有些奇怪,因为我们的物体大部分都是木头,我们知道木头不应该有这么强的镜面高光的。我们可以将物体的镜面光材质设置为vec3(0.0)来解决这个问题,但这也意味着箱子钢制的边框将不再能够显示镜面高光了,我们知道钢铁应该是有一些镜面高光的。所以,我们想要让物体的某些部分以不同的强度显示镜面高光。这个问题看起来和漫反射贴图非常相似。是巧合吗?我想不是。

我们同样可以使用一个专门用于镜面高光的纹理贴图。这也就意味着我们需要生成一个黑白的(如果你想得话也可以是彩色的)纹理,来定义物体每部分的镜面光强度。下面是一个镜面光贴图(Specular Map)的例子:
在这里插入图片描述

镜面高光的强度可以通过图像每个像素的亮度来获取。镜面光贴图上的每个像素都可以由一个颜色向量来表示,比如说黑色代表颜色向量vec3(0.0),灰色代表颜色向量vec3(0.5)。在片段着色器中,我们接下来会取样对应的颜色值并将它乘以光源的镜面强度。一个像素越「白」,乘积就会越大,物体的镜面光分量就会越亮。

由于箱子大部分都由木头所组成,而且木头材质应该没有镜面高光,所以漫反射纹理的整个木头部分全部都转换成了黑色。箱子钢制边框的镜面光强度是有细微变化的,钢铁本身会比较容易受到镜面高光的影响,而裂缝则不会。
使用Photoshop或Gimp之类的工具,将漫反射纹理转换为镜面光纹理还是比较容易的,只需要剪切掉一些部分,将图像转换为黑白的,并增加亮度/对比度就好了。用户有兴趣可以自己尝试新增一个采样用于处理边框尝试。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章