Metal 系列教程
Metal_入門01_為什么要學習它
Metal_入門02_帶你走流程
- 有話要說
要學好Metal 它的工作原理,是比較重要的,搞清楚每個類都在干什么事情,就很不錯了,今天只是帶大家跑起來一個工程,熟悉一下相關流程,可能不會用太多的知識點,順便說一句,學過OpenGL 的同學可能理解起來更加容易,但是沒有學過OpenGL 的同學也不要灰心,畢竟兩者沒有任何關系,我們從簡單做起!后面會不斷更新相關技術!
- 步驟
1.創建工程
就和創建一般的應用工程一樣,我選擇的是Swift 語言,為啥用它,個人比較懶,Swift語法寫起來比較簡單。
2.導入框架
注意
為了簡單,我們借助系統提供給我的Metalkit來簡化操作,后面我會教大家只使用Metal 去實現這個過程,由于是入門就不要那么復雜了。
3.創建Metal專用視圖
let mtkView = MTKView(frame: self.view.bounds)
self.view.addSubview(mtkView)
提示:
這個視圖有個屬性就是MTLDevice 必須要指定的,默認是沒有賦值的
4.獲取GPU設備,檢查手機是否支持
guard let device = MTLCreateSystemDefaultDevice() else{
print("不支持Metal,可以在這里使用OpenGL ES 代替Metal")
return
}
提示:
在上一章我們知道,device 代表的就是GPU ,可以創建新的命令隊列,可以分配內存,可以創建紋理和查詢設備信息
5.創建命令線程
let commandQueue = device.makeCommandQueue()
提示:
1.上一章講到過命令線程,主要提供了方法創建命令緩沖對象,MTLCommandBuffer協議為命令緩沖對象定義了一些方法,提供方法去創建命令編碼器,入隊命令緩沖區執行,檢查狀態
2.本實例,我們只用線程隊列創建一個命令緩沖對象
6.創建代表繪圖函數的資源對象
let defaultLibrary = device.newDefaultLibrary()
let fragmentProgram = defaultLibrary?.makeFunction(name: "passThroughFragment")!
let vertexProgram = defaultLibrary?.makeFunction(name: "passThroughVertex")!
提示:
資源對象的作用就是加載Metal 支持的著色器程序,生成MTLFunction 對象,我們在渲染管線描述對象需要使用生成的函數對象
passThroughFragment 和 passThroughVertex 是處理頂點和片段著色器的函數名
7.創建渲染管線描述對象
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = vertexProgram// 指定頂點處理程序
pipelineStateDescriptor.fragmentFunction = fragmentProgram// 指定片段程序
pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat// 指定顏色格式
pipelineStateDescriptor.sampleCount = mtkView.sampleCount// 設置采樣數量
提示:
這個對象的作用,主要是描述渲染管線狀態的配置信息,如指定片段著色器函數,設置渲染像素格式等
頂點著色器和片段著色器程序方法必須指定,顏色格式也必須設置
8.創建管線狀態對象
do {
try pipelineState = device.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
} catch let error {
print("Failed to create pipeline state, error \(error)")
}
提示:
這個對象創建很簡單,就是根據管線描述對象生成需要的管線狀態對象
8.創建緩沖區(頂點和顏色)
let vertexLength = vertexData.count * MemoryLayout<Float>.size
let vertexBuffer = device.makeBuffer(bytes: vertexData, length: vertexLength, options: [])
let colorLength = vertexColorData.count * MemoryLayout<Float>.size
let colorBuffer = device.makeBuffer(bytes: vertexColorData, length: colorLength, options: [])
提示:
MTLBuffer 是我們緩存數據的緩沖區對象
10.創建命令緩沖區
let commandBuffer = commandQueue.makeCommandBuffer()
提示:
這個對象相對比較重要,它攜帶了GPU 渲染圖像的所有數據
11.創建命令編碼器
/// 獲取視圖當前的渲染描述和繪制對象
let renderPassDescriptor = mtkView.currentRenderPassDescriptor
let currentDrawable = mtkView.currentDrawable // 獲取當前幀的繪制對象
/// 創建渲染編碼器
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)
renderEncoder.setRenderPipelineState(pipleState!)// 指定渲染管線對象
renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, at: 0)// 設置頂點緩沖區
renderEncoder.setVertexBuffer(colorBuffer, offset:0 , at: 1)// 設置顏色緩沖區
renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6, instanceCount: 1)// 設置繪制方式
renderEncoder.endEncoding()/// 結束掉這個編碼對象
commandBuffer.present(currentDrawable!) /// 讓繪制對象綁定到當前繪制幀
12.提交
commandBuffer.commit()
提示:
執行這一步,GPU 會記錄命令緩沖區對象,準備渲染
- 附上頂點和顏色數組
let vertexData:[Float] =
[
-1.0, -1.0, 0.0, 1.0,
-1.0, 1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0,
]
let vertexColorData:[Float] =
[
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0,
]
這個時候你運行一下程序就能看到下面的畫面
我制作了一張流程圖幫助大家理解
代碼地址 - 想要不斷學習的同學可以標記一下,后續的代碼都會放在這里