OpenGL ES學(xué)習(xí)筆記(1) 使用GLKView加載一個立方體圖形并使其旋轉(zhuǎn)

由于蘋果在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個步驟:

    1. 使用glGenBuffers()生成新緩存對象。
    2. 使用glBindBuffer()綁定緩存對象。
    3. 使用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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容