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;
2.1 立方体贴图+旋转
解决了上面的问题,完成立方体贴图+旋转就很简单了。
- 写好立方体的顶点位置。
- - (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 1
CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
[self addFace:0 withTransform:transform];
//add cube face 2
transform = CATransform3DMakeTranslation(100, 0, 0);
transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
[self addFace:1 withTransform:transform];
//add cube face 3
transform = CATransform3DMakeTranslation(0, -100, 0);
transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
[self addFace:2 withTransform:transform];
//add cube face 4
transform = CATransform3DMakeTranslation(0, 100, 0);
transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);
[self addFace:3 withTransform:transform];
//add cube face 5
transform = CATransform3DMakeTranslation(-100, 0, 0);
transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);
[self addFace:4 withTransform:transform];
//add cube face 6
transform = 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