learnOpenGL 4.6 立方纹理
立方纹理是一种特殊纹理,它利用从立方体中心出发的方向向量进行纹理的采样,如下图:
方向向量的大小并不重要,只要提供了方向,OpenGL就会获取方向向量(最终)所击中的纹素,并返回对应的采样纹理值。
这样子,只要立方体的中心位于原点,我们就能使用立方体的实际位置向量来对立方体贴图进行采样了。
于是乎,我们要做的就是要使面与相应的贴图对应。
因为立方体贴图包含有6个纹理,每个面一个,我们需要调用glTexImage2D函数6次,参数和之前教程中很类似。但这一次我们将纹理目标(target)参数设置为立方体贴图的一个特定的面,告诉OpenGL我们在对立方体贴图的哪一个面创建纹理。
和OpenGL的很多枚举(Enum)一样,它们背后的int值是线性递增的,所以如果我们有一个纹理位置的数组或者vector,我们就可以从GL_TEXTURE_CUBE_MAP_POSITIVE_X开始遍历它们,在每个迭代中对枚举值加1,遍历了整个纹理目标:
int width, height, nrChannels;
unsigned char *data;
for(unsigned int i = 0; i < textures_faces.size(); i++)
{
data = stbi_load(textures_faces[i].c_str(), &width, &height, &nrChannels, 0);
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data
);
}
天空盒
立方纹理的一个重要应用就是天空盒,因为天空盒的中心始终在原点不移动,而且可以方便做反射折射等操作。
天空盒本质上是一个单位立方纹理,不过有几点特殊:
- 它的深度值要设为1.0(最大值),因为作为背景不应该挡住任何物体。
为了方便,我们直接将z值设为和W值相同。
gl_Position = pos.xyww;
我们还要改变一下深度函数,将它从默认的GL_LESS改为GL_LEQUAL(小于等于)。因为1.0已经是默认的最大值,只能等于不可能小于。
glDepthFunc(GL_LEQUAL);
- 它不需要model矩阵,因为在原点。
- 它的观察矩阵应该不移动,只能旋转。
方便起见,我们可以通过取4x4矩阵左上角的3x3矩阵来移除变换矩阵的位移部分。我们可以将观察矩阵转换为3x3矩阵(移除位移),再将其转换回4x4矩阵,来达到类似的效果。
glm::mat4 view = glm::mat4(glm::mat3(camera.GetViewMatrix()));
反射折射
因为立方纹理只需要方向向量就可以进行纹理采样,我们只要计算反射和折射的向量,就可做出环境的反射和折射效果了。
顶点着色器:
...
void main()
{
Normal = mat3(transpose(inverse(model))) * aNormal;
Position = vec3(model * vec4(aPos, 1.0));
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
反射的片元着色器:
...
void main()
{
vec3 I = normalize(Position - cameraPos);
vec3 R = reflect(I, normalize(Normal));
FragColor = vec4(texture(skybox, R).rgb, 1.0);
}
折射的片元着色器:
1.52为玻璃的折射率。
...
void main()
{
float ratio = 1.00 / 1.52;
vec3 I = normalize(Position - cameraPos);
vec3 R = refract(I, normalize(Normal), ratio);
FragColor = vec4(texture(skybox, R).rgb, 1.0);
}
上一篇: 看我如何模拟Cobalt Strike上线欺骗入侵者
下一篇: 4.6.springdata