Metal 系列教程
Metal_入門01_為什么要學(xué)習(xí)它
Metal_入門02_帶你走流程
- 有話要說
要學(xué)好Metal 它的工作原理,是比較重要的,搞清楚每個類都在干什么事情,就很不錯了,今天只是帶大家跑起來一個工程,熟悉一下相關(guān)流程,可能不會用太多的知識點(diǎn),順便說一句,學(xué)過OpenGL 的同學(xué)可能理解起來更加容易,但是沒有學(xué)過OpenGL 的同學(xué)也不要灰心,畢竟兩者沒有任何關(guān)系,我們從簡單做起!后面會不斷更新相關(guān)技術(shù)!
- 步驟
1.創(chuàng)建工程

就和創(chuàng)建一般的應(yīng)用工程一樣,我選擇的是Swift 語言,為啥用它,個人比較懶,Swift語法寫起來比較簡單。
2.導(dǎo)入框架

注意
為了簡單,我們借助系統(tǒng)提供給我的Metalkit來簡化操作,后面我會教大家只使用Metal 去實現(xiàn)這個過程,由于是入門就不要那么復(fù)雜了。
3.創(chuàng)建Metal專用視圖
let mtkView = MTKView(frame: self.view.bounds)
self.view.addSubview(mtkView)
提示:
這個視圖有個屬性就是MTLDevice 必須要指定的,默認(rèn)是沒有賦值的
4.獲取GPU設(shè)備,檢查手機(jī)是否支持
guard let device = MTLCreateSystemDefaultDevice() else{
print("不支持Metal,可以在這里使用OpenGL ES 代替Metal")
return
}
提示:
在上一章我們知道,device 代表的就是GPU ,可以創(chuàng)建新的命令隊列,可以分配內(nèi)存,可以創(chuàng)建紋理和查詢設(shè)備信息
5.創(chuàng)建命令線程
let commandQueue = device.makeCommandQueue()
提示:
1.上一章講到過命令線程,主要提供了方法創(chuàng)建命令緩沖對象,MTLCommandBuffer協(xié)議為命令緩沖對象定義了一些方法,提供方法去創(chuàng)建命令編碼器,入隊命令緩沖區(qū)執(zhí)行,檢查狀態(tài)
2.本實例,我們只用線程隊列創(chuàng)建一個命令緩沖對象
6.創(chuàng)建代表繪圖函數(shù)的資源對象
let defaultLibrary = device.newDefaultLibrary()
let fragmentProgram = defaultLibrary?.makeFunction(name: "passThroughFragment")!
let vertexProgram = defaultLibrary?.makeFunction(name: "passThroughVertex")!
提示:
資源對象的作用就是加載Metal 支持的著色器程序,生成MTLFunction 對象,我們在渲染管線描述對象需要使用生成的函數(shù)對象
passThroughFragment 和 passThroughVertex 是處理頂點(diǎn)和片段著色器的函數(shù)名

7.創(chuàng)建渲染管線描述對象
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = vertexProgram// 指定頂點(diǎn)處理程序
pipelineStateDescriptor.fragmentFunction = fragmentProgram// 指定片段程序
pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat// 指定顏色格式
pipelineStateDescriptor.sampleCount = mtkView.sampleCount// 設(shè)置采樣數(shù)量
提示:
這個對象的作用,主要是描述渲染管線狀態(tài)的配置信息,如指定片段著色器函數(shù),設(shè)置渲染像素格式等
頂點(diǎn)著色器和片段著色器程序方法必須指定,顏色格式也必須設(shè)置
8.創(chuàng)建管線狀態(tài)對象
do {
try pipelineState = device.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
} catch let error {
print("Failed to create pipeline state, error \(error)")
}
提示:
這個對象創(chuàng)建很簡單,就是根據(jù)管線描述對象生成需要的管線狀態(tài)對象

8.創(chuàng)建緩沖區(qū)(頂點(diǎn)和顏色)
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 是我們緩存數(shù)據(jù)的緩沖區(qū)對象
10.創(chuàng)建命令緩沖區(qū)
let commandBuffer = commandQueue.makeCommandBuffer()
提示:
這個對象相對比較重要,它攜帶了GPU 渲染圖像的所有數(shù)據(jù)
11.創(chuàng)建命令編碼器
/// 獲取視圖當(dāng)前的渲染描述和繪制對象
let renderPassDescriptor = mtkView.currentRenderPassDescriptor
let currentDrawable = mtkView.currentDrawable // 獲取當(dāng)前幀的繪制對象
/// 創(chuàng)建渲染編碼器
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)
renderEncoder.setRenderPipelineState(pipleState!)// 指定渲染管線對象
renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, at: 0)// 設(shè)置頂點(diǎn)緩沖區(qū)
renderEncoder.setVertexBuffer(colorBuffer, offset:0 , at: 1)// 設(shè)置顏色緩沖區(qū)
renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6, instanceCount: 1)// 設(shè)置繪制方式
renderEncoder.endEncoding()/// 結(jié)束掉這個編碼對象
commandBuffer.present(currentDrawable!) /// 讓繪制對象綁定到當(dāng)前繪制幀
12.提交
commandBuffer.commit()
提示:
執(zhí)行這一步,GPU 會記錄命令緩沖區(qū)對象,準(zhǔn)備渲染
- 附上頂點(diǎn)和顏色數(shù)組
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,
]
這個時候你運(yùn)行一下程序就能看到下面的畫面

我制作了一張流程圖幫助大家理解

代碼地址 - 想要不斷學(xué)習(xí)的同學(xué)可以標(biāo)記一下,后續(xù)的代碼都會放在這里