项目准备

新建一个iOS项目,值得注意的是:life Cycle请选择 UIKit App Delegate,后续因为需要从UIView中集成,项目还需要做一些变化。

Metal 从ios几开始支持 ios metal 教程_Metal 从ios几开始支持


删除 AppDelegate.swift中关于SceneDeleagete的相关函数,添加一个成员变量window类型为UIWindow。删除info.plist中关于Scene Manifest的配置,添加一个storyboard。命名为main.storyboard,添加一个View Control,设置这个View Control为 “is initial view control”。(尝试拖一个label到view中,运行程序后,查看结果)

Metal 从ios几开始支持 ios metal 教程_Metal_02


Metal 从ios几开始支持 ios metal 教程_指令队列_03

Hello Metal

  1. 添加MyViewController.swift创建 MyViewController 类继承自 UIViewController
  2. 添加参数metalView作为MTKView的持有,并且初始化。
  3. 在main.storyboard中分别对viewcontroler和内部的view设置Class,分别为MyViewController,MTKView。MyViewController的代码如下:
import UIKit
import MetalKit
class MyViewController:UIViewController{
    var metalView:MTKView{
        return view as! MTKView
    }    
    override func viewDidLoad() {        
    }
}

初始化Metal

添加以下代码。运行后,Metal会绘制背景颜色,此时,metal只会在运行的时候去绘制一次,并不能在在一定的从刷新率下去重绘。

enum Colors {
    static let wenderlichGreen = MTLClearColor(red:0.0,green: 0.4,blue: 0.21,alpha: 1.0)
}
override func viewDidLoad() {
      super.viewDidLoad()
      metalView.device = MTLCreateSystemDefaultDevice()// 创建设备
      device = metalView.device //设置到controller的成员变量中
      metalView.clearColor = Colors.wenderlichGreen //设置背景颜色
      commandQueue = device.makeCommandQueue() //为gpu准备指令队列
      let commandBuffer = commandQueue.makeCommandBuffer() //为指令队列设置缓冲区
      let commandEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: metalView.currentRenderPassDescriptor!) //为缓冲区创建一个编码器
      commandEncoder?.endEncoding()//停止编码
      commandBuffer?.present(metalView.currentDrawable as! MTLDrawable) //绘制图像
      commandBuffer?.commit() //提交给gpu
  }

初始化

  1. 首先Metal需要创建一个设备,这个设备可以理解成GPU
  2. GPU需要接受一系列指令,因此我们需要创建一个CommandQueue
  3. 设置一些PiplineState,这些State可以告诉GPU需要执行哪些渲染函数。
  4. 当然,GPU也需要MTLBuffer来存储一些Model和Texture。例如,需要渲染一个三角形的时候,我们需要把定点的位置记在MTLBuffer里面

渲染时

  1. 需要创建一个CommandBuffer,这里存储了如何渲染的信息
  2. CommandBuffer需要创建一个Encoder,Encoder需要找到相关的渲染素材,比如Model和Texture。
  3. 最后需要使用Commit,提交给GPU

实现每帧刷新

  1. controler继承MTKViewDelegate,因此需要实现两个函数,一个是mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize),另外一个是func draw(in view: MTKView) 。
  2. 把MetalView的delegate设置为controler
    整体的代码如下:
import UIKit
import MetalKit

enum Colors {
    static let wenderlichGreen = MTLClearColor(red:0.0,green: 0.4,blue: 0.21,alpha: 1.0)
}

class MyViewController:UIViewController,MTKViewDelegate{
    var metalView:MTKView{
        return view as! MTKView
    }
    var device:MTLDevice!
    var commandQueue:MTLCommandQueue!
    override func viewDidLoad() {
        super.viewDidLoad()
        metalView.device = MTLCreateSystemDefaultDevice()// 创建设备
        device = metalView.device //设置到controller的成员变量中
        metalView.clearColor = Colors.wenderlichGreen //设置背景颜色
        commandQueue = device.makeCommandQueue() //为gpu准备指令队列
        metalView.delegate = self
    }
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {   
    }
    func draw(in view: MTKView) {
        let commandBuffer = commandQueue.makeCommandBuffer() //为指令队列设置缓冲区
        let commandEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: metalView.currentRenderPassDescriptor!) //为缓冲区创建一个编码器
        commandEncoder?.endEncoding()//停止编码
        commandBuffer?.present(metalView.currentDrawable as! MTLDrawable) //绘制图像
        commandBuffer?.commit() //提交给gpu
    }
}