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),但它对大部分场合都是没问题的。

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