天空盒

​ 天空盒子是一种让场景看上去更广阔无垠的一种视觉技术,用无缝对接的封闭纹理将摄像机的视口360度无死角的包裹起来。视角中除了真实模型的其他空余部分被封闭纹理所完全填充充当背景。

​ 天空盒子的一种实现方法是渲染一个巨大的正六面体封闭盒子纹理,并将相机置于中心,当摄像机移动的时候封闭纹理也跟着移动,所以看上去永远走不到场景中的视平线边缘。天空盒子技术除了用上面的立方体实现,还可以用球面来实现。

​ 这种纹理叫做立方体贴图(Cubemap)。为了从立方体贴图中采样,要采用3d纹理坐标而不是我们之前用的2d纹理坐标。纹理采样器将3d纹理坐标看做一个向量,找出该文素位于立方体的哪一个面上并从那个面上取出需要的文素。

创建纹理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static GLuint
InitTextures(void)
{
GLenum imgFormat;
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, texture);

GLint imgWidth, imgHeight;
GLubyte* image = NULL;

image = LoadRGBImage(TexFiles[0], &imgWidth, &imgHeight, &imgFormat);

glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0,
GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0,
GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0,
GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0,
GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0,
GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0,
GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
free(image);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

return texture;
}

​ 开始先创建一个纹理对象来加载cubemap纹理,这个对象绑定到了一个特殊的GL_TEXTURE_CUBE_MAP目标对象上。设置cubemap的六个面枚举:

​ GL_TEXTURE_CUBE_MAP_NEGATIVE_X
​ GL_TEXTURE_CUBE_MAP_POSITIVE_X
​ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
​ GL_TEXTURE_CUBE_MAP_POSITIVE_Y
​ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
​ GL_TEXTURE_CUBE_MAP_POSITIVE_Z

​ 通过glTexImage2D()函数将资源数据传给OpenGL。cubemap加载解析结束后,设置过滤。(这里简化只加载了一张图片)

着色器文件

顶点着色器

1.顶点着色器之后,光栅器将获得gl_Position向量,并进行透视分割以完成投影变换(将各分量除以W分量)。将Z分量设置成W分量的值可以保证透视分割后位置向量最终的Z分量值为1.0。Z分量为1意味着永远处于Z轴最远处,在深度测试中相对于其他物体模型天空盒子将永远处于劣势,因此天空盒子就总是作为其他物体的背景了,而其他物体会一直渲染在背景前面。

2.使用天空盒子自身坐标系中顶点的原始坐标来作为3D纹理坐标。因为对cubemap纹理采样时是从中心发射一个向量到立方体盒子或者球面上的,因此盒子表面上点的坐标恰好就是纹理坐标。

1
2
3
v_Position=vertexPosition_modelspace;
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
gl_Position=gl_Position.xyww;

片段着色器

使用’samplerCube’而不是’sampler2D’以获取cubemap的纹理。

1
2
uniform samplerCube myTextureSampler;
gl_FragColor=textureCube(myTextureSampler, v_Position);

应用纹理

1.第一个要改变的是表面剔除模式。通常,会剔除掉背向相机看不到的三角形图元,而对于天空盒子来说,相机是置于盒子内部的,所以想看到盒子的内部而不是外部。用相反的OpenGL剔除模式,就要告诉OpenGL剔除去正面的三角形。

1
2
glEnable(GL_CULL_FACE);//开启表面剔除(默认背面剔除)
glCullFace(GL_FRONT);//剔除正面实现

2.第二个要改变的是深度测试函数模式。默认是告诉OpenGL,输入的片元如果比存储的片元Z值小就认为赢得深度测试而被渲染,但是对于天空盒子,Z值总是最远的边界,如果深度测试函数模式设置为‘小于’,天空盒子会被裁剪掉,为了让盒子成为场景的一部分要将深度测试函数模式改为‘小于等于’。

1
glDepthFunc(GL_LEQUAL);

3.计算WVP矩阵。对于天空盒子来说,世界坐标系的中心位于相机处,从而保证相机始终在天空盒子中心。

1
2
glm::mat4 V = glm::mat4(glm::mat3(getViewMatrix()));//天空盒v矩阵
glm::mat4 V1 = getViewMatrix();//其他物体v矩阵

cubemap的纹理贴图绑定到纹理单元0号上,设置为GL_TEXTURE_CUBE_MAP

1
2
3
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, Texture);
glUniform1i(TextureID, 0);

最后还原原本的剔除模式和深度测试函数还原

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码

请我喝杯咖啡吧~

支付宝
微信