Vulkan_Shader_Day03—光照(材质_Materials)

光照之材质_Materials

一、定义材质数据

在现实世界里,每个物体会对光产生不同的反应。比如说,钢看起来通常会比陶瓷花瓶更闪闪发光,木头箱子也不会像钢制箱子那样对光产生很强的反射。每个物体对镜面高光也有不同的反应。有些物体反射光的时候不会有太多的散射(Scatter),因而产生一个较小的高光点,而有些物体则会散射很多,产生一个有着更大半径的高光点。如果我们想要在Vulkan中模拟多种类型的物体,我们必须为每个物体分别定义一个材质(Material)属性。

在上一节中,我们指定了一个物体和光的颜色,以及结合环境光和镜面强度分量,来定义物体的视觉输出。当描述一个物体的时候,我们可以用这三个分量来定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。通过为每个分量指定一个颜色,我们就能够对物体的颜色输出有着精细的控制了。现在,我们再添加反光度(Shininess)这个分量到上述的三个颜色中,这就有我们需要的所有材质属性了(本次教程我将这些材质信息加载在Vertex中,注意实际项目应另建一个UniformBuffer以避免顶点数据过于庞大,类似MVP矩阵,后续有时间再优化)。:

struct Vertex {
	...
	//Material 
	glm::vec3 m_ambient;
	glm::vec3 m_diffuse;
	glm::vec3 m_specular;
	float m_shininess;
	...
	//属性描述
	static std::array<VkVertexInputAttributeDescription, 8> getAttributeDescriptions() {
		std::array<VkVertexInputAttributeDescription, 8> attributeDescriptions = {};
		...
        attributeDescriptions[4].location = 4;
		attributeDescriptions[4].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[4].offset = offsetof(Vertex, m_ambient);

		attributeDescriptions[5].binding = 0;
		attributeDescriptions[5].location = 5;
		attributeDescriptions[5].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[5].offset = offsetof(Vertex, m_diffuse);

		attributeDescriptions[6].binding = 0;
		attributeDescriptions[6].location = 6;
		attributeDescriptions[6].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[6].offset = offsetof(Vertex, m_specular);

		attributeDescriptions[7].binding = 0;
		attributeDescriptions[7].location = 7;
		attributeDescriptions[7].format = VK_FORMAT_R32_SFLOAT;
		attributeDescriptions[7].offset = offsetof(Vertex, m_shininess);

		...
	}
};

在顶点着色器中接受并传出这些数据:

...
layout(location = 4) in vec3 inM_ambient;
layout(location = 5) in vec3 inM_diffuse;
layout(location = 6) in vec3 inM_specular;
layout(location = 7) in float inM_shininess;

...
//Material
layout(location = 9) out vec3 m_ambient;
layout(location = 10) out vec3 m_diffuse;
layout(location = 11) out vec3 m_specular;
layout(location = 12) out float m_shininess;

void main() {
    ...
	m_ambient = inM_ambient;
	m_diffuse = inM_diffuse;
	m_specular = inM_specular;
	m_shininess = inM_shininess;
}

在片段着色器中,我们再接受这些数据:

...
//Material
layout(location = 9) in vec3 m_ambient;
layout(location = 10) in vec3 m_diffuse;
layout(location = 11) in vec3 m_specular;
layout(location = 12) in float m_shininess;
...

你可以看到,我们为每个冯氏光照模型的分量都定义一个颜色向量。ambient材质向量定义了在环境光照下这个物体反射得是什么颜色,通常这是和物体颜色相同的颜色。diffuse材质向量定义了在漫反射光照下物体的颜色。(和环境光照一样)漫反射颜色也要设置为我们需要的物体颜色。specular材质向量设置的是镜面光照对物体的颜色影响(或者甚至可能反射一个物体特定的镜面高光颜色)。最后,shininess影响镜面高光的散射/半径。

这四个元素定义了一个物体的材质,通过它们我们能够模拟很多现实世界中的材质。devernay.free.fr上的一个表格展示了几种材质属性,它们模拟了现实世界中的真实材质。下面的图片展示了几种现实世界的材质对我们的立方体的影响:
在这里插入图片描述
可以看到,通过正确地指定一个物体的材质属性,我们对这个物体的感知也就不同了。效果非常明显,但是要想获得更真实的效果,我们最终需要更加复杂的形状,而不单单是一个立方体。在后面有时间的话,我们会讨论更复杂的形状。
除此之外,别忘记更新vertices数据:

vertices = {
		   //positions             colors             normals             texture coords m.ambient            m.diffuse           m.specular        m.shininess
		  {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f, -1.0f},{0.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f, -1.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f,  0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f, -1.0f},{1.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f,  0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f, -1.0f},{1.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f,  0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f, -1.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f, -1.0f},{0.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
																					   	
		  {{-0.5f, -0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f,  1.0f},{0.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f, -0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f,  1.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f,  1.0f},{1.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f,  1.0f},{1.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f,  1.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f, -0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  0.0f,  1.0f},{0.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
																					  
		  {{-0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{-1.0f, 0.0f,  0.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f,  0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{-1.0f, 0.0f,  0.0f},{1.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{-1.0f, 0.0f,  0.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{-1.0f, 0.0f,  0.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f, -0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{-1.0f, 0.0f,  0.0f},{0.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{-1.0f, 0.0f,  0.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
																					
		  {{ 0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{1.0f,  0.0f,  0.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f,  0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{1.0f,  0.0f,  0.0f},{1.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{1.0f,  0.0f,  0.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{1.0f,  0.0f,  0.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f, -0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{1.0f,  0.0f,  0.0f},{0.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{1.0f,  0.0f,  0.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
																					   	
		  {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f, -1.0f,  0.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f, -1.0f,  0.0f},{1.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f, -0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f, -1.0f,  0.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f, -0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f, -1.0f,  0.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f, -0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f, -1.0f,  0.0f},{0.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f, -1.0f,  0.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
																					
		  {{-0.5f,  0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  1.0f,  0.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f,  0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  1.0f,  0.0f},{1.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  1.0f,  0.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{ 0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  1.0f,  0.0f},{1.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f,  0.5f,  0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  1.0f,  0.0f},{0.0f,  0.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f},
		  {{-0.5f,  0.5f, -0.5f}, {1.0f, 0.0f, 0.0f},{0.0f,  1.0f,  0.0f},{0.0f,  1.0f},{1.0f, 0.5f, 0.31f}, {1.0f, 0.5f, 0.31f},{0.5f, 0.5f, 0.5f},32.0f}
		 
		};

为一个物体赋予一款合适的材质是非常困难的,这需要大量实验和丰富的经验,所以由于不合适的材质而毁了物体的视觉质量是件经常发生的事。

让我们在着色器中实现这样的一个材质系统。

二、设置材质

我们在片段着色器中获取了一个材质信息,所以下面我们希望修改一下光照的计算来顺应新的材质属性。

void main() {
    ...
    //Material Ambient Lighting
    vec3 ambient = m_ambient * fragBaseLight;

	//Material Diffuse Lighting
	vec3 norm = normalize(fragNormal);
    vec3 lightDir = normalize(lightPos - fragPos);
	float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * m_diffuse * fragBaseLight;

	//Material 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 * m_specular * fragBaseLight;

    vec3 result = ambient + diffuse + specular;
    outColor = vec4(result, 1.0);
}

可以看到,我们现在在需要的地方访问了材质结构体中的所有属性,并且这次是根据材质的颜色来计算最终的输出颜色的。物体的每个材质属性都乘上了它们对应的光照分量。

我们将环境光和漫反射分量设置成我们想要让物体所拥有的颜色,而将镜面分量设置为一个中等亮度的颜色,我们不希望镜面分量在这个物体上过于强烈。我们将反光度保持为32。现在我们能够程序中非常容易地修改物体的材质了。

运行程序,调整到适当的角度,你应该会得到下面这样的结果:
在这里插入图片描述
但它看起来很奇怪不是吗?

三、光的属性

这个物体太亮了。物体过亮的原因是环境光、漫反射和镜面光这三个颜色对任何一个光源都会去全力反射。光源对环境光、漫反射和镜面光分量也具有着不同的强度。前面的教程,我们通过使用一个强度值改变环境光和镜面光强度的方式解决了这个问题。我们想做一个类似的系统,但是这次是要为每个光照分量都指定一个强度向量。如果我们假设fragBaseLight是vec3(1.0),代码会看起来像这样:

    vec3 ambient = m_ambient * vec3(1.0);
    vec3 diffuse = diff * m_diffuse * vec3(1.0);
    vec3 specular = spec * m_specular * vec3(1.0);

所以物体的每个材质属性对每一个光照分量都返回了最大的强度。对单个光源来说,这些vec3(1.0)值同样可以分别改变,而这通常就是我们想要的。现在,物体的环境光分量完全地影响了立方体的颜色,可是环境光分量实际上不应该对最终的颜色有这么大的影响,所以我们会将光源的环境光强度设置为一个小一点的值,从而限制环境光颜色:
比如,我们将:

glm::vec3 baseLight = glm::vec3(0.3f, 0.3f, 0.3f);

运行,可以得到:
在这里插入图片描述

我们可以用同样的方式修改光源的漫反射和镜面光强度。这和我们在上一节中所做的极为相似,你可以说我们已经创建了一些光照属性来影响每个单独的光照分量。在这,我就采用一种硬编码形式,将光照的这些参数直接写在片元着色器中:

void main() {
    vec3 lightAmbient  = vec3( 0.2f, 0.2f, 0.2f);
    vec3 lightDiffuse  = vec3( 0.5f, 0.5f, 0.5f);
    vec3 lightSpecular = vec3( 1.0f, 1.0f, 1.0f);
    ...

接下来,我们为每个分量乘上各自的光源系数

void main() {
    ...
    vec3 ambient = m_ambient * fragBaseLight * lightAmbient;
    ...
    vec3 diffuse = diff * m_diffuse * fragBaseLight * lightDiffuse;
	...
    vec3 specular = spec * m_specular * fragBaseLight * lightSpecular;
    ...
}

编译着色器后在此运行,可看到如下材质效果:
在这里插入图片描述

附:着色器源码

//顶点着色器
#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(binding = 0) uniform UniformBufferObject {
    mat4 model;
    mat4 view;
    mat4 proj;
	vec3 baseLight;
	float ambientStrength;
	vec3 lightPos;
	float specularStrength ;
	vec3 viewPos;
} ubo;

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec3 inNormal;
layout(location = 3) in vec2 inTexCoord;
layout(location = 4) in vec3 inM_ambient;
layout(location = 5) in vec3 inM_diffuse;
layout(location = 6) in vec3 inM_specular;
layout(location = 7) in float inM_shininess;

layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec3 fragNormal;
layout(location = 2) out vec2 fragTexCoord;
layout(location = 3) out vec3 fragBaseLight;
layout(location = 4) out float ambientStrength;
layout(location = 5) out vec3 lightPos;
layout(location = 6) out vec3 fragPos;
layout(location = 7) out float specularStrength;
layout(location = 8) out vec3 viewPos;
//Material
layout(location = 9) out vec3 m_ambient;
layout(location = 10) out vec3 m_diffuse;
layout(location = 11) out vec3 m_specular;
layout(location = 12) out float m_shininess;

void main() {
    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
	fragPos =vec3( ubo.model * vec4(inPosition, 1.0));;
    fragColor = inColor;
    fragNormal = mat3(transpose(inverse(ubo.model))) *inNormal;
	fragTexCoord = inTexCoord;
	fragBaseLight = ubo.baseLight;
	ambientStrength= ubo.ambientStrength;
	lightPos=ubo.lightPos;
	specularStrength=ubo.specularStrength;
	viewPos=ubo.viewPos;
	m_ambient = inM_ambient;
	m_diffuse = inM_diffuse;
	m_specular = inM_specular;
	m_shininess = inM_shininess;
}
//片元着色器
#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(binding = 1) uniform sampler2D texSampler;

layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec3 fragNormal;
layout(location = 2) in vec2 fragTexCoord;
layout(location = 3) in vec3 fragBaseLight;
layout(location = 4) in float ambientStrength;
layout(location = 5) in vec3 lightPos;
layout(location = 6) in vec3 fragPos;
layout(location = 7) in float specularStrength;
layout(location = 8) in vec3 viewPos;
//Material
layout(location = 9) in vec3 m_ambient;
layout(location = 10) in vec3 m_diffuse;
layout(location = 11) in vec3 m_specular;
layout(location = 12) in float m_shininess;

layout(location = 0) out vec4 outColor;

void main() {

    vec3 lightAmbient  = vec3( 0.2f, 0.2f, 0.2f);
    vec3 lightDiffuse  = vec3( 0.5f, 0.5f, 0.5f);
    vec3 lightSpecular = vec3( 1.0f, 1.0f, 1.0f);

    //Material Ambient Lighting
    vec3 ambient = m_ambient * fragBaseLight * lightAmbient;

	//Material Diffuse Lighting
	vec3 norm = normalize(fragNormal);
    vec3 lightDir = normalize(lightPos - fragPos);
	float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * m_diffuse * fragBaseLight * lightDiffuse;

	//Material 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 * m_specular * fragBaseLight * lightSpecular;

    vec3 result = ambient + diffuse + specular;
    outColor = vec4(result, 1.0);

    //outColor = vec4(fragTexCoord, 0.0, 1.0);
	//outColor = vec4(fragColor * texture(texSampler, fragTexCoord).rgb, 1.0);
    //outColor = texture(texSampler, fragTexCoord)* vec4(ambient + diffuse + specular, 1.0);
}

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