版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2019.01.18 星期五 |
前言
很多做視頻和圖像的,相信對這個框架都不是很陌生,它渲染高級3D圖形,并使用GPU執行數據并行計算。接下來的幾篇我們就詳細的解析這個框架。感興趣的看下面幾篇文章。
1. Metal框架詳細解析(一)—— 基本概覽
2. Metal框架詳細解析(二) —— 器件和命令(一)
3. Metal框架詳細解析(三) —— 渲染簡單的2D三角形(一)
4. Metal框架詳細解析(四) —— 關于GPU Family 4(一)
5. Metal框架詳細解析(五) —— 關于GPU Family 4之關于Imageblocks(二)
6. Metal框架詳細解析(六) —— 關于GPU Family 4之關于Tile Shading(三)
7. Metal框架詳細解析(七) —— 關于GPU Family 4之關于光柵順序組(四)
8. Metal框架詳細解析(八) —— 關于GPU Family 4之關于增強的MSAA和Imageblock采樣覆蓋控制(五)
9. Metal框架詳細解析(九) —— 關于GPU Family 4之關于線程組共享(六)
10. Metal框架詳細解析(十) —— 基本組件(一)
11. Metal框架詳細解析(十一) —— 基本組件之器件選擇 - 圖形渲染的器件選擇(二)
12. Metal框架詳細解析(十二) —— 基本組件之器件選擇 - 計算處理的設備選擇(三)
13. Metal框架詳細解析(十三) —— 計算處理(一)
14. Metal框架詳細解析(十四) —— 計算處理之你好,計算(二)
15. Metal框架詳細解析(十五) —— 計算處理之關于線程和線程組(三)
16. Metal框架詳細解析(十六) —— 計算處理之計算線程組和網格大小(四)
17. Metal框架詳細解析(十七) —— 工具、分析和調試(一)
18. Metal框架詳細解析(十八) —— 工具、分析和調試之Metal GPU Capture(二)
19. Metal框架詳細解析(十九) —— 工具、分析和調試之GPU活動監視器(三)
20. Metal框架詳細解析(二十) —— 工具、分析和調試之關于Metal著色語言文件名擴展名、使用Metal的命令行工具構建庫和標記Metal對象和命令(四)
21. Metal框架詳細解析(二十一) —— 基本課程之基本緩沖區(一)
22. Metal框架詳細解析(二十二) —— 基本課程之基本紋理(二)
23. Metal框架詳細解析(二十三) —— 基本課程之CPU和GPU同步(三)
24. Metal框架詳細解析(二十四) —— 基本課程之參數緩沖 - 基本參數緩沖(四)
25. Metal框架詳細解析(二十五) —— 基本課程之參數緩沖 - 帶有數組和資源堆的參數緩沖區(五)
26. Metal框架詳細解析(二十六) —— 基本課程之參數緩沖 - 具有GPU編碼的參數緩沖區(六)
27. Metal框架詳細解析(二十七) —— 高級技術之圖層選擇的反射(一)
28. Metal框架詳細解析(二十八) —— 高級技術之使用專用函數的LOD(一)
29. Metal框架詳細解析(二十九) —— 高級技術之具有參數緩沖區的動態地形(一)
30. Metal框架詳細解析(三十) —— 延遲照明(一)
31. Metal框架詳細解析(三十一) —— 在視圖中混合Metal和OpenGL渲染(一)
32. Metal框架詳細解析(三十二) —— Metal渲染管道教程(一)
33. Metal框架詳細解析(三十三) —— Metal渲染管道教程(二)
34. Metal框架詳細解析(三十四) —— Hello Metal! 一個簡單的三角形的實現(一)
35. Metal框架詳細解析(三十五) —— Hello Metal! 一個簡單的三角形的實現(二)
36. Metal框架詳細解析(三十六) —— Metal編程指南之概覽(一)
37. Metal框架詳細解析(三十七) —— Metal編程指南之基本Metal概念(二)
38. Metal框架詳細解析(三十八) —— Metal編程指南之命令組織和執行模型(三)
39. Metal框架詳細解析(三十九) —— Metal編程指南之資源對象:緩沖區和紋理(四)
40. Metal框架詳細解析(四十) —— Metal編程指南之函數和庫(五)
41. Metal框架詳細解析(四十一) —— Metal編程指南之圖形渲染:渲染命令編碼器之Part 1(六)
42. Metal框架詳細解析(四十二) —— Metal編程指南之圖形渲染:渲染命令編碼器之Part 2(七)
43. Metal框架詳細解析(四十三) —— Metal編程指南之數據并行計算處理:計算命令編碼器(八)
44. Metal框架詳細解析(四十四) —— Metal編程指南之緩沖和紋理操作:Blit命令編碼器(九)
45. Metal框架詳細解析(四十五) —— Metal編程指南之Metal工具(十)
46. Metal框架詳細解析(四十六) —— Metal編程指南之Tessellation(十一)
47. Metal框架詳細解析(四十七) —— Metal編程指南之資源堆(十二)
48. Metal框架詳細解析(四十八) —— 將項目從OpenGL轉化到Metal(一)
Adding Shaders
創建一個新文件。 單擊File ? New ? File…
,選擇iOS ? Source ? Metal File
。 點擊下一步。 將其命名為Shaders.metal
并將其保存在您想要的任何位置。
Metal使用C ++
編寫著色器。 在大多數情況下,它類似于用于OpenGL
的GLSL
。
將其添加到文件的底部:
struct VertexIn {
packed_float3 position;
packed_float4 color;
};
struct VertexOut {
float4 computedPosition [[position]];
float4 color;
};
您將使用這些結構作為頂點著色器的輸入和輸出數據。
1. Writing a Vertex Shader
頂點著色器是為您繪制的每個頂點運行的函數。
在結構下面,添加以下代碼:
vertex VertexOut basic_vertex( // 1
const device VertexIn* vertex_array [[ buffer(0) ]], // 2
unsigned int vid [[ vertex_id ]]) { // 3
// 4
VertexIn v = vertex_array[vid];
// 5
VertexOut outVertex = VertexOut();
outVertex.computedPosition = float4(v.position, 1.0);
outVertex.color = v.color;
return outVertex;
}
- 1)
vertex
表示這是一個頂點著色器函數。 此著色器的返回類型是VertexOut
。 - 2) 在這里,您將獲得傳遞給命令編碼器的頂點緩沖區。
- 3) 此參數是為此著色器調用的
vertex id
。 - 4) 抓取當前
vertex id
的輸入頂點。 - 5) 在這里,您創建一個
VertexOut
并從當前VertexIn
傳遞數據。 這只是使用與輸入相同的位置和顏色。
此時,頂點著色器的工作已完成。
2. Writing a Fragment Shader
頂點著色器完成后,片段著色器將針對每個可能的像素運行。
在頂點著色器下方,添加以下代碼:
fragment float4 basic_fragment(VertexOut interpolated [[stage_in]]) {
return float4(interpolated.color);
}
片段著色器接收頂點著色器的輸出 - VertexOut
。 然后,片段著色器返回當前片段的顏色。
3. Hooking up the Shaders to the Pipeline
著色器已就位,但您還沒有將它們連接到您的管道上。 為此,請返回ViewController.swift
,并將此屬性添加到類中:
private var pipelineState: MTLRenderPipelineState!
此屬性將包含著色器數據。
現在,找到setupMetal()
。 在方法的底部添加以下內容:
// 1
let defaultLibrary = metalDevice.makeDefaultLibrary()!
let fragmentProgram = defaultLibrary.makeFunction(name: "basic_fragment")
let vertexProgram = defaultLibrary.makeFunction(name: "basic_vertex")
// 2
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = vertexProgram
pipelineStateDescriptor.fragmentFunction = fragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
// 3
pipelineState = try! metalDevice
.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
這就是代碼的作用:
- 1) 在所有
.metal
文件中按名稱查找頂點和片段著色器。 - 2) 使用頂點著色器和片段著色器創建描述符對象。 像素格式設置為8位無符號的標準
BGRA(Blue Green Red Alpha)
。 - 3) 讓GPU將所有內容編譯到GPU優化
(GPU-optimized)
對象中。
現在,管道狀態已準備就緒。 是時候使用它了。 為此,在draw(in:)
中,下面代碼的前面:
renderEncoder.drawIndexedPrimitives(
type: .triangle,
indexCount: Indices.count,
indexType: .uint32,
indexBuffer: indicesBuffer,
indexBufferOffset: 0)
添加
renderEncoder.setRenderPipelineState(pipelineState)
構建并運行,你會看到彩色的屏幕。
Matrices
為了操縱場景,您需要將投影和模型視圖矩陣(projection and model-view matrices)
傳遞給GPU。 投影矩陣允許您操縱場景的感知,使更近的物體看起來比更遠的物體更大。 模型視圖矩陣允許您操縱對象或整個場景的位置,旋轉和縮放。
為了使用這些矩陣,您將創建一個新的結構。 打開Vertex.swift
。 在文件的頂部,添加:
struct SceneMatrices {
var projectionMatrix: GLKMatrix4 = GLKMatrix4Identity
var modelviewMatrix: GLKMatrix4 = GLKMatrix4Identity
}
請注意,您仍將使用GLKMatrix4
。 這一部分不使用GLKit
,因此您可以將它用于Metal中的矩陣。
現在,打開ViewController.swift
,并添加兩個新屬性:
private var sceneMatrices = SceneMatrices()
private var uniformBuffer: MTLBuffer!
到方法draw(in:)
中,在下面之前
renderEncoder.setRenderPipelineState(pipelineState)
添加
// 1
let modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -6.0)
sceneMatrices.modelviewMatrix = modelViewMatrix
// 2
let uniformBufferSize = MemoryLayout.size(ofValue: sceneMatrices)
uniformBuffer = metalDevice.makeBuffer(
bytes: &sceneMatrices,
length: uniformBufferSize,
options: .storageModeShared)
// 3
renderEncoder.setVertexBuffer(uniformBuffer, offset: 0, index: 1)
以下是上面代碼的作用:
- 1) 創建一個矩陣,將對象向后移動6個單位,使其看起來更小。
- 2) 像之前使用頂點緩沖區一樣創建一個統一的緩沖區,但是使用矩陣數據。
- 3) 將統一緩沖區連接到管道并將其標識符設置為1。
1. Projection Matrix
當你仍然在ViewController.swift
中,在mtkView(_:drawableSizeWillChange :)
中,添加以下內容:
let aspect = fabsf(Float(size.width) / Float(size.height))
let projectionMatrix = GLKMatrix4MakePerspective(
GLKMathDegreesToRadians(65.0),
aspect,
4.0,
10.0)
sceneMatrices.projectionMatrix = projectionMatrix
此代碼基于視圖的縱橫比創建投影矩陣。 然后,它將它分配給場景的投影矩陣。
有了這個,您的方塊現在看起來方形,而不是伸展到整個屏幕。
2. Matrices in Shaders
接下來,您需要在著色器中接收矩陣數據。 打開Shaders.metal
。 在最頂層,添加一個新結構:
struct SceneMatrices {
float4x4 projectionMatrix;
float4x4 viewModelMatrix;
};
現在,用以下內容替換basic_vertex
函數:
vertex VertexOut basic_vertex(
const device VertexIn* vertex_array [[ buffer(0) ]],
const device SceneMatrices& scene_matrices [[ buffer(1) ]], // 1
unsigned int vid [[ vertex_id ]]) {
// 2
float4x4 viewModelMatrix = scene_matrices.viewModelMatrix;
float4x4 projectionMatrix = scene_matrices.projectionMatrix;
VertexIn v = vertex_array[vid];
// 3
VertexOut outVertex = VertexOut();
outVertex
.computedPosition = projectionMatrix * viewModelMatrix * float4(v.position, 1.0);
outVertex.color = v.color;
return outVertex;
}
下面就是代碼做的事情:
- 1) 接收矩陣作為頂點著色器內的參數。
- 2) 提取視圖模型和投影矩陣。
- 3) 投影和視圖模型矩陣乘以位置。
構建并運行應用程序。 你應該看到這個:
這就是一個正方形了。
Making it Spin
在OpenGL實現中,GLViewController
提供了lastUpdateDate
,它將告訴您何時執行最后一次渲染。 在Metal中,你必須自己創建它。
首先,在ViewController
中添加一個新屬性:
private var lastUpdateDate = Date()
在方法draw(in: )
里,下面之前:
commandBuffer.present(drawable)
添加下面代碼
commandBuffer.addCompletedHandler { _ in
self.lastUpdateDate = Date()
}
有了這個,當一幀繪制完成時,它會將lastUpdateDate
更新為當前日期和時間。
現在,是時候旋轉了! 在draw(in :)
中,替換:
let modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -6.0)
使用下面的內容
var modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -6.0)
let timeSinceLastUpdate = lastUpdateDate.timeIntervalSince(Date())
// 1
rotation += 90 * Float(timeSinceLastUpdate)
// 2
modelViewMatrix = GLKMatrix4Rotate(
modelViewMatrix,
GLKMathDegreesToRadians(rotation), 0, 0, 1)
這會將rotation
屬性增加一個與最后一次渲染和此渲染之間的時間成比例的量。 然后,它將圍繞Z軸的旋轉應用于模型視圖矩陣。
構建并運行應用程序。 你會看到立方體旋轉。 成功!
恭喜! 你已經學到了很多關于Metal API
的知識! 現在,您了解了Metal中一些最重要的概念,例如著色器,設備,命令緩沖區和管道(shaders, devices, command buffers, and pipelines)
,并且您對OpenGL和Metal之間的差異進行深入的了解。
更多信息,請務必查看Apple的這些優秀資源:
- 對于擁有OpenGL經驗的人來說,Apple的Metal for OpenGL Developers是一個很棒的視頻。
- Apple的Metal for Developers頁面包含文檔,視頻和示例代碼的鏈接。
- Apple的 Metal Programming Guide。
- Metal Shading Language Guide。
- 來自WWDC 2014的金屬視頻。
- 來自WWDC 2015的Metal視頻。
- 來自WWDC 2016的Metal視頻。
- 來自WWDC 2017的Metal視頻。
- 來自WWDC 2018的Metal視頻。
后記
本篇主要講述了將項目從OpenGL轉化到Metal,感興趣的給個贊或者關注~~~