1. OpenGL ES
1.1 透视投影
GLfloat vertexData[] = {0.5, -0.5, 0.0f, 1.0f, 0.0f, //右下0.5, 0.5, 0.0f, 1.0f, 1.0f, //右上-0.5, 0.5, 0.0f, 0.0f, 1.0f, //左上0.5, -0.5, 0.0f, 1.0f, 0.0f, //右下-0.5, 0.5, 0.0f, 0.0f, 1.0f, //左上-0.5, -0.5, 0.0f, 0.0f, 0.0f, //左下};
虽然我们的代码看起来是绘制了一个正方形,但由于视口(视图)宽高比的问题产生了拉伸问题。如下图所示:
为了解决这个问题,我们可以设置透视投影矩阵。又由于平截头体可视范围的问题,我们需要将顶点向后移4.0单位。
CGFloat aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height);GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), aspect, 0.1, 100.0);_baseEffect.transform.projectionMatrix = projectionMatrix;GLKMatrix4 modelviewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -4.0);_baseEffect.transform.modelviewMatrix = modelviewMatrix;

1.2 立方体贴图+旋转
解决了上面的问题,完成立方体贴图+旋转就很简单了。
- 写好立方体的顶点位置。
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect会不断回调,在里面更新模型视图变换矩阵就可以了。

@interface DemoViewController (){EAGLContext *_context;GLKBaseEffect *_baseEffect;int _angle;}@end@implementation DemoViewController- (void)viewDidLoad {[super viewDidLoad];//1.OpenGL ES 相关初始化[self setUpConfig];//2.加载顶点/纹理坐标数据[self setUpVertexData];//3.加载纹理数据(使用GLBaseEffect)[self setUpTexture];}-(void)setUpTexture{NSString *filePath = [[NSBundle mainBundle]pathForResource:@"qiyu" ofType:@"jpg"];NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft : @(YES)};GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];// 纹理设置_baseEffect = [[GLKBaseEffect alloc] init];_baseEffect.texture2d0.enabled = GL_TRUE;_baseEffect.texture2d0.name = textureInfo.name;// 透视投影矩阵CGFloat aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height);GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), aspect, 0.1, 100.0);_baseEffect.transform.projectionMatrix = projectionMatrix;}-(void)setUpVertexData{GLfloat vertices[] = {-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.0f, 1.0f,-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, 0.5f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f};// 顶点缓存GLuint bufferID;glGenBuffers(1, &bufferID);glBindBuffer(GL_ARRAY_BUFFER, bufferID);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//顶点坐标数据glEnableVertexAttribArray(GLKVertexAttribPosition);glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);//纹理坐标数据glEnableVertexAttribArray(GLKVertexAttribTexCoord0);glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);}- (void)setUpConfig{_context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];if (!_context) {NSLog(@"Create ES context Failed");}//设置当前上下文[EAGLContext setCurrentContext:_context];GLKView *view =(GLKView *) self.view;view.context = _context;view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;view.drawableDepthFormat = GLKViewDrawableDepthFormat16;glClearColor(0.8, 0.8, 0.8, 1.0);}#pragma mark -- GLKViewDelegate- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{glEnable(GL_DEPTH_TEST);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 更新旋转[self update];[_baseEffect prepareToDraw];glDrawArrays(GL_TRIANGLES, 0, 48);}- (void)update {_angle = (_angle + 2) % 360;GLKMatrix4 modelviewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -4.0);modelviewMatrix = GLKMatrix4Rotate(modelviewMatrix, GLKMathDegreesToRadians(_angle), 0.3, 0.5, 0.7);_baseEffect.transform.modelviewMatrix = modelviewMatrix;}@end
2. Core Animation
使用 Core Animation 完成上面的功能也非常简单,利用 layer.transform 的数据结构CATransform3D即可完成3D变换。
值得一提的是,为了实现立方体整体的旋转动画,我们不需要为为一个面都专门做变换。我们只需要将它们放在同一个父视图中,利用父视图的 layer.sublayerTransform 即可对所有子视图进行统一变换。

@interface ViewController ()@property (nonatomic, strong) UIView *containerView;@end@implementation ViewController- (void)addFace:(int)index withTransform:(CATransform3D)transform{NSString *filePath = [[NSBundle mainBundle]pathForResource:@"qiyu" ofType:@"jpg"];UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];imageView.image = [UIImage imageWithContentsOfFile:filePath];[self.containerView addSubview:imageView];CGSize containerSize = self.containerView.bounds.size;imageView.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);// 变换imageView.layer.transform = transform;}- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = UIColor.lightGrayColor;self.containerView = [[UIView alloc] initWithFrame:self.view.bounds];[self.view addSubview:self.containerView];//add cube face 1CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);[self addFace:0 withTransform:transform];//add cube face 2transform = CATransform3DMakeTranslation(100, 0, 0);transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);[self addFace:1 withTransform:transform];//add cube face 3transform = CATransform3DMakeTranslation(0, -100, 0);transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);[self addFace:2 withTransform:transform];//add cube face 4transform = CATransform3DMakeTranslation(0, 100, 0);transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);[self addFace:3 withTransform:transform];//add cube face 5transform = CATransform3DMakeTranslation(-100, 0, 0);transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);[self addFace:4 withTransform:transform];//add cube face 6transform = CATransform3DMakeTranslation(0, 0, -100);transform = CATransform3DRotate(transform, M_PI, 0, 1, 0);[self addFace:5 withTransform:transform];__block int step = 0;NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 repeats:YES block:^(NSTimer * _Nonnull timer) {step = (step + 2) % 360;self.containerView.layer.sublayerTransform = CATransform3DMakeRotation(M_PI / 180.0 * step, -0.3, 0.5, -0.7);}];[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];}@end
