光照之材質_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);
}