为了账号安全,请及时绑定邮箱和手机立即绑定

Metal框架详细解析(五十) —— 将项目从OpenGL转化到Metal(三)

标签:
Java

源码

1. Swift

OpenGL源码

首先看下工程组织结构

webp

接着看一下sb文件

webp

下面就是源码了

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 memory 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

final class ViewController: GLKViewController {
  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),
    ]
  
  var Indices: [GLubyte] = [0, 1, 2, 2, 3, 0]  
  private var context: EAGLContext?  private var effect = GLKBaseEffect()  private var rotation: Float = 0.0
  private var ebo = GLuint()  private var vbo = GLuint()  private var vao = GLuint()
  
  deinit {
    tearDownGL()
  }  
  private func setupGL() {
    context = EAGLContext(api: .openGLES3)
    EAGLContext.setCurrent(context)    
    if let view = view as? GLKView, let context = context {
      view.context = context
      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
    let colorOffset = MemoryLayout<GLfloat>.stride * 3
    let colorOffsetPointer = UnsafeRawPointer(bitPattern: colorOffset)    
    // VAO
    
    // Generate and bind a vertex array object.
    glGenVertexArraysOES(1, &vao)
    glBindVertexArrayOES(vao)    
    // VBO
    glGenBuffers(1, &vbo)
    glBindBuffer(GLenum(GL_ARRAY_BUFFER), vbo)
    glBufferData(GLenum(GL_ARRAY_BUFFER), Vertices.size(), Vertices, GLenum(GL_STATIC_DRAW))
    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
    glGenBuffers(1, &ebo)
    glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), ebo)
    glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), Indices.size(), Indices, GLenum(GL_STATIC_DRAW))
    glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
    glBindVertexArrayOES(0)
  }  
  /// Perform cleanup, and delete buffers and memory.
  private func tearDownGL() {
    EAGLContext.setCurrent(context)
    
    glDeleteBuffers(1, &vao)
    glDeleteBuffers(1, &vbo)
    glDeleteBuffers(1, &ebo)
    
    EAGLContext.setCurrent(nil)
    context = nil
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    setupGL()
  }
}

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
  }

  override func glkView(_ view: GLKView, drawIn rect: CGRect) {
    glClearColor(0.85, 0.85, 0.85, 1.0)
    glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
    
    effect.prepareToDraw()
    glBindVertexArrayOES(vao)
    glDrawElements(GLenum(GL_TRIANGLES), GLsizei(Indices.count), GLenum(GL_UNSIGNED_BYTE), nil)
    glBindVertexArrayOES(0)
  }
}

下面看一下实现效果

webp

Metal源码

首先还是看下工程组织结构

webp

接着看sb中的内容

webp

下面就是源码了

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 memory 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
}

struct SceneMatrices {  var projectionMatrix: GLKMatrix4 = GLKMatrix4Identity  var modelviewMatrix: GLKMatrix4 = GLKMatrix4Identity
}
3. ViewController.swift
import GLKit
import MetalKit
final class ViewController: UIViewController {
  @IBOutlet weak var metalView: MTKView!

  private var vertexBuffer: MTLBuffer!
  private var indicesBuffer: MTLBuffer!
  private var metalDevice: MTLDevice!
  private var metalCommandQueue: MTLCommandQueue!
  private var pipelineState: MTLRenderPipelineState!

  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),
    ]
  
  var Indices: [UInt32] = [0, 1, 2, 2, 3, 0]
  
  private var rotation: Float = 0.0
  private var ebo = GLuint()
  private var vbo = GLuint()
  private var vao = GLuint()

  private var sceneMatrices = SceneMatrices()
  private var uniformBuffer: MTLBuffer!
  private var lastUpdateDate = Date()
  
  private func setupMetal() {
    metalDevice = MTLCreateSystemDefaultDevice()  // 1
    metalCommandQueue = metalDevice.makeCommandQueue()  // 2
    metalView.device = metalDevice // 3
    metalView.delegate = self // 4

    let vertexBufferSize = Vertices.size()
    vertexBuffer = metalDevice.makeBuffer(bytes: &Vertices, length: vertexBufferSize, options: .storageModeShared)

    let indicesBufferSize = Indices.size()
    indicesBuffer = metalDevice.makeBuffer(bytes: &Indices, length: indicesBufferSize, options: .storageModeShared)

    let defaultLibrary = metalDevice.makeDefaultLibrary()!
    let fragmentProgram = defaultLibrary.makeFunction(name: "basic_fragment")
    let vertexProgram = defaultLibrary.makeFunction(name: "basic_vertex") // 1

    let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
    pipelineStateDescriptor.vertexFunction = vertexProgram
    pipelineStateDescriptor.fragmentFunction = fragmentProgram
    pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm // 2

    pipelineState = try! metalDevice.makeRenderPipelineState(descriptor: pipelineStateDescriptor) // 3
  }
  
  override func viewDidLoad() {    super.viewDidLoad()
    
    setupMetal()
  }
}// MARK: - MTKView Delegate Extensionextension ViewController: MTKViewDelegate {  // 1
  func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
    let aspect = fabsf(Float(size.width) / Float(size.height))  // 1
    let projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), aspect, 4.0, 10.0)  // 2
    sceneMatrices.projectionMatrix = projectionMatrix  // 3
  }  // 2
  func draw(in view: MTKView) {    // 1
    guard let drawable = view.currentDrawable else {      return
    }

    let renderPassDescriptor = MTLRenderPassDescriptor() // 2
    renderPassDescriptor.colorAttachments[0].texture = drawable.texture // 3
    renderPassDescriptor.colorAttachments[0].loadAction = .clear // 4
    renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.85, green: 0.85, blue: 0.85, alpha: 1.0) // 5

    // 6
    guard let commandBuffer = metalCommandQueue.makeCommandBuffer() else {      return
    }    // 7
    guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {      return
    }    // Frame drawing goes here
    renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)  // 1
    // Update logic
    var modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -6.0)
    let timeSinceLastUpdate = lastUpdateDate.timeIntervalSince(Date())
    rotation += 90 * Float(timeSinceLastUpdate) // 1
    modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, GLKMathDegreesToRadians(rotation), 0, 0, 1)  // 2
    sceneMatrices.modelviewMatrix = modelViewMatrix    // Set uniform buffer
    let uniformBufferSize = MemoryLayout.size(ofValue: sceneMatrices)
    uniformBuffer = metalDevice.makeBuffer(bytes: &sceneMatrices, length: uniformBufferSize, options: .storageModeShared) // 2
    renderEncoder.setVertexBuffer(uniformBuffer, offset: 0, index: 1)  // 3

    renderEncoder.setRenderPipelineState(pipelineState)
    renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: Indices.count, indexType: .uint32, indexBuffer: indicesBuffer, indexBufferOffset: 0) // 2

    renderEncoder.endEncoding() // 8
    commandBuffer.addCompletedHandler { _ in
      self.lastUpdateDate = Date()
    }
    commandBuffer.present(drawable) // 9
    commandBuffer.commit() // 10
  }
}

具体效果同OpenGL一样,这里就不展示了。

后记

本篇主要讲述了将项目从OpenGL转化到Metal,感兴趣的给个赞或者关注~~



作者:刀客传奇
链接:https://www.jianshu.com/p/ad2ceae81a2b


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消