環境映射
我們現在將整個環境映射到了一個紋理對象上了,能利用這個信息的不僅僅只有天空盒。通過使用環境的立方體貼圖,我們可以給物體反射和折射的屬性。這樣使用環境立方體貼圖的技術叫做環境映射(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),但它對大部分場合都是沒問題的。