天空盒子(skyBox)
SkyBox 制作3D游戏的一个经典技术应用。
原理
SkyBox 是基于正方体模型的渲染技术。将6个方向拍下来的天空图片分别贴在正方体的对应面上,从内部观察正方体就可以得到一个密闭的天空环境
这是我们skyBox贴图。
可以把它分成上下左右前后六张图,然后创建CubeMap。标准化ModelMatrix矩阵处理后的顶点位置,就可以用来在CubeMap上进行采样了。渲染SkyBox时不需要使用光照模型,直接使用采样CubeMap得到的颜色即可。所以需要为天空盒单独写一个Fragment Shader。
天空盒几何体
首先准备基本的Cube几何体,创建SkyBox类,让它继承Cube类,这样就具备了渲染一个Cube的能力。然后对draw
方法进行一些特殊处理。因为需要在内部观察这个Cube,所以需要让这个Cube的正面被裁剪,反面显示出来。 glCullFace(GL_FRONT);
正是用来做这件事情。渲染完后还需要将裁减状态回归到裁减背面。
- (void)draw:(GLContext *)glContext { glCullFace(GL_FRONT); glDepthMask(GL_FALSE); [super draw:glContext]; glDepthMask(GL_TRUE); glCullFace(GL_BACK); }
天空CubeMap
上图中的6张图就是我们用于生成CubeMap的6张图。
- (void)createCubeTexture { NSMutableArray *files = [NSMutableArray new]; for (int i = 0; i < 6; ++i) { NSString *filename = [NSString stringWithFormat:@"cube-%d", i + 1]; NSString *filePath = [[NSBundle mainBundle] pathForResource:filename ofType:@"jpg"]; [files addObject:filePath]; } NSError *error; self.cubeTexture = [GLKTextureLoader cubeMapWithContentsOfFiles:files options:nil error:&error]; }
Fragment Shader
生成采样向量,返回采样结果。
precision highp float; varying vec2 fragUV; varying vec3 fragPosition; uniform samplerCube envMap; uniform mat4 modelMatrix; void main(void) { vec3 sampleVector = normalize(modelMatrix * vec4(fragPosition, 1.0)).xyz; gl_FragColor = textureCube(envMap, sampleVector); }
效果图: