Vulkan_Shader—環境映射天空盒

環境映射

我們現在將整個環境映射到了一個紋理對象上了,能利用這個信息的不僅僅只有天空盒。通過使用環境的立方體貼圖,我們可以給物體反射和折射的屬性。這樣使用環境立方體貼圖的技術叫做環境映射(Environment Mapping),其中最流行的兩個是反射(Reflection)和折射(Refraction)。
在介紹這兩種之前,我們先把,天空盒的頂點和片元着色器貼出來,很常規:
頂點着色器:

#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout (location = 0) in vec3 inPos;

layout (binding = 0) uniform UBO 
{
	mat4 projection;
	mat4 model;
} ubo;

layout (location = 0) out vec3 outUVW;

out gl_PerVertex 
{
	vec4 gl_Position;
};

void main() 
{
	outUVW = inPos;
	outUVW.x *= -1.0;
	gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
}

片元着色器:

#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout (binding = 1) uniform samplerCube samplerCubeMap;

layout (location = 0) in vec3 inUVW;

layout (location = 0) out vec4 outFragColor;

void main() 
{
	outFragColor = texture(samplerCubeMap, inUVW);
}

一、反射

反射這個屬性表現爲物體(或物體的一部分)反射它周圍環境,即根據觀察者的視角,物體的顏色或多或少等於它的環境。鏡子就是一個反射性物體:它會根據觀察者的視角反射它周圍的環境。

反射的原理並不難。下面這張圖展示了我們如何計算反射向量,並如何使用這個向量來從立方體貼圖中採樣:
在這裏插入圖片描述
我們根據觀察方向向量I¯和物體的法向量N¯,來計算反射向量R¯。我們可以使用GLSL內建的reflect函數來計算這個反射向量。最終的R¯向量將會作爲索引/採樣立方體貼圖的方向向量,返回環境的顏色值。最終的結果是物體看起來反射了天空盒。

因爲我們已經在場景中配置好天空盒了,創建反射效果並不會很難。我們將會改變箱子的片段着色器,讓箱子有反射性。

#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout (binding = 1) uniform samplerCube samplerColor;

layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in float inLodBias;
layout (location = 3) in vec3 inViewVec;
layout (location = 4) in vec3 inLightVec;
layout (location = 5) in mat4 inInvModelView;

layout (location = 0) out vec4 outFragColor;

void main() 
{
	vec3 cI = normalize (inPos);
	vec3 cR = reflect (cI, normalize(inNormal));

	cR = vec3(inInvModelView * vec4(cR, 0.0));
	cR.x *= -1.0;

	vec4 color = texture(samplerColor, cR, inLodBias);

	vec3 N = normalize(inNormal);
	vec3 L = normalize(inLightVec);
	vec3 V = normalize(inViewVec);
	vec3 R = reflect(-L, N);
	vec3 ambient = vec3(0.5) * color.rgb;
	vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);
	vec3 specular = pow(max(dot(R, V), 0.0), 16.0) * vec3(0.5);
	outFragColor = vec4(ambient + diffuse * color.rgb + specular, 1.0);		
}

首先,我們先計算了觀察/攝像機方向向量I,並使用它來計算反射向量R,之後我們將使用R來從天空盒立方體貼圖中採樣。注意,我們現在又有了片段的插值Normal和Position變量,所以我們需要更新一下頂點着色器。

#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inNormal;

layout (binding = 0) uniform UBO 
{
	mat4 projection;
	mat4 model;
	float lodBias;
} ubo;

layout (location = 0) out vec3 outPos;
layout (location = 1) out vec3 outNormal;
layout (location = 2) out float outLodBias;
layout (location = 3) out vec3 outViewVec;
layout (location = 4) out vec3 outLightVec;
layout (location = 5) out mat4 outInvModelView;

out gl_PerVertex 
{
	vec4 gl_Position;
};

void main() 
{
	gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
	
	outPos = vec3(ubo.model * vec4(inPos, 1.0));
	outNormal = mat3(ubo.model) * inNormal;	
	outLodBias = ubo.lodBias;
	
	outInvModelView = inverse(ubo.model);

	vec3 lightPos = vec3(0.0f, -5.0f, 5.0f);
	outLightVec = lightPos.xyz - outPos.xyz;
	outViewVec = -outPos.xyz;		
}

我們現在使用了一個法向量,所以我們將再次使用法線矩陣(Normal Matrix)來變換它們。outPos 輸出向量是一個世界空間的位置向量。頂點着色器的這個outPos 輸出將用來在片段着色器內計算觀察方向向量。

編譯,運行,調整相應視角,你可以看到:
在這裏插入圖片描述

折射

環境映射的另一種形式是折射,它和反射很相似。折射是光線由於傳播介質的改變而產生的方向變化。在常見的類水錶面上所產生的現象就是折射,光線不是直直地傳播,而是彎曲了一點。將你的半隻胳膊伸進水裏,觀察出來的就是這種效果。

折射是通過斯涅爾定律(Snell’s Law)來描述的,使用環境貼圖的話看起來像是這樣:
在這裏插入圖片描述
同樣,我們有一個觀察向量I¯,一個法向量N¯,而這次是折射向量R¯。可以看到,觀察向量的方向輕微彎曲了。彎折後的向量R¯將會用來從立方體貼圖中採樣。

折射可以使用GLSL的內建refract函數來輕鬆實現,它需要一個法向量、一個觀察方向和兩個材質之間的折射率(Refractive Index)。

折射率決定了材質中光線彎曲的程度,每個材質都有自己的折射率。一些最常見的折射率可以在下表中找到:
在這裏插入圖片描述
我們使用這些折射率來計算光傳播的兩種材質間的比值。在我們的例子中,光線/視線從空氣進入玻璃(如果我們假設箱子是玻璃制的),所以比值爲1.00/1.52=0.658.
我們已經綁定了立方體貼圖,提供了頂點數據和法線,並設置了攝像機位置的uniformBuffer。唯一要修改的就是片段着色器:

vec3 cR = reflect(cI, normalize(inNormal), 1.00 / 1.52);

通過改變折射率,你可以創建完全不同的視覺效果。編譯程序並運行,但結果並不是很有趣,因爲我們只使用了一個簡單的圓球,它不太能顯示折射的效果,現在看起來只是有點像一個放大鏡,甚至於上面的反射差別不太大,但它的確是一個類玻璃的物體(從其圓邊出可以看出與上圖反射更像鏡子折射效果)。
在這裏插入圖片描述
你可以想象出有了光照、反射、折射和頂點移動的正確組合,你可以創建出非常漂亮的水。注意,如果要想獲得物理上精確的結果,我們還需要在光線離開物體的時候再次折射,現在我們使用的只是單面折射(Single-side Refraction),但它對大部分場合都是沒問題的。

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