好啦 歡迎大家今天老師會透過一個項目的方式,來跟大家說明一些關於 metal. 的原理
用一句話說 什麼事 metal ?
Low-level access to the GPU
他是一種低級訪問 GPU的 API
Graphics rendering 圖形渲染
Parallel computation並行計算
他可以幫我們有效的減少CPU的負擔,這也就是 metal 的作用,他在我們所有的圖形顯示的底層,比方說 coreAnimation, coreImage, sprictKit, sceneKit.
假設說你有一個很好的點子關於開發遊戲的話,我建議你『不要』 使用 Metal.
首先 metal. 不是一個遊戲引擎,
如果你真的想做一個遊戲的話推薦你用 3DSceneKit. Unity, 2DSprictKit
當然啦 這些不是我們今天要討論的地方,如果你想了解背後的圖形結構原理,還有他們在遊戲引擎底下是如何運作的話,那就需要。Metal. 了以,也就是你可以更加了解他們背後的原因了。
大多數的遊戲套件,都是在用metal. 作為著色氣
假設說你想了解神經網路如何管理大數據,並且還有編寫並行在GPU中的話那就要使用 metal. 了
當然 metal不會運行在模擬器上面,所以我們需要一台A7的設備,
如果你知道 openGL. 的強大的話,你就應該有一歇基本的掌握了。當然啦 如果你一點點 openGL 都不懂的話 也沒關係!因為我們整個裡面也不會用到它,也不會需要大量的數學理解。我們會用的是一些方法來代替她,當然啦,電腦裡面通常都是數學啦
Import model —> Texture
Positon(animate) —> Render
Light
我們把預設的幀數在 60 的基礎上面來製作,那麼我們先來看看到底什麼事 GPU
· GPU & Pipeline
· Triangles
· Shader Functions
· Textures
· Transforms, Matrices
· model I / O
· Lighting
接下來我們要在較高的層面上來看看到底是什麼事 GPU
SOC System On Chip 就是說這個系統是在新片的內部集成起來的。
這裡面說明了 CUP 可以在不同時間點做很多不同的事情。而GPU 可以座很多小任務 在同一時間點。
其實他們不是在同一時間點啦,但是我們可以想像他們是在同一時間點,
想像一下這裡有個數組
[1,2,3,4,5,6,7,8,9]
然後我們要給這個數組裡面每個元素都加 1. 那麼這個時候通常我們會用 for.循環
那麼 CPU的處理方式就是從左到右給他加一
變成 [2,3,4,5,6,7,8,9,10]
那麼如果是 GPU 來做的話
他是一口氣把所有的元素 全部 + 1
所以我們看到的是真的同時處理,GPU 很擅長再處理密集的計算任務
在開始之前 我們先需要一個設備對象
MTLDevice —> MTLCommandQueue —> MTLRenderPipelineState —> MTLBuffer
他會創建這樣的一個隊列來告訴 GPU. 著色器是什麼,然後我們經過一系列特殊的緩衝放到圖片儲存器
比方說 我們要畫出一個三角形,那麼我們要做的是情就是把三角形的每個頂典儲存在緩衝器裡面。所以每一幀我們都需要創建一個新的列表來存放 GPU 的命令。像是在這裡就是三角形的內容。所以這就讓我們
我們就來試試看吧~
首先我們創建一個新項目創建一個簡單的 single view application
然後我們第一件事情就是要導入 MatalKit
import MetalKit
然後我們要在屏幕上面放置一個 MTKView
var matelView: MTKView {
return view as! MTKView
}
然後我們要創建一個變量
var divece: MTLDevice!
var commandQueue: MTLCommandQueue!
然後我們在 viewdidLoad. 裡面 我們創建 GPU,
// set mtkview
matelView.device = MTLCreateSystemDefaultDevice()
device = matelView.device
然後我們來創建一個枚舉來設置我們的顏色
enum Colors {
static let wenderlichGreen = MTLClearColor(red: 0.0, green: 0.4, blue: 0.21, alpha: 1.0)
}
然後我們來使用這個顏色
matelView.clearColor = Colors.wenderlichGreen
commandQueue = device.makeCommandQueue()
接下來我們要創建一個命令緩衝,來容納所有的命令。
// command Buffer
let commandBuffer = commandQueue.makeCommandBuffer()
let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: matelView.currentRenderPassDescriptor!)
Throughout this course you’ll be refactoring
這個地方要特別注意一下,當我們在使用。metal. 的時候, 只能用真機調適
目前,所以地渲染代碼都在 ViewController 裡面,那麼我們今天要挑戰的一點就做一個新的渲染器,讓他用來處理所有的渲染。
當然啦 我們都知道抽代碼很重要,那麼我們就開始啦!
首先我們創建一個 Render.swift
然後在裡面導入 import. Metal
import Foundation
import MetalKit
class Render: NSObject {
let device: MTLDevice!
let commandQueue: MTLCommandQueue!
init(device: MTLDevice) {
self.device = device
self.commandQueue = device.makeCommandQueue()
super.init()
}
}
這邊要說明一下,首先我們要設定設備,然後給他一個命令行,接下來我們要對這兩個常數進行初始化。
接下來我們來到 veiwController 裡面的 extension. 裡面 把原本擴展 ViewController 改成對於 Redner. 的擴展
extension Render: MTKViewDelegate {
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { }
func draw(in view: MTKView) {
guard let drawable = view.currentDrawable,
let descriptor = view.currentRenderPassDescriptor else { return }
let commandBuffer = commandQueue.makeCommandBuffer()
let commandEncoder =
commandBuffer.makeRenderCommandEncoder(descriptor: descriptor)
commandEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
}
然後我們在 viewController 裡面給她增加一個繼承魚 Render的變數較在ㄨ Redner
var render: Render?
然後刪掉。device and commandQueue 這兩個變數
這個時候我們會在 viewdidload. 裡面看到很多報錯
不要驚慌不要害怕,我們來做些修改就好了
override func viewDidLoad() {
super.viewDidLoad()
metalView.device = MTLCreateSystemDefaultDevice()
metalView.device = MTLCreateSystemDefaultDevice()
guard let device = metalView.device else {
fatalError("Device not created. Run on a physical device.")
}
metalView.clearColor = Colors.wenderlichPink
render = Render(device: device)
metalView.delegate = render
}