GLKit 框架详细解析(四)—— 一个详细示例和说明(三)

版本记录

版本号 时间
V1.0 2018.08.10

前言

GLKit框架的设计目标是为了简化基于OpenGL或者OpenGL ES的应用开发。 接下来几篇我们就解析一下这个框架。感兴趣的看下面几篇文章。
1. GLKit 框架详细解析(一)—— 基本概览
2. GLKit 框架详细解析(二)—— 一个详细示例和说明(一)
3. GLKit 框架详细解析(三)—— 一个详细示例和说明(二)

示例效果

这个示例是一个旋转的方块,点击就会停止旋转,再次点击就会恢复旋转。

前两篇关于理论等都已经说了很多了,这一篇主要就是给出Swift版本和OC版本的的源码。


Swift源码

下面看一下Swift版的源码。

1. Array+Helpers.swift
import Foundation

//
// MARK: - Array Helpers
//

/// Array extension to help with size/memory calculations when working with OpenGL.
extension Array {
  
  //
  // MARK: - Instance Methods
  //
  
  /// Returns the momory size/footprint (in bytes) of a given array.
  ///
  /// - Returns: Integer value representing the memory size the array.
  func size () -> Int {
    return count * MemoryLayout.size(ofValue: self[0])
  }
}
2. Vertex.swift
import GLKit

//
// MARK: - Vertex
//

/// Structure to hold a vertex's position and color data.
struct Vertex {
  
  /// Stores the X coordinate of a vertex.
  var x: GLfloat
  
  /// Stores the Y coordinate of a vertex.
  var y: GLfloat
  
  /// Stores the Z coordinate of a vertex.
  var z: GLfloat
  
  /// Stores the red color value of a vertex.
  var r: GLfloat
  
  /// Stores the green color value of a vertex.
  var g: GLfloat
  
  /// Stores the blue color value of a vertex.
  var b: GLfloat
  
  /// Stores the alpha value of a vertex.
  var a: GLfloat
  
}
3. ViewController.swift
import GLKit

//
// MARK: - View Controller
//

/// Our subclass of GLKViewController to perform drawing, and logic updates using OpenGL ES.
final class ViewController: GLKViewController {
  
  /// Vertices array that stores 4 Vertex objects used to draw and color a square on screen.
  var Vertices = [
    Vertex(x:  1, y: -1, z: 0, r: 1, g: 0, b: 0, a: 1),
    Vertex(x:  1, y:  1, z: 0, r: 0, g: 1, b: 0, a: 1),
    Vertex(x: -1, y:  1, z: 0, r: 0, g: 0, b: 1, a: 1),
    Vertex(x: -1, y: -1, z: 0, r: 0, g: 0, b: 0, a: 1),
    ]
  
  /// Array used to store the indices in the order we want to draw the triangles of our square.
  var Indices: [GLubyte] = [
    0, 1, 2,
    2, 3, 0
  ]
  
  //
  // MARK: - Variables And Properties
  //
  
  /// Reference to provide easy access to our EAGLContext.
  private var context: EAGLContext?
  
  /// Effect to facilitate having to write shaders in order to achieve shading and lighting.
  private var effect = GLKBaseEffect()
  
  /// Used to store and determine the rotation value of our drawn geometry.
  private var rotation: Float = 0.0
  
  /// Element buffer object. Stores the indices that tell OpenGL what vertices to draw.
  private var ebo = GLuint()
  
  /// Vertex buffer object. Stores our vertex information within the GPU's memory.
  private var vbo = GLuint()
  
  /// Vertex array object. Stores vertex attribute calls that facilitate future drawing. Instead of having to bind/unbind
  /// several buffers constantly to perform drawn, you can simply bind your VAO, make the vertex attribute cals you would
  /// to draw elements on screen, and then whenever you want to draw you simply bind your VAO and it stores those other
  /// vertex attribute calls.
  private var vao = GLuint()
  
  //
  // MARK: - Initialization
  //
  
  /// Method to deinitialize and perform cleanup when the view controller is removed from memory.
  deinit {
    // Delete buffers, cleanup memory, etc.
    tearDownGL()
  }
  
  //
  // MARK: - Private Methods
  //
  
  /// Setup the current OpenGL context, generate and find necessary buffers, and store geometry data in memory (buffers).
  private func setupGL() {
    // Create an OpenGL ES 3.0 context and store it in our local variable.
    context = EAGLContext(api: .openGLES3)
    
    // Set the current EAGLContext to our context we created when performing OpenGL setup.
    EAGLContext.setCurrent(context)
    
    // Perform checks and unwrap options in order to perform more OpenGL setup.
    if let view = self.view as? GLKView, let context = context {
      // Set our view's context to the EAGLContext we just created.s
      view.context = context
      
      // Set ourselves as delegates of GLKViewControllerDelegate
      delegate = self
    }
    
    // Helper variables to identify the position and color attributes for OpenGL calls.
    let vertexAttribColor = GLuint(GLKVertexAttrib.color.rawValue)
    let vertexAttribPosition = GLuint(GLKVertexAttrib.position.rawValue)
    
    // The size, in memory, of a Vertex structure.
    let vertexSize = MemoryLayout<Vertex>.stride
    // The byte offset, in memory, of our color information within a Vertex object.
    let colorOffset = MemoryLayout<GLfloat>.stride * 3
    // Swift pointer object that stores the offset of the color information within our Vertex structure.
    let colorOffsetPointer = UnsafeRawPointer(bitPattern: colorOffset)
    
    // VAO
    
    // Generate and bind a vertex array object.
    glGenVertexArraysOES(1, &vao)
    glBindVertexArrayOES(vao)
    
    // VBO
    
    // Generatea a buffer for our vertex buffer object.
    glGenBuffers(1, &vbo)
    // Bind the vertex buffer object we just generated (created).
    glBindBuffer(GLenum(GL_ARRAY_BUFFER), vbo)
    // Pass data for our vertices to the vertex buffer object.
    glBufferData(GLenum(GL_ARRAY_BUFFER), Vertices.size(), Vertices, GLenum(GL_STATIC_DRAW))
    
    // Enable the position vertex attribute to then specify information about how the position of a vertex is stored.
    glEnableVertexAttribArray(vertexAttribPosition)
    glVertexAttribPointer(vertexAttribPosition, 3, GLenum(GL_FLOAT), GLboolean(UInt8(GL_FALSE)), GLsizei(vertexSize), nil)
    
    // Enable the colors vertex attribute to then specify information about how the color of a vertex is stored.
    glEnableVertexAttribArray(vertexAttribColor)
    glVertexAttribPointer(vertexAttribColor, 4, GLenum(GL_FLOAT), GLboolean(UInt8(GL_FALSE)), GLsizei(vertexSize), colorOffsetPointer)
    
    // EBO
    
    // Generatea a buffer for our element buffer object.
    glGenBuffers(1, &ebo)
    // Bind the element buffer object we just generated (created).
    glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), ebo)
    // Pass data for our element indices to the element buffer object.
    glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), Indices.size(), Indices, GLenum(GL_STATIC_DRAW))
    
    // Unbind all buffers and objects.
    
    // Unbind the vertex buffer and the vertex array object.
    glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
    glBindVertexArrayOES(0)
  }
  
  
  /// Perform cleanup, and delete buffers and memory.
  private func tearDownGL() {
    // Set the current EAGLContext to our context. This ensures we are deleting buffers against it and potentially not a
    // different context.
    EAGLContext.setCurrent(context)
    
    // Delete the vertex array object, the element buffer object, and the vertex buffer object.
    glDeleteBuffers(1, &vao)
    glDeleteBuffers(1, &vbo)
    glDeleteBuffers(1, &ebo)
    
    // Set the current EAGLContext to nil.
    EAGLContext.setCurrent(nil)
    
    // Then nil out or variable that references our EAGLContext.
    context = nil
  }
  
  //
  // MARK: - Touch Handling
  //
  
  /// Used to detect when a tap occurs on screen so we can pause updates of our program.
  ///
  /// - Parameters:
  ///   - touches: The touches that occurred on screen.
  ///   - event: Describes the user interactions in the app.
  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    // Pause or unpause updating our program.
    isPaused = !isPaused
  }
  
  //
  // MARK: - View Controller
  //
  
  /// Called when the view controller's view is loaded into memory.
  override func viewDidLoad() {
    super.viewDidLoad()
    
    // Perform OpenGL setup, create buffers, pass geometry data to memory.
    setupGL()
  }
}

//
// MARK: - GLKViewController Delegate
//
extension ViewController: GLKViewControllerDelegate {
  func glkViewControllerUpdate(_ controller: GLKViewController) {
    let aspect = fabsf(Float(view.bounds.size.width) / Float(view.bounds.size.height))
    let projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), aspect, 4.0, 10.0)
    effect.transform.projectionMatrix = projectionMatrix
    
    var modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -6.0)
    rotation += 90 * Float(timeSinceLastUpdate)
    modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, GLKMathDegreesToRadians(rotation), 0, 0, 1)
    effect.transform.modelviewMatrix = modelViewMatrix
  }
}

//
// MARK: - GLKView Delegate
//

/// Extension to implement the GLKViewDelegate methods.
extension ViewController {

  /// Draw the view's contents using OpenGL ES.
  ///
  /// - Parameters:
  ///   - view: The GLKView object to redraw contents into.
  ///   - rect: Rectangle that describes the area to draw into.
  override func glkView(_ view: GLKView, drawIn rect: CGRect) {
    // Set the color we want to clear the screen with (before drawing) to black.
    glClearColor(0.85, 0.85, 0.85, 1.0)
    // Clear the contents of the screen (the color buffer) with the black color we just set.
    glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
    
    // Compiles the shaders for drawing and binds them to the current context.
    effect.prepareToDraw()
    
    // We bind our vertex array object, essentially indicating we want to use its information to draw geometry on screen.
    glBindVertexArrayOES(vao);
    // Make the call to draw elements on screen. We indicate we want to draw triangles, specify the number of vertices we
    // want to draw via our indices array, and also tell OpenGL what variable type is used to store the index information.
    glDrawElements(GLenum(GL_TRIANGLES), GLsizei(Indices.count), GLenum(GL_UNSIGNED_BYTE), nil)
    // Unbind the vertex array object so future calls don't accidentally use it.
    glBindVertexArrayOES(0)
  }
}

OC源码

下面我们就看一下OC版本的源码。

1. 准备工作

首先删除工程建立时带的ViewController,在控件区找到GLKViewController,拖进去如下所示:

设置sb初始化入口

修改ViewController继承并绑定sb

#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>

@interface ViewController : GLKViewController


@end

将帧率设置为60

这个源码正在写,后续会进行同步更新~~~

后记

本篇主要讲述了一个详细示例和说明,感兴趣的给个赞或者关注~~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,030评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,198评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,995评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,973评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,869评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,766评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,967评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,599评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,886评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,901评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,728评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,504评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,967评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,128评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,445评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,018评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,224评论 2 339

推荐阅读更多精彩内容