1. OpenGL ES

1.1 透视投影

  1. GLfloat vertexData[] = {
  2. 0.5, -0.5, 0.0f, 1.0f, 0.0f, //右下
  3. 0.5, 0.5, 0.0f, 1.0f, 1.0f, //右上
  4. -0.5, 0.5, 0.0f, 0.0f, 1.0f, //左上
  5. 0.5, -0.5, 0.0f, 1.0f, 0.0f, //右下
  6. -0.5, 0.5, 0.0f, 0.0f, 1.0f, //左上
  7. -0.5, -0.5, 0.0f, 0.0f, 0.0f, //左下
  8. };

虽然我们的代码看起来是绘制了一个正方形,但由于视口(视图)宽高比的问题产生了拉伸问题。如下图所示:
image.png

为了解决这个问题,我们可以设置透视投影矩阵。又由于平截头体可视范围的问题,我们需要将顶点向后移4.0单位。

  1. CGFloat aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height);
  2. GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), aspect, 0.1, 100.0);
  3. _baseEffect.transform.projectionMatrix = projectionMatrix;
  4. GLKMatrix4 modelviewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -4.0);
  5. _baseEffect.transform.modelviewMatrix = modelviewMatrix;

image.png

1.2 立方体贴图+旋转

解决了上面的问题,完成立方体贴图+旋转就很简单了。

  • 写好立方体的顶点位置。
  • - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect 会不断回调,在里面更新模型视图变换矩阵就可以了。

image.png

  1. @interface DemoViewController ()
  2. {
  3. EAGLContext *_context;
  4. GLKBaseEffect *_baseEffect;
  5. int _angle;
  6. }
  7. @end
  8. @implementation DemoViewController
  9. - (void)viewDidLoad {
  10. [super viewDidLoad];
  11. //1.OpenGL ES 相关初始化
  12. [self setUpConfig];
  13. //2.加载顶点/纹理坐标数据
  14. [self setUpVertexData];
  15. //3.加载纹理数据(使用GLBaseEffect)
  16. [self setUpTexture];
  17. }
  18. -(void)setUpTexture
  19. {
  20. NSString *filePath = [[NSBundle mainBundle]pathForResource:@"qiyu" ofType:@"jpg"];
  21. NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft : @(YES)};
  22. GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
  23. // 纹理设置
  24. _baseEffect = [[GLKBaseEffect alloc] init];
  25. _baseEffect.texture2d0.enabled = GL_TRUE;
  26. _baseEffect.texture2d0.name = textureInfo.name;
  27. // 透视投影矩阵
  28. CGFloat aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height);
  29. GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), aspect, 0.1, 100.0);
  30. _baseEffect.transform.projectionMatrix = projectionMatrix;
  31. }
  32. -(void)setUpVertexData
  33. {
  34. GLfloat vertices[] = {
  35. -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
  36. 0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
  37. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  38. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  39. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
  40. -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
  41. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  42. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
  43. 0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
  44. 0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
  45. -0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
  46. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  47. -0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  48. -0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  49. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  50. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  51. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  52. -0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  53. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  54. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  55. 0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  56. 0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  57. 0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  58. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  59. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  60. 0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
  61. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
  62. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
  63. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  64. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  65. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
  66. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  67. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  68. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  69. -0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
  70. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f
  71. };
  72. // 顶点缓存
  73. GLuint bufferID;
  74. glGenBuffers(1, &bufferID);
  75. glBindBuffer(GL_ARRAY_BUFFER, bufferID);
  76. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  77. //顶点坐标数据
  78. glEnableVertexAttribArray(GLKVertexAttribPosition);
  79. glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
  80. //纹理坐标数据
  81. glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
  82. glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
  83. }
  84. - (void)setUpConfig
  85. {
  86. _context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
  87. if (!_context) {
  88. NSLog(@"Create ES context Failed");
  89. }
  90. //设置当前上下文
  91. [EAGLContext setCurrentContext:_context];
  92. GLKView *view =(GLKView *) self.view;
  93. view.context = _context;
  94. view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
  95. view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
  96. glClearColor(0.8, 0.8, 0.8, 1.0);
  97. }
  98. #pragma mark -- GLKViewDelegate
  99. - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
  100. {
  101. glEnable(GL_DEPTH_TEST);
  102. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  103. // 更新旋转
  104. [self update];
  105. [_baseEffect prepareToDraw];
  106. glDrawArrays(GL_TRIANGLES, 0, 48);
  107. }
  108. - (void)update {
  109. _angle = (_angle + 2) % 360;
  110. GLKMatrix4 modelviewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -4.0);
  111. modelviewMatrix = GLKMatrix4Rotate(modelviewMatrix, GLKMathDegreesToRadians(_angle), 0.3, 0.5, 0.7);
  112. _baseEffect.transform.modelviewMatrix = modelviewMatrix;
  113. }
  114. @end

2. Core Animation

使用 Core Animation 完成上面的功能也非常简单,利用 layer.transform 的数据结构CATransform3D即可完成3D变换。

值得一提的是,为了实现立方体整体的旋转动画,我们不需要为为一个面都专门做变换。我们只需要将它们放在同一个父视图中,利用父视图的 layer.sublayerTransform 即可对所有子视图进行统一变换。

image.png

  1. @interface ViewController ()
  2. @property (nonatomic, strong) UIView *containerView;
  3. @end
  4. @implementation ViewController
  5. - (void)addFace:(int)index withTransform:(CATransform3D)transform
  6. {
  7. NSString *filePath = [[NSBundle mainBundle]pathForResource:@"qiyu" ofType:@"jpg"];
  8. UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
  9. imageView.image = [UIImage imageWithContentsOfFile:filePath];
  10. [self.containerView addSubview:imageView];
  11. CGSize containerSize = self.containerView.bounds.size;
  12. imageView.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
  13. // 变换
  14. imageView.layer.transform = transform;
  15. }
  16. - (void)viewDidLoad {
  17. [super viewDidLoad];
  18. self.view.backgroundColor = UIColor.lightGrayColor;
  19. self.containerView = [[UIView alloc] initWithFrame:self.view.bounds];
  20. [self.view addSubview:self.containerView];
  21. //add cube face 1
  22. CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
  23. [self addFace:0 withTransform:transform];
  24. //add cube face 2
  25. transform = CATransform3DMakeTranslation(100, 0, 0);
  26. transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
  27. [self addFace:1 withTransform:transform];
  28. //add cube face 3
  29. transform = CATransform3DMakeTranslation(0, -100, 0);
  30. transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
  31. [self addFace:2 withTransform:transform];
  32. //add cube face 4
  33. transform = CATransform3DMakeTranslation(0, 100, 0);
  34. transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);
  35. [self addFace:3 withTransform:transform];
  36. //add cube face 5
  37. transform = CATransform3DMakeTranslation(-100, 0, 0);
  38. transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);
  39. [self addFace:4 withTransform:transform];
  40. //add cube face 6
  41. transform = CATransform3DMakeTranslation(0, 0, -100);
  42. transform = CATransform3DRotate(transform, M_PI, 0, 1, 0);
  43. [self addFace:5 withTransform:transform];
  44. __block int step = 0;
  45. NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
  46. step = (step + 2) % 360;
  47. self.containerView.layer.sublayerTransform = CATransform3DMakeRotation(M_PI / 180.0 * step, -0.3, 0.5, -0.7);
  48. }];
  49. [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
  50. }
  51. @end