由于蘋果在iOS12棄用了OpenGL ES,Xcode大量提示警告很是礙眼,先在Build Settings - Other Warning Flags 添加 -Wno-deprecated-declarations 忽略警告。
下面進入正題
代碼附帶參數(shù)說明
- 導(dǎo)入頭文件#import <GLKit/GLKit.h>
- 定義struct 紋理坐標為二維向量,頂點坐標和法線為三維向量
typedef struct {
GLKVector3 positionCoord; //頂點坐標 x,y,z
GLKVector2 textureCoord; //紋理坐標 x,y
GLKVector3 normal; //法線 x,y,z
} CCVertex;
- 定義對象以及變量
@property (nonatomic, strong) GLKView *glkView;
@property (nonatomic, strong) GLKBaseEffect *baseEffect;//類似shader,包含對頂點著色器和片元著色器的處理
@property (nonatomic, assign) CCVertex *vertices;//包含頂點坐標、紋理坐標、法線的結(jié)構(gòu)體
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) NSInteger angle;//角度
@property (nonatomic, assign) GLuint vertexBuffer;//頂點緩存區(qū)
接下來分為總體分為是三部分
[self commonInit];
[self vertexDataSetup];
[self addCADisplayLink];
- OpenGL ES相關(guān)的初始化
- (void)commonInit {
//創(chuàng)建上下文,選擇OpenGL ES3.0
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
//設(shè)置context為當前上下文
[EAGLContext setCurrentContext:context];
CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
self.glkView = [[GLKView alloc] initWithFrame:frame context:context];
self.glkView.backgroundColor = [UIColor clearColor];
self.glkView.delegate = self;
//設(shè)置深度緩存格式,也可以用GLKViewDrawableDepthFormat16,區(qū)別只是空間大小
self.glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
//默認是(0, 1),這里用于翻轉(zhuǎn) z 軸
glDepthRangef(1, 0);
[self.view addSubview:self.glkView];
//獲取圖片資源
NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"jugg.jpg"];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft : @(YES)};
//設(shè)置紋理參數(shù)以及貼圖
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:[image CGImage] options:options error:nil];
//將textureInfo賦值給self.baseEffect.texture2d0,GLKBaseEffect最多可以處理2個紋理
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.texture2d0.name = textureInfo.name;
self.baseEffect.texture2d0.target = textureInfo.target;
}
-
頂點緩存對象(VBO)
創(chuàng)建VBO需要3個步驟:
- 使用glGenBuffers()生成新緩存對象。
- 使用glBindBuffer()綁定緩存對象。
- 使用glBufferData()將頂點數(shù)據(jù)拷貝到緩存對象中。
-(void)vertexDataSetup
{
/*
解釋一下:
這里我們不復(fù)用頂點,使用每 3 個點畫一個三角形的方式,需要 12 個三角形,則需要 36 個頂點
以下的數(shù)據(jù)用來繪制以(0,0,0)為中心,邊長為 1 的立方體
*/
//8. 開辟頂點數(shù)據(jù)空間(數(shù)據(jù)結(jié)構(gòu)SenceVertex 大小 * 頂點個數(shù)kCoordCount)
self.vertices = malloc(sizeof(CCVertex) * kCoordCount);
// 前面 頂點坐標 紋理坐標
self.vertices[0] = (CCVertex){{-0.5, 0.5, 0.5}, {0, 1}};
self.vertices[1] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 0}};
self.vertices[2] = (CCVertex){{0.5, 0.5, 0.5}, {1, 1}};
self.vertices[3] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 0}};
self.vertices[4] = (CCVertex){{0.5, 0.5, 0.5}, {1, 1}};
self.vertices[5] = (CCVertex){{0.5, -0.5, 0.5}, {1, 0}};
// 上面
self.vertices[6] = (CCVertex){{0.5, 0.5, 0.5}, {1, 1}};
self.vertices[7] = (CCVertex){{-0.5, 0.5, 0.5}, {0, 1}};
self.vertices[8] = (CCVertex){{0.5, 0.5, -0.5}, {1, 0}};
self.vertices[9] = (CCVertex){{-0.5, 0.5, 0.5}, {0, 1}};
self.vertices[10] = (CCVertex){{0.5, 0.5, -0.5}, {1, 0}};
self.vertices[11] = (CCVertex){{-0.5, 0.5, -0.5}, {0, 0}};
// 下面
self.vertices[12] = (CCVertex){{0.5, -0.5, 0.5}, {1, 1}};
self.vertices[13] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 1}};
self.vertices[14] = (CCVertex){{0.5, -0.5, -0.5}, {1, 0}};
self.vertices[15] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 1}};
self.vertices[16] = (CCVertex){{0.5, -0.5, -0.5}, {1, 0}};
self.vertices[17] = (CCVertex){{-0.5, -0.5, -0.5}, {0, 0}};
// 左面
self.vertices[18] = (CCVertex){{-0.5, 0.5, 0.5}, {1, 1}};
self.vertices[19] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 1}};
self.vertices[20] = (CCVertex){{-0.5, 0.5, -0.5}, {1, 0}};
self.vertices[21] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 1}};
self.vertices[22] = (CCVertex){{-0.5, 0.5, -0.5}, {1, 0}};
self.vertices[23] = (CCVertex){{-0.5, -0.5, -0.5}, {0, 0}};
// 右面
self.vertices[24] = (CCVertex){{0.5, 0.5, 0.5}, {1, 1}};
self.vertices[25] = (CCVertex){{0.5, -0.5, 0.5}, {0, 1}};
self.vertices[26] = (CCVertex){{0.5, 0.5, -0.5}, {1, 0}};
self.vertices[27] = (CCVertex){{0.5, -0.5, 0.5}, {0, 1}};
self.vertices[28] = (CCVertex){{0.5, 0.5, -0.5}, {1, 0}};
self.vertices[29] = (CCVertex){{0.5, -0.5, -0.5}, {0, 0}};
// 后面
self.vertices[30] = (CCVertex){{-0.5, 0.5, -0.5}, {0, 1}};
self.vertices[31] = (CCVertex){{-0.5, -0.5, -0.5}, {0, 0}};
self.vertices[32] = (CCVertex){{0.5, 0.5, -0.5}, {1, 1}};
self.vertices[33] = (CCVertex){{-0.5, -0.5, -0.5}, {0, 0}};
self.vertices[34] = (CCVertex){{0.5, 0.5, -0.5}, {1, 1}};
self.vertices[35] = (CCVertex){{0.5, -0.5, -0.5}, {1, 0}};
//開辟緩存區(qū) VBO
//glGenBuffers()創(chuàng)建緩存對象并且返回緩存對象的標示符。它需要2個參數(shù):第一個為需要創(chuàng)建的緩存數(shù)量,第二個為用于存儲單一ID或多個ID的GLuint變量或數(shù)組的地址,所以此處需要用&取地址符。
glGenBuffers(1, &_vertexBuffer);
//當緩存對象創(chuàng)建之后,在使用緩存對象之前,我們需要將緩存對象連接到相應(yīng)的緩存上。glBindBuffer()有2個參數(shù):target與buffer。
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
GLsizeiptr bufferSizeBytes = sizeof(CCVertex) * kCoordCount;
//當緩存初始化之后,你可以使用glBufferData()將數(shù)據(jù)拷貝到緩存對象。
//第一個參數(shù)target可以為GL_ARRAY_BUFFER或GL_ELEMENT_ARRAY。size為待傳遞數(shù)據(jù)字節(jié)數(shù)量。第三個參數(shù)為源數(shù)據(jù)數(shù)組指針,如data為NULL,則VBO僅僅預(yù)留給定數(shù)據(jù)大小的內(nèi)存空間。最后一個參數(shù)usage標志位VBO的另一個性能提示,它提供緩存對象將如何使用:static、dynamic或stream、與read、copy或draw。
glBufferData(GL_ARRAY_BUFFER, bufferSizeBytes, self.vertices, GL_STATIC_DRAW);
//頂點數(shù)據(jù)
glEnableVertexAttribArray(GLKVertexAttribPosition);
/*
glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *ptr)
*index*指定要修改的頂點屬性的索引值
*size*指定每個頂點屬性的組件數(shù)量。必須為1、 2、3或者4。初始值為4。(如position是由3個(x,y,z)組成,而顏色是4個(r,g,b,a))
*type*指定數(shù)組中每個組件的數(shù)據(jù)類型。可用的符號常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值為GL_FLOAT。
*normalized*指定當被訪問時,固定點數(shù)據(jù)值是否應(yīng)該被歸一化(GL_TRUE)或者直接轉(zhuǎn)換為固定點值(GL_FALSE)。
*stride*指定連續(xù)頂點屬性之間的偏移量。如果為0,那么頂點屬性會被理解為:它們是緊密排列在一起的。初始值為0。
*pointer*指定第一個組件在數(shù)組的第一個頂點屬性中的偏移量。該數(shù)組與GL_ARRAY_BUFFER綁定,儲存于緩沖區(qū)中。初始值為0
*/
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(CCVertex), NULL + offsetof(CCVertex, positionCoord));
//紋理數(shù)據(jù)
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(CCVertex), NULL + offsetof(CCVertex, textureCoord));
}
- 創(chuàng)建CADisplayLink
CADisplayLink是用于同步屏幕刷新頻率的計時器,將CADisplayLink加入runLoop保證了一個基于屏幕刷新頻率的不斷重繪的過程
- (void)addCADisplayLink {
self.angle = 0;
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(reDisplay)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)reDisplay {
//計算旋轉(zhuǎn)度數(shù),360代表旋轉(zhuǎn)一整周
self.angle = (self.angle + 5) % 360;
//旋轉(zhuǎn)模型視圖矩陣,GLKMathDegreesToRadians角度轉(zhuǎn)弧度
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(self.angle), 0.5, 1, -0.5);
//重新渲染
[self.glkView display];
}
- 實現(xiàn)GLKView的代理方法
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
//開啟深度測試
glEnable(GL_DEPTH_TEST);
//清除顏色緩存區(qū)&深度緩存區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//準備繪制
[self.baseEffect prepareToDraw];
//繪制
glDrawArrays(GL_TRIANGLES, 0, kCoordCount);
}
- 最后,不要忘記釋放內(nèi)存,非OC代碼申請的內(nèi)存空間需要自己手動釋放
- (void)dealloc {
if ([EAGLContext currentContext] == self.glkView.context) {
[EAGLContext setCurrentContext:nil];
}
if (_vertices) {
free(_vertices);
_vertices = nil;
}
if (_vertexBuffer) {
glDeleteBuffers(1, &_vertexBuffer);
_vertexBuffer = 0;
}
//displayLink 失效
[self.displayLink invalidate];
}
-
最后附上運行效果
Simulator Screen Shot - iPhone X? - 2019-06-05 at 15.13.54.png
