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, //左下
};
雖然我們的代碼看起來是繪制了一個正方形戈锻,但由于視口(視圖)寬高比的問題產(chǎn)生了拉伸問題。如下圖所示:
為了解決這個問題轩性,我們可以設置透視投影矩陣声登。又由于平截頭體可視范圍的問題,我們需要將頂點向后移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 立方體貼圖+旋轉(zhuǎn)
解決了上面的問題悯嗓,完成立方體貼圖+旋轉(zhuǎn)就很簡單了。
- 寫好立方體的頂點位置卸察。
- - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect 會不斷回調(diào)脯厨,在里面更新模型視圖變換矩陣就可以了。
OpenGL ES
@interface DemoViewController ()
{
EAGLContext *_context;
GLKBaseEffect *_baseEffect;
int _angle;
}
@end
@implementation DemoViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1.OpenGL ES 相關(guān)初始化
[self setUpConfig];
//2.加載頂點/紋理坐標數(shù)據(jù)
[self setUpVertexData];
//3.加載紋理數(shù)據(jù)(使用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);
//頂點坐標數(shù)據(jù)
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
//紋理坐標數(shù)據(jù)
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);
// 更新旋轉(zhuǎn)
[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
的數(shù)據(jù)結(jié)構(gòu)CATransform3D
即可完成3D變換合武。
值得一提的是,為了實現(xiàn)立方體整體的旋轉(zhuǎn)動畫涡扼,我們不需要為為一個面都專門做變換稼跳。我們只需要將它們放在同一個父視圖中,利用父視圖的layer.sublayerTransform
即可對所有子視圖進行統(tǒng)一變換吃沪。
Core Animation
@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