GPU在绘制的时候,只会去绘制线和三角形。绘制线段比较好理解,但是为啥是三角形,因为,三角形是在二维平面中利用最少的点就能绘制出来的图形。而且,它可以很容易的切割成两个。本文就是使用Metal来绘制一个三角形
Metal绘制的过程
大体上分为两个部分,一个部分是在整体初始化的过程中,初始化的过程主要是编译metal文件,取得三角形定点和整体的着色函数shader,着色函数和颜色格式组成了一个叫做piplineDescriptor的对象,通过piplineDescriptor即可生成一个piplineState。另一部分是渲染,需要做的是生成一个commandBuffer,commandBuffer有点像数据库中的事务,真正渲染的工作需要由commandEncoder来做。这里commandEncoder接受piplineState和相关的数据。流程图如下:
描述一个三角形
- 创建一个存储三角形三个定点的数据结构,这里的-1和0指的是坐标位置,matal中的坐标如下图:
var vetices : [Float] = [
0,0,0,
-1,-1,0,
1,-1,0
]
- 创建绘制三角形的Shader。新建一个metal文件,命名为shader.metal。在文件中,需要创建两个函数,一个是函数是告诉gpu需要绘制的定点位置,另一个是着色函数,告诉gpu使用什么颜色去绘制这个三角形。
vertex float4 vertex_shader(const device packed_float3 *vertices [[buffer(0)]] ,uint vertexId [[vertex_id]] ){
return float4(vertices[vertexId],1);
}
fragment half4 fragment_shader(){
return half4(1,0,0,1);
}
初始化piplineDescriptor和piplineState
let library = device.makeDefaultLibrary()//框架会自动找到项目中的Metal文件
let vertex_shader = library?.makeFunction(name: "vertex_shader")//编译vertex_shader函数
let fragment_shader = library?.makeFunction(name: "fragment_shader")//编译fragment_shader函数
let piplineDescriptor = MTLRenderPipelineDescriptor();//生成piplinDescriptor
piplineDescriptor.vertexFunction = vertex_shader //设置vertex_shader函数
piplineDescriptor.fragmentFunction = fragment_shader//设置fragment_shader函数
piplineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm //设置颜色格式,这个应该是固定写法
do {
piplineState = try device.makeRenderPipelineState(descriptor: piplineDescriptor)//通过piplineDescript生成piplineState
} catch let error as NSError {
print("error \(error.localizedDescription)")
}
绘制
生成一个commandBuffer(简单对比数据库中的事务),通过commandBuffer生成一个encoder。然后为Encode设置piplineState和vertex等数据。
整体代码如下:
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 vetices : [Float] = [
0,0,0,
-1,-1,0,
1,-1,0
]
var metalView:MTKView{
return view as! MTKView
}
var device:MTLDevice!
var commandQueue:MTLCommandQueue!
var vetextBuffer:MTLBuffer!
var piplineState:MTLRenderPipelineState!
override func viewDidLoad() {
super.viewDidLoad()
metalView.device = MTLCreateSystemDefaultDevice()// 创建设备
device = metalView.device //设置到controller的成员变量中
metalView.clearColor = Colors.wenderlichGreen //设置背景颜色
commandQueue = device.makeCommandQueue() //为gpu准备指令队列
vetextBuffer = device.makeBuffer(bytes: vetices, length: vetices.count * MemoryLayout<Float>.size, options: []) //定点存储的位置
let library = device.makeDefaultLibrary()//框架会自动找到项目中的Metal文件
let vertex_shader = library?.makeFunction(name: "vertex_shader")//编译vertex_shader函数
let fragment_shader = library?.makeFunction(name: "fragment_shader")//编译fragment_shader函数
let piplineDescriptor = MTLRenderPipelineDescriptor();//生成piplinDescriptor
piplineDescriptor.vertexFunction = vertex_shader //设置vertex_shader函数
piplineDescriptor.fragmentFunction = fragment_shader//设置fragment_shader函数
piplineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm //设置颜色格式,这个应该是固定写法
do {
piplineState = try device.makeRenderPipelineState(descriptor: piplineDescriptor)//通过piplineDescript生成piplineState
} catch let error as NSError {
print("error \(error.localizedDescription)")
}
metalView.delegate = self
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
func draw(in view: MTKView) {
guard let pState = piplineState else {
return
}
let commandBuffer = commandQueue.makeCommandBuffer() //为指令队列设置缓冲区
let commandEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: metalView.currentRenderPassDescriptor!) //为缓冲区创建一个编码器
commandEncoder?.setRenderPipelineState(pState);
commandEncoder?.setVertexBuffer(vetextBuffer, offset: 0, index: 0)
commandEncoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vetices.count)
commandEncoder?.endEncoding()//停止编码
commandBuffer?.present(metalView.currentDrawable as! MTLDrawable) //绘制图像
commandBuffer?.commit() //提交给gpu
}
}