8.1、天空盒

图8-1显示了一个没完全展开的天空盒,以及一个天空盒内部的场景渲染图。无论视点在天空盒内怎么移动,天空盒的纹理都会产生一个一直延伸到地平线的全景假象。

图 8-1

天空盒只用了12个三角形,即立方体的六个面,每个面需要两个三角形。OpenGLES提供了一个叫做立方体贴图(cubemapping)的专门用以产生类似天空盒效果的纹理贴图模式。立方体的每个面都有一个2D纹理贴图。当渲染时,OpenGL ES会基于一个矢量来计算每个片元的颜色,这个矢量是指从视点到立方体表面上的某个位置之间的3D方向矢量。立方体表面上的这个位置对应于六个面中的一个面的纹理内的S和T纹素坐标。第3章已经讲解了S和T纹素坐标。

图8-2显示的是放置在天空盒内的一个假想摄像头要渲染的体积。就图中显示的摄像头方向来说,一个从镜头的中心延伸到这个渲染体积内的任意一点的矢量会 与三个面中的一面相交,北面、西面,或者底面。矢量与面的交点决定了要取样六个纹理中的哪一个。取样纹理的S和T坐标是由矢量与面的交点位置决定的。例如在图8-2中,指向渲染场景右上角的矢量大约会与北面的中心相交,这会让S和T坐标大约处于北面纹理 的{0.5,0.5} 位置处。与此相反,一个指向场景左下角的矢量会与底面纹理相交。

图 8-2

当假想摄像头改变方向时,来自摄像头的矢量也会与立方体的不同面相交。当摄像头竖直向上时,可能只会从立方体的顶面纹理中取样。当摄像头从图8-2中的方向开始顺时针旋转90度后,可能就要从北面、东面,和底面纹理取样了。

在iOS 5应用中,苹果的GLKit为天空盒的渲染提供了一个专门的类GLKSky-boxEffect。例子OpenGLES_Ch8_1 就是使用GLKSkyboxEffect类产生了图8-1中的屏幕截图。这个例子会让视点在天空盒中绕着帆船模型转动,以此来展示效果。下面摘录自OpenGLES_Ch8_1ViewController 类的代码就是分配、初始化,并配置一个天空盒的步骤。

   // Create and configure skybox
   self.skyboxEffect = [[GLKSkyboxEffect alloc] init];
   self.skyboxEffect.textureCubeMap.name = self.textureInfo.name;
   self.skyboxEffect.textureCubeMap.target = 
      self.textureInfo.target;
   self.skyboxEffect.xSize = 6.0f;
   self.skyboxEffect.ySize = 6.0f;
   self.skyboxEffect.zSize = 6.0f;

天空盒的尺寸是随意的。它需要足够大以容纳要渲染的场景,并且它需要是一个立方体(所有的面尺寸相等),以避免渲染时的纹理扭曲。天空盒的中心要在绘图前设定,并且必须要尽量靠近用于设置视点的眼睛位置。如果眼睛的位置太接近天空盒立方体的边缘,那么由此产生的纹理拉伸会损坏最终的渲染效果。

GLKTextureLoader提供了处理GLKSkyboxEffect所需的立方体纹理贴图的方法。下面的粗体代码使用一个名为skybox0.png的图像作为立方体纹理贴图,并初始化了 self.textureInfo。

   // Load cubeMap texture
   NSString *path = [[NSBundle bundleForClass:[self class]]
      pathForResource:@"skybox0" ofType:@"png"];
   NSAssert(nil != path, @"Path to skybox image not found");   
   NSError *error = nil;
   self.textureInfo = [GLKTextureLoader 
      cubeMapWithContentsOfFile:path 
      options:nil 
      error:&error];

为了绘制天空盒,需要设置GLKSkyboxEffect实例的中心等于眼睛的位置,并设置projectionMatrix和modelViewMatrix,以匹配用于渲染场景其他部分的变换。 调用天空盒的“-prepareToDraw” 方法,然后调用天空盒的“-draw” 方法。例子OpenGLES_Ch8_1ViewController 就是使用下面的代码做到这些的。

  // Draw skybox centered on eye position
   self.skyboxEffect.center = self.eyePosition;
   self.skyboxEffect.transform.projectionMatrix = 
      self.baseEffect.transform.projectionMatrix;
   self.skyboxEffect.transform.modelviewMatrix = 
      self.baseEffect.transform.modelviewMatrix;
   [self.skyboxEffect prepareToDraw];
   glDepthMask(false);
   [self.skyboxEffect draw];
   glBindVertexArrayOES(0);

   // Draw boat model 
   [self.modelManager prepareToDraw];
   [self.baseEffect prepareToDraw];   
   glDepthMask(true);
   [self.boatModel draw];

注意

与天空盒类似, 还有一个效果叫做环境贴图(environment map),又 叫做反射贴图(reflection map),使用它可以很容易地模拟镜面和类似水面的其他反射面。GLKit包含GLKReflectionMapEffect类。你的应用会像GLKBaseEffect实例一样配置关于镜面反射的属性,并且会多设置一个属性一textureCubeMap。 渲染反射与渲染天空盒的操作相同。两种情况下都是使用方向来取样纹理的。对于天空盒,这个方向是从观察者到包裹立方体的面。对于环境贴图,这个方向是指从反射面到包裹立方体的面。


results matching ""

    No results matching ""