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

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

后记

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

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容