《OGL dev》Etay Meiri Tutorial 25 - SkyBox 笔记

天空盒(SkyBox)的思路是创建一个立方体或球体,将摄像机放置在它的中心,它跟随摄像机移动。

天空盒的纹理由6张图片组成,是一个立方体的6个面,相邻的面之间的画面是连续的,称为cubemap。如下图所示:

cubemap为3d纹理,texture target为GL_TEXTURE_CUBE_MAP。

cubemap需要调用6次glTexImage2D才能设置数据源,其中glTexImage2D的第一个参数接收一个枚举对应cubemap立方体的一个面,对应关系如下:

GL_TEXTURE_CUBE_MAP_POSITIVE_X  ->  right
GL_TEXTURE_CUBE_MAP_NEGATIVE_X  ->  left
GL_TEXTURE_CUBE_MAP_POSITIVE_Y  ->  top
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y  ->  bottom
GL_TEXTURE_CUBE_MAP_POSITIVE_Z  ->  front
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z  ->  back
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);

其中GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T、GL_TEXTURE_WRAP_R分别对应纹理坐标的第一个维度、第二个维度、第三个维度的坐标。

无论模型是立方体还是球体,天空盒的纹理都是cubemap。cubemap的采样方式与2D纹理不同,采样的结果为从原点出发的射线与立方体表面的交点的texel。因此,如果局部空间(local space)中原点在模型中心的话,局部空间的坐标就是纹理坐标。

与2D纹理不同,cubemap对应的sampler uniform variable类型为samplerCube。采样函数都使用texture()。(2D纹理还可以使用texture2D())

因为天空盒需要绘制的是内表面,而不是像通常那样绘制外表面,所以需要设置为剔除正面:glCullFace(GL_FRONT);

因为天空盒必须渲染在其他mesh的后方,所以需要进行以下操作:

  1. 最后渲染天空盒,这样depth buffer中已经保存了其他mesh的深度值。
  2. 在vertex shader,将经历WVP变换的天空盒的顶点的z值替换为w值,这样透视除法之后z值为1,确保在深度测试中总是失败。
  3. depth buffer的默认值为1,因为要将z值为1的天空盒顶点写入depth bufer,必须将深度测试的设定从默认的“小于”改为“小于等于”:glDepthFunc(GL_LEQUAL);

OpenGL渲染时经常需要切换渲染状态,因此OpenGL提供了Get*函数,可以获取当前的渲染状态,用于在切换渲染状态之后还原。依据渲染状态的类型,Get*函数分为glGetIntegerv()、glGetBooleanv()、glGetInteger64v()、glGetFloatv()和glGetDoublev()。